web-dev-qa-db-fra.com

Quel est le meilleur moyen d'accéder à Redux Store en dehors d'un composant de réaction?

@connect fonctionne très bien lorsque j'essaie d'accéder au magasin dans un composant de réaction. Mais comment dois-je y accéder dans un autre morceau de code. Par exemple, disons que je souhaite utiliser un jeton d'autorisation pour créer mon instance axios pouvant être utilisée globalement dans mon application, quel serait le meilleur moyen d'y parvenir?

C'est mon 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

Maintenant, je veux accéder à un point de données de mon magasin. Voici ce à quoi cela ressemblerait si j'essayais de le récupérer dans un composant de réaction en utilisant @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() {...}
}

Avez-vous des idées ou des modèles de flux de travail?

131
Subodh Pareek

Exportez le magasin à partir du module avec lequel vous avez appelé createStore. Ensuite, vous êtes assuré qu'il sera créé et ne polluera pas l'espace global de la fenêtre.

MyStore.js

const store = createStore(myReducer);
export store;

ou 

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

MyClient.js

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

ou si vous avez utilisé default

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

Pour plusieurs cas d'utilisation de magasins

Si vous avez besoin de plusieurs instances d’un magasin, exportez une fonction d’usine . Je vous conseillerais de le créer async (en retournant un promise).

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

Sur le client (dans un bloc async (

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')
109
Steven Spungin

Trouvé une solution. Donc, j'importe le magasin dans mon api util et je m'y abonne. Et dans cette fonction d’audience, j’ai défini les paramètres par défaut d’axios avec mon nouveau jeton.

Voici à quoi ressemble mon nouveau 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

Peut-être pourrait-il être encore amélioré, car il semble actuellement un peu inélégant. Ce que je pourrais faire plus tard, c’est d’ajouter un middleware à mon magasin et de définir le jeton à tout moment.

30
Subodh Pareek

Vous pouvez utiliser l'objet store renvoyé par la fonction createStore (qui doit déjà être utilisé dans votre code lors de l'initialisation de l'application). Vous pouvez ensuite utiliser cet objet pour obtenir l'état actuel avec la méthode store.getState() ou store.subscribe(listener) pour vous abonner aux mises à jour du magasin. 

Vous pouvez même enregistrer cet objet dans la propriété window pour y accéder depuis n’importe quelle partie de l’application si vous le souhaitez vraiment (window.store = store)

Plus d'informations peuvent être trouvées dans Documentation Redux .

12
trashgenerator

On dirait que Middleware est la voie à suivre.
Référez-vous à la documentation officielle et ce numéro sur leur repo

8
sanchit

Comme @sanchit, le middleware proposé est une solution intéressante de Nice si vous définissez déjà votre instance axios globalement.

Vous pouvez créer un middleware tel que:

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;

Et utilisez-le comme ceci:

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

Il définira le jeton pour chaque action, mais vous ne pourrez écouter que les actions modifiant le jeton, par exemple.

5
erksch

Pour TypeScript 2.0, cela ressemblerait à ceci:

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

Cette question est assez ancienne, mais je pense que cela vaut la peine de partager mon idée .

Plutôt que de stocker le jeton dans le magasin Redux, je le stocke en mémoire.
* Lorsque l'application est chargée, lisez TOKEN à partir d'AsyncStorage (ou ailleurs) et réglez-le sur 

import {updateToke} from 'path_to_the_below_module';

updateToken({your token}). 

Voici un code d'extrait que j'ai fait.

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;

Simplement moyen de l'utiliser

import networkRequest from '...pathtothemodule';

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

Notez que chaque fois que vous actualisez le jeton, veillez à appeler updateToken ({votre jeton}) afin de le conserver au plus tard.

C’est une autre alternative que j’ai utilisée dans mon projet et je souhaite aller chercher vos idées.

0
Leu

Un moyen facile d'accéder au jeton consiste à le placer dans LocalStorage ou AsyncStorage avec React Native.

Ci-dessous un exemple avec un 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;
  }
};
...

et 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 };

Pour le Web, vous pouvez utiliser Window.localStorage au lieu de AsyncStorage

0
Denis Zheng