Який найкращий спосіб отримати доступ до магазину скорочення за межами реагуючого компонента?


192

@connectчудово працює, коли я намагаюся зайти до магазину в межах реагуючого компонента. Але як мені отримати доступ до нього в якомусь іншому біті коду. Наприклад: скажімо, я хочу використовувати маркер авторизації для створення свого екземпляра axios, який можна використовувати в усьому світі в моєму додатку, що було б найкращим способом досягти цього?

Це моє api.js

// tooling modules
import axios from 'axios'

// configuration
const api = axios.create()
api.defaults.baseURL = 'http://localhost:5001/api/v1'
api.defaults.headers.common['Authorization'] = 'AUTH_TOKEN' // need the token here
api.defaults.headers.post['Content-Type'] = 'application/json'

export default api

Тепер я хочу отримати доступ до точки даних з мого магазину, ось як це могло б виглядати, якби я намагався отримати його в компоненті реакції, використовуючи @connect

// connect to store
@connect((store) => {
  return {
    auth: store.auth
  }
})
export default class App extends Component {
  componentWillMount() {
    // this is how I would get it in my react component
    console.log(this.props.auth.tokens.authorization_token) 
  }
  render() {...}
}

Якісь уявлення чи схеми робочого процесу там?


Ви не хочете, щоб екземпляр Axios жив у програмі з надмірним програмним забезпеченням? Це буде доступне для всіх ваших заявок таким чином
Емріс Міруін

Ви можете імпортувати apiв Appкласі і після отримання дозволу маркерів ви можете зробити api.defaults.headers.common['Authorization'] = this.props.auth.tokens.authorization_token;, і в той же час ви можете зберегти його в LocalStorage, так що, коли тонізує користувач сторінки, ви можете перевірити , якщо маркер існує в LocalStorage і якщо він так, ви можете встановити його. Я думаю, що найкраще буде встановити маркер на модулі api, як тільки ви його отримаєте.
Друв Кумар Джа

1
Дан Абромов наводить приклад у черзі випусків тут: github.com/reactjs/redux/isissue/916
chrisjlee

1
Якщо вам просто потрібно отримати доступ до певного стану з певного редуктора, ви можете спробувати з допомогою редукторів-іменників, що дозволяє отримати доступ до останнього стану з будь-якого місця.
miles_christian

Відповіді:


146

Експортуйте магазин із модуля, з яким ви дзвонили createStore. Тоді ви впевнені, що вони будуть створені і не забруднюють глобальний простір вікон.

MyStore.js

const store = createStore(myReducer);
export store;

або

const store = createStore(myReducer);
export default store;

MyClient.js

import {store} from './MyStore'
store.dispatch(...)

або якщо ви використовували за замовчуванням

import store from './MyStore'
store.dispatch(...)

Для кількох випадків використання магазину

Якщо вам потрібно кілька примірників магазину, експортуйте фабричну функцію. Я рекомендував би зробити це async(повертаючи а promise).

async function getUserStore (userId) {
   // check if user store exists and return or create it.
}
export getUserStore

Про клієнта (в asyncблоці)

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')

47
ПОПЕРЕДЖЕННЯ для ізоморфних додатків: виконуючи цю сторону сервера, ви зможете дістати сховище скорочень для всіх ваших користувачів !!!
Лоуренс Вагерфілд

6
Питання також не вказує прямо "браузер". Оскільки і Redux, і JavaScript можна використовувати на будь-якому сервері або в браузері, конкретніше набагато безпечніше.
Лоуренс Вагерфілд

7
схоже, що експортуючий магазин створює кошмар циклічного імпорту, createStore включає ваші редуктори, які вимагають ваших дій (принаймні, тип дії enum та інтерфейси Action), які не повинні імпортувати нічого, що намагається імпортувати магазин. Таким чином, ви не повинні використовувати магазин ні у ваших редукторах, ні в діях (або переплутати іншим способом навколо циклічного імпорту.)
Eloff

6
Це правильна відповідь, але якщо ви (як я) хочете прочитати замість того, щоб відправити дію в магазині, вам потрібно зателефонуватиstore.getState()
Хуан Хосе Рамірес

3
Що ж, моє тлумачення щодо "доступу до магазину скорочення" було "прочитане" магазином. Просто намагаюся допомогти іншим.
Хуан Хосе Рамірес

47

Знайшов рішення. Тож я імпортую магазин у своєму api util і підписуюся на нього там. І в цій функції слухача я встановив глобальні параметри аксіосу за допомогою свого нещодавно отриманого маркера.

Ось так api.jsвиглядає мій новий :

// tooling modules
import axios from 'axios'

// store
import store from '../store'
store.subscribe(listener)

function select(state) {
  return state.auth.tokens.authentication_token
}

function listener() {
  let token = select(store.getState())
  axios.defaults.headers.common['Authorization'] = token;
}

// configuration
const api = axios.create({
  baseURL: 'http://localhost:5001/api/v1',
  headers: {
    'Content-Type': 'application/json',
  }
})

export default api

Можливо, це можна вдосконалити, бо наразі це здається трохи неелегантним. Що я можу зробити пізніше, це додати проміжне програмне забезпечення до свого магазину та встановити маркер то тут, то там.


1
Ви можете поділитися тим, store.jsяк виглядає ваш файл?
Вік

саме те, що я шукав, дякую тоні @subodh
Harkirat Saluja

1
Я знаю, що питання давнє, але ви можете прийняти власну відповідь як правильну. Це полегшує пошук вашої відповіді для інших, які згодом можуть прийти сюди.
Філіпе Меркер

3
Я отримав "TypeError: WEBPACK_IMPORTED_MODULE_2__store_js .b не визначений", коли я намагаюся отримати доступ до магазину поза компонентом або функцією. Будь-яка допомога, чому це так?
ben

21

Ви можете використовувати storeоб'єкт, який повертається з createStoreфункції (який вже повинен бути використаний у вашому коді при ініціалізації програми). Тоді ви можете використовувати цей об’єкт, щоб отримати поточний стан store.getState()методом або store.subscribe(listener)підписатися на зберігання оновлень.

Ви навіть можете зберегти цей об’єкт у windowвласності, щоб отримати доступ до нього з будь-якої частини програми, якщо ви дійсно хочете його ( window.store = store)

Більше інформації можна знайти в документації Redux .


19
звуки свого роду Hacky , щоб зберегти storeвwindow
Vic

3
@ Vic Звичайно, це так :) І зазвичай ти цього не хочеш робити. Я просто хотів зазначити, що ви можете робити все, що завгодно, за допомогою цієї змінної. Можливо, найкращою ідеєю було б зберігати його у файлі, де ви створили своє "createStore" життя, а потім просто імпортувати з нього.
кошик

У мене є кілька iframe, які потребують доступу до стану інших iframe. Я знаю, що це свого роду хакі, але я думаю, що мати магазин у вікні було б краще, ніж використовувати повідомлення в iframe. Будь-які думки ?? @Vic @trashgenerator?
Шон


10

Як і @sanchit, пропоноване проміжне програмне забезпечення - хороше рішення, якщо ви вже визначаєте свій екземпляр axios у всьому світі.

Ви можете створити проміжне програмне забезпечення, наприклад:

function createAxiosAuthMiddleware() {
  return ({ getState }) => next => (action) => {
    const { token } = getState().authentication;
    global.axios.defaults.headers.common.Authorization = token ? `Bearer ${token}` : null;

    return next(action);
  };
}

const axiosAuth = createAxiosAuthMiddleware();

export default axiosAuth;

І використовуйте його так:

import { createStore, applyMiddleware } from 'redux';
const store = createStore(reducer, applyMiddleware(axiosAuth))

Він встановить маркер для кожної дії, але ви можете слухати лише дії, які змінюють, наприклад, маркер.


як ви можете досягти того ж за допомогою спеціального екземпляра axios?
Анатолій

3

Для TypeScript 2.0 це виглядатиме так:

MyStore.ts

export namespace Store {

    export type Login = { isLoggedIn: boolean }

    export type All = {
        login: Login
    }
}

import { reducers } from '../Reducers'
import * as Redux from 'redux'

const reduxStore: Redux.Store<Store.All> = Redux.createStore(reducers)

export default reduxStore;

MyClient.tsx

import reduxStore from "../Store";
{reduxStore.dispatch(...)}

3
Якщо ви голосуєте вниз, будь ласка, додайте коментар, чому.
Огглас

3
Вниз проголосували, оскільки у вашій відповіді бракує пояснень і використовується TypeScript, а не Javascript.
Буде

2
@Буду спасибі, що сказали чому. Imao код не вимагає специфікацій, але якщо ви хочете щось конкретно пояснено, будь ласка, скажіть що. TypeScript справді використовується, але якщо видалити типізацію, той самий код буде запущений в ES6 без проблем.
Огглас

Майте на увазі, що це буде дуже погано, якщо ви робите серверне візуалізація, воно поділиться станом з усіма запитами.
Роб Еванс

2

Це може бути трохи пізно, але я думаю, що найкращим способом є використання, axios.interceptorsяк показано нижче. URL-адреси імпорту можуть змінюватися залежно від налаштування вашого проекту.

index.js

import axios from 'axios';
import setupAxios from './redux/setupAxios';
import store from './redux/store';

// some other codes

setupAxios(axios, store);

setupAxios.js

export default function setupAxios(axios, store) {
    axios.interceptors.request.use(
        (config) => {
            const {
                auth: { tokens: { authorization_token } },
            } = store.getState();

            if (authorization_token) {
                config.headers.Authorization = `Bearer ${authorization_token}`;
            }

            return config;
        },
       (err) => Promise.reject(err)
    );
}

1

Робити це гачками. Я зіткнувся з подібною проблемою, але я використовував гачки-реакції. Я не хотів поширювати свій інтерфейсний код (тобто реагувати на компоненти) з великою кількістю коду, призначеного для отримання / надсилання інформації з / у магазин. Швидше, я хотів, щоб функції із загальними іменами отримували та оновлювали дані. Мій шлях полягав у тому, щоб поставити програму

const store = createSore(
   allReducers,
   window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
 );

в модуль, названий store.jsі доданий exportдо constі додавання звичайного імпорту реагування-скорочення в store.js. файл. Потім я імпортував до index.jsрівня додатків, який потім імпортував у index.js зі звичайними import {store} from "./store.js"дочірніми компонентами, потім звертався до магазину за допомогою useSelector()і useDispatch()гаків.

Для доступу до магазину в некомпонентному коді переднього кінця я використовував аналогічний імпорт (тобто, import {store} from "../../store.js"), а потім використовував store.getState()і store.dispatch({*action goes here*})обробляв пошук і оновлення (ер, надсилання дій до магазину).


0

Найпростіший спосіб отримати доступ до маркера - це помістити маркер у LocalStorage або AsyncStorage з React Native.

Нижче прикладу з проектом React Native

authReducer.js

import { AsyncStorage } from 'react-native';
...
const auth = (state = initialState, action) => {
  switch (action.type) {
    case SUCCESS_LOGIN:
      AsyncStorage.setItem('token', action.payload.token);
      return {
        ...state,
        ...action.payload,
      };
    case REQUEST_LOGOUT:
      AsyncStorage.removeItem('token');
      return {};
    default:
      return state;
  }
};
...

і api.js

import axios from 'axios';
import { AsyncStorage } from 'react-native';

const defaultHeaders = {
  'Content-Type': 'application/json',
};

const config = {
  ...
};

const request = axios.create(config);

const protectedRequest = options => {
  return AsyncStorage.getItem('token').then(token => {
    if (token) {
      return request({
        headers: {
          ...defaultHeaders,
          Authorization: `Bearer ${token}`,
        },
        ...options,
      });
    }
    return new Error('NO_TOKEN_SET');
  });
};

export { request, protectedRequest };

Для Інтернету можна використовувати Window.localStorageзамість AsyncStorage

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.