Qual è il modo migliore per accedere al negozio redux al di fuori di un componente di reazione?


192

@connectfunziona benissimo quando sto provando ad accedere al negozio all'interno di un componente di reazione. Ma come dovrei accedervi con qualche altro codice. Ad esempio: diciamo che voglio usare un token di autorizzazione per creare la mia istanza axios che può essere utilizzata a livello globale nella mia app, quale sarebbe il modo migliore per ottenerlo?

Questo è il mio 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

Ora voglio accedere a un punto dati dal mio negozio, ecco come sarebbe se provassi a recuperarlo all'interno di un componente di reazione usando @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() {...}
}

Qualche idea o flusso di lavoro là fuori?


Non vuoi che l'istanza di Axios viva in un middleware redux? Sarà disponibile da tutte le applicazioni in questo modo
Emrys Myrooin,

Puoi importare apiin Appclasse e dopo aver ottenuto il token di autorizzazione puoi farlo api.defaults.headers.common['Authorization'] = this.props.auth.tokens.authorization_token;, e allo stesso tempo puoi anche memorizzarlo in localStorage, quindi quando l'utente aggiorna la pagina, puoi verificare se il token esiste in localStorage e se sì, puoi impostarlo., Penso che sarà meglio impostare il token sul modulo api non appena lo ottieni.
Dhruv Kumar Jha,

1
Dan Abromov fornisce un esempio nella coda dei problemi qui: github.com/reactjs/redux/issues/916
chrisjlee

1
Se hai solo bisogno di accedere a uno stato particolare da un determinato riduttore, puoi provare con i riduttori con nome redux che ti consentono di accedere allo stato più recente da qualsiasi luogo.
miles_christian

Risposte:


146

Esporta il negozio dal modulo con cui hai chiamato createStore. Quindi hai la certezza che saranno entrambi creati e non inquineranno lo spazio globale della finestra.

MyStore.js

const store = createStore(myReducer);
export store;

o

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

MyClient.js

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

o se hai usato il valore predefinito

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

Per casi d'uso multipli

Se sono necessarie più istanze di un negozio, esportare una funzione di fabbrica. Consiglierei di farlo async(restituendo a promise).

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

Sul client (in un asyncblocco)

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')

47
ATTENZIONE per le app isomorfe: facendo questo lato server condividerai il redux store tra tutti i tuoi utenti !!!
Lawrence Wagerfield,

6
Inoltre, la domanda non indica nemmeno esplicitamente "browser". Poiché Redux e JavaScript possono essere utilizzati su server o browser, è molto più sicuro essere specifici.
Lawrence Wagerfield,

7
esportare negozio sembra creare un incubo di importazioni cicliche, createStore include i tuoi riduttori, che richiedono le tue azioni (almeno il tipo di azione enum e le interfacce Action), che non devono importare nulla che tenti di importare store. Quindi non devi usare il negozio nei tuoi riduttori o azioni (o combattere in qualche modo attorno all'importazione ciclica.)
Eloff

6
Questa è la risposta corretta, ma se tu (come me) vuoi leggere invece di inviare un'azione nel negozio, devi chiamarestore.getState()
Juan José Ramírez,

3
Bene, la mia interpretazione su "access redux store" è stata "leggere" il negozio. Sto solo cercando di aiutare gli altri.
Juan José Ramírez,

47

Ho trovato una soluzione Quindi, importa il negozio nel mio API API e mi iscrivo lì. E in quella funzione di ascolto ho impostato le impostazioni predefinite globali degli assi con il mio token appena recuperato.

Ecco api.jscome appare il mio nuovo :

// 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

Forse può essere ulteriormente migliorato, perché al momento sembra un po 'inelegante. Quello che potrei fare in seguito è aggiungere un middleware al mio negozio e impostare il token in quel momento.


1
puoi condividere l' store.jsaspetto del tuo file?
Vic

esattamente qualcosa che stavo cercando, grazie mille @subodh
Harkirat Saluja,

1
So che la domanda è vecchia, ma puoi accettare la tua risposta come corretta. Questo rende la tua risposta più facile da trovare per gli altri che potrebbero eventualmente venire qui.
Filipe Merker,

3
Ho ricevuto "TypeError: WEBPACK_IMPORTED_MODULE_2__store_js .b non è definito" quando provo ad accedere al negozio al di fuori di un componente o di una funzione. Qualche aiuto sul perché?
ben

21

È possibile utilizzare l' storeoggetto restituito dalla createStorefunzione (che dovrebbe essere già utilizzato nel codice nell'inizializzazione dell'app). Quindi è possibile utilizzare questo oggetto per ottenere lo stato corrente con store.getState()metodo ostore.subscribe(listener) per iscriverti per archiviare gli aggiornamenti.

Puoi persino salvare questo oggetto nella windowproprietà per accedervi da qualsiasi parte dell'applicazione se lo desideri davvero (window.store = store )

Ulteriori informazioni sono disponibili nella documentazione di Redux .


19
suona un po 'confuso da salvare storeinwindow
Vic

3
@Vic Certo che lo è :) E normalmente non vuoi farlo. Volevo solo menzionare che puoi fare quello che vuoi con questa variabile. Probabilmente l'idea migliore sarebbe quella di archiviarlo in un file in cui hai creato le tue vite "createStore" e quindi importarlo da esso.
trashgenerator,

Ho più iframe che hanno bisogno di accedere allo stato degli altri iframe. So che è un po 'confuso, ma penso che avere il negozio sulla finestra sarebbe meglio che usare i messaggi per iframe. Qualche idea?? @Vic @trashgenerator?
Sean Malter,


10

Come il middleware proposto da @sanchit è una buona soluzione se stai già definendo l'istanza axios a livello globale.

È possibile creare un middleware come:

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;

E usalo in questo modo:

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

Imposta il token su ogni azione, ma puoi solo ascoltare le azioni che cambiano il token, ad esempio.


come puoi ottenere lo stesso con un'istanza axios personalizzata?
Anatol,

3

Per TypeScript 2.0 sarebbe simile al seguente:

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
Se stai votando verso il basso, aggiungi un commento perché.
Ogglas,

3
Down ha votato perché la tua risposta manca di spiegazione e utilizza TypeScript anziché Javascript.
Sarà il

2
@Will Grazie per aver detto perché. Imao il codice non richiede specifiche ma se desideri qualcosa di specifico spiegato, per favore, dì cosa. TypeScript viene effettivamente utilizzato, ma se le tipizzazioni vengono rimosse lo stesso codice verrà eseguito in ES6 senza problemi.
Ogglas,

Tieni presente che questo sarà molto negativo se stai eseguendo il rendering sever-side, condividerà lo stato con tutte le richieste.
Rob Evans,

2

Potrebbe essere un po 'tardi, ma penso che il modo migliore sia usare axios.interceptors come di seguito. Gli URL di importazione potrebbero cambiare in base alla configurazione del progetto.

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

Fallo con i ganci. Mi sono imbattuto in un problema simile, ma stavo usando reagire-redux con ganci. Non volevo aggiungere il mio codice di interfaccia (ovvero, reagire ai componenti) con un sacco di codice dedicato al recupero / invio di informazioni da / al negozio. Volevo piuttosto funzioni con nomi generici per recuperare e aggiornare i dati. Il mio percorso era mettere l'app

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

in un modulo denominato store.jse che aggiunge exportprima conste aggiunge le consuete importazioni di reazioni reattive in store.js. file. Poi, ho importato a index.jsal livello applicazione, che ho poi importati in index.js con la solita import {store} from "./store.js"I componenti figlio poi entrati nel negozio utilizzando l' useSelector()euseDispatch() ganci .

Per accedere al negozio con un codice front-end non componente, ho usato l'importazione analoga (ovvero, import {store} from "../../store.js") e quindi ho usato store.getState()e store.dispatch({*action goes here*})gestito il recupero e l'aggiornamento (er, l'invio di azioni) al negozio.


0

Un modo semplice per accedere al token è quello di inserire il token in LocalStorage o AsyncStorage con React Native.

Di seguito un esempio con un progetto 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;
  }
};
...

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

Per il web puoi usare al Window.localStorageposto di AsyncStorage

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.