Qual é a melhor maneira de acessar o repositório redux fora de um componente de reação?


192

@connectfunciona muito bem quando estou tentando acessar a loja dentro de um componente de reação. Mas como devo acessá-lo em algum outro código? Por exemplo: digamos que eu queira usar um token de autorização para criar minha instância axios que possa ser usada globalmente no meu aplicativo, qual seria a melhor maneira de conseguir isso?

Este é meu 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

Agora eu quero acessar um ponto de dados da minha loja. Aqui está como isso seria se eu estivesse tentando buscá-lo em um componente de reação 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() {...}
}

Existem idéias ou padrões de fluxo de trabalho por aí?


Você não quer que sua instância do Axios viva em um middleware redux? Ele estará disponível por toda a sua aplicação desta maneira
Emrys Myrooin

Você pode importar o apina Appclasse e depois de obter a autorização de token que você pode fazer api.defaults.headers.common['Authorization'] = this.props.auth.tokens.authorization_token;, e ao mesmo tempo você pode armazená-lo em localStorage bem, então quando as atualizações do usuário da página, você pode verificar se o token existe no localStorage e se , você pode configurá-lo., acho que será melhor configurar o token no módulo api assim que você o receber.
Dhruv Kumar Jha

1
Dan Abromov fornece um exemplo na fila questão aqui: github.com/reactjs/redux/issues/916
chrisjlee

1
Se você só precisa acessar um estado em particular a partir de um redutor em particular, pode tentar com redutores de nome redux que permite acessar o estado mais recente de qualquer lugar.
miles_christian

Respostas:


146

Exporte a loja do módulo com o qual você ligou createStore. Então, você tem certeza de que ambos serão criados e não poluirão o espaço global da janela.

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 se você usou o padrão

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

Para vários casos de uso de armazenamento

Se você precisar de várias instâncias de uma loja, exporte uma função de fábrica. Eu recomendaria fazê-lo async(retornando a promise).

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

No cliente (em um asyncbloco)

import {getUserStore} from './store'

const joeStore = await getUserStore('joe')

47
AVISO para aplicativos isomórficos: fazer isso do lado do servidor compartilhará o repositório redux entre todos os seus usuários !!!
Lawrence Wagerfield

6
A questão também não indica explicitamente "navegador". Como o Redux e o JavaScript podem ser usados ​​no servidor ou no navegador, é muito mais seguro ser específico.
Lawrence Wagerfield

7
Como a loja de exportação parece criar um pesadelo de importações cíclicas, o createStore inclui seus redutores, que exigem suas ações (pelo menos o tipo de ação enum e interfaces de ação), que não devem importar nada que tente importar a loja. Portanto, você não deve usar a loja em seus redutores ou ações (ou discutir de outra maneira a importação cíclica).
Eloff

6
Esta é a resposta correta, mas se você (como eu) querem ler em vez de envio uma ação na loja, você precisa chamarstore.getState()
Juan José Ramírez

3
Bem, minha interpretação sobre "acessar redux store" foi "ler" a loja. Apenas tentando ajudar os outros.
Juan José Ramírez

47

Encontrei uma solução. Então eu importo a loja no meu api util e assino lá. E nessa função de ouvinte, defino os padrões globais dos axios com meu token recém-buscado.

É assim que meu novo api.jsvisual é:

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

Talvez possa ser melhorado ainda mais, porque atualmente parece um pouco deselegante. O que eu poderia fazer mais tarde é adicionar um middleware à minha loja e definir o token naquele momento.


1
você pode compartilhar a store.jsaparência do seu arquivo?
Vic

exatamente algo que eu estava procurando, graças a @subodh tonelada
Harkirat Saluja

1
Sei que a pergunta é antiga, mas você pode aceitar sua própria resposta como correta. Isso facilita sua resposta para outras pessoas que possam vir aqui.
Filipe Merker

3
Recebi " TypeError : WEBPACK_IMPORTED_MODULE_2__store_js .b está indefinido" quando tento acessar a loja fora de um componente ou função. Alguma ajuda sobre o porquê disso?
ben

21

Você pode usar o storeobjeto retornado da createStorefunção (que já deve ser usada no seu código na inicialização do aplicativo). Você pode usar esse objeto para obter o estado atual com o store.getState()método ou store.subscribe(listener)se inscrever para armazenar atualizações.

Você pode até salvar esse objeto na windowpropriedade para acessá-lo de qualquer parte do aplicativo, se realmente quiser ( window.store = store)

Mais informações podem ser encontradas na documentação do Redux .


19
soa meio hacky para salvar storeawindow
Vic

3
@ Vic Claro que é :) E normalmente você não quer fazê-lo. Eu só queria mencionar que você pode fazer o que quiser com essa variável. Provavelmente, a melhor idéia seria armazená-lo no arquivo em que você criou sua vida "createStore" e depois apenas importar.
trashgenerator

Eu tenho vários iframes que precisam acessar o estado dos outros iframes. Eu sei que é meio hacky, mas acho que ter a loja na vitrine seria melhor do que usar mensagens para iframes. Alguma ideia?? @Vic @trashgenerator?
Sean Malter


10

Como o @sanchit, o middleware proposto é uma boa solução se você já estiver definindo sua instância do axios globalmente.

Você pode criar um middleware como:

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 use-o assim:

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

Ele definirá o token em todas as ações, mas você só poderá ouvir ações que alterem o token, por exemplo.


como você pode conseguir o mesmo com uma instância de axios personalizada?
Anatol

3

Para o TypeScript 2.0, seria assim:

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 você estiver votando para baixo, adicione um comentário.
Ogglas

3
Votado com baixa porque sua resposta não tem explicação e usa TypeScript em vez de Javascript.
Will

2
@ Will Obrigado por dizer o porquê. Imao o código não requer especificação, mas se você gostaria de algo específico explicado, por favor diga o que. O TypeScript é realmente usado, mas se as digitações forem removidas, o mesmo código será executado no ES6 sem problemas.
Ogglas

Lembre-se de que isso será muito ruim se você estiver processando o lado do servidor, ele compartilhará o estado com todas as solicitações.
Rob Evans

2

Pode ser um pouco tarde, mas acho que a melhor maneira é usar axios.interceptors como abaixo. Os URLs de importação podem mudar com base na configuração do seu projeto.

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

Fazendo isso com ganchos. Eu tive um problema semelhante, mas estava usando o react-redux com ganchos. Eu não queria restringir meu código de interface (por exemplo, reagir componentes) com muito código dedicado à recuperação / envio de informações de / para a loja. Em vez disso, eu queria funções com nomes genéricos para recuperar e atualizar os dados. Meu caminho foi colocar o aplicativo

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

em um módulo nomeado store.jse adicionando exportantes conste adicionando as importações usuais do react-redux no store.js. Arquivo. Em seguida, importei para index.jso nível do aplicativo, que depois importei para o index.js com o habitualimport {store} from "./store.js" Os componentes filho acessaram a loja usando os useSelector()botõesuseDispatch() ganchos .

Para acessar a loja no código de front-end sem componente, usei a importação análoga (ou seja import {store} from "../../store.js") e, em seguida, usei store.getState()e store.dispatch({*action goes here*})manipulei a recuperação e atualização (er, enviando ações para) a loja.


0

Uma maneira fácil de acessar o token é colocar o token no LocalStorage ou no AsyncStorage com React Native.

Abaixo um exemplo com um projeto 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 };

Para a Web, você pode usar em Window.localStoragevez do AsyncStorage

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.