it-roy-ru.com

Каков наилучший способ получить доступ к хранилищу резервов вне реагирующего компонента?

@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() {...}
}

Есть идеи или шаблоны рабочего процесса?

151
Subodh Pareek

Экспортируйте хранилище из модуля, который вы назвали 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')
109
Steven Spungin

Нашел решение. Поэтому я импортирую магазин в моем утилите API и подписываюсь на него там. И в этой функции слушателя я установил глобальные значения по умолчанию для axios с помощью своего недавно извлеченного токена.

Вот как выглядит мой новый 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

Может быть, это может быть улучшено еще больше, потому что в настоящее время это выглядит немного не элегантно. Что я мог сделать позже, так это добавить промежуточное ПО в мой магазин и установить токен тут же.

30
Subodh Pareek

Вы можете использовать объект store, который возвращается из функции createStore (который уже должен использоваться в вашем коде при инициализации приложения). Чем вы можете использовать этот объект для получения текущего состояния с помощью метода store.getState() или store.subscribe(listener), чтобы подписаться на обновления магазина. 

Вы даже можете сохранить этот объект в свойстве window, чтобы получить к нему доступ из любой части приложения, если вы действительно этого хотите (window.store = store)

Дополнительную информацию можно найти в документации Redux .

12
trashgenerator

Похоже, Middleware это путь.
Ссылка официальная документация и этот вопрос на их репо

8
sanchit

Как и @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))

Он будет устанавливать токен на каждое действие, но вы можете прослушивать только действия, которые меняют токен, например.

5
erksch

Для 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
Ogglas

Этот вопрос довольно старый, но я думаю, что стоит поделиться моей идеей .

Вместо того, чтобы хранить токен в онлайн-хранилище, я храню его в памяти.
* Когда приложение загружено, прочитайте TOKEN из AsyncStorage (или где-то еще) и установите для него значение 

import {updateToke} from 'path_to_the_below_module';

updateToken({your token}). 

Вот фрагмент кода, который я сделал.

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

const AUTH_TOKEN='AUTH_TOKEN';
const BASE_URL = 'http://localhost:5001/api/v1';
let authenticationToken = {};

export const updateToken = (token) => {
    AsyncStorage.setItem(AUTH_TOKEN, JSON.stringify(token));
    authenticationToken = token;
};

const networkRequest = Axios.create({
    baseURL: BASE_URL,
});

networkRequest.interceptors.request.use(config => {
    const bearer = `Bearer ${authenticationToken.access_token}`;
    if (bearer) {
        config.headers['Authorization'] = bearer;
    }
    console.log(config);
    return config;
}, error => Promise.reject(error));

export default networkRequest;

Просто способ использовать это

import networkRequest from '...pathtothemodule';

networkRequest.get('/user/info').then(....).catch(...);

Обратите внимание, что каждый раз, когда вы обновляете токен, обязательно вызывайте updateToken ({ваш токен}), чтобы сохранить его последним.

Это еще одна альтернатива, которую я использовал в своем проекте, и я хотел бы получить ваши идеи.

0
Leu

Простой способ получить доступ к токену - это поместить токен в LocalStorage или AsyncStorage с React Native.

Ниже приведен пример с React Native project

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

0
Denis Zheng