web-dev-qa-db-de.com

Was ist der beste Weg, um auf den Redux Store außerhalb einer React-Komponente zuzugreifen?

@connect funktioniert hervorragend, wenn ich versuche, innerhalb einer reag-Komponente auf den Store zuzugreifen. Aber wie soll ich in etwas anderem Code darauf zugreifen? Zum Beispiel: Angenommen, ich möchte ein Autorisierungs-Token zum Erstellen meiner Axios-Instanz verwenden, das global in meiner App verwendet werden kann. Was wäre der beste Weg, um dies zu erreichen?

Dies ist mein 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

Jetzt möchte ich auf einen Datenpunkt aus meinem Speicher zugreifen. So würde es aussehen, wenn ich es mit einer @connect-Komponente in einer React-Komponente abrufen wollte.

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

Irgendwelche Erkenntnisse oder Workflow-Muster da draußen?

131
Subodh Pareek

Exportieren Sie den Store aus dem Modul, mit dem Sie createStore aufgerufen haben. Dann können Sie sicher sein, dass sowohl der globale Fensterbereich erstellt wird als auch, dass er nicht verschmutzt wird.

MyStore.js

const store = createStore(myReducer);
export store;

oder 

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

MyClient.js

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

oder wenn Sie default verwendet haben

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

Für mehrere Store-Anwendungsfälle

Wenn Sie mehrere Instanzen eines Geschäfts benötigen, exportieren Sie eine Factory-Funktion Ich würde empfehlen, async zu erstellen (eine promise zurückgeben).

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

Auf dem Client (in einem async-Block (

import {getUserStore} from './store'

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

Eine Lösung gefunden Also importiere ich den Store in meinem api util und abonniere ihn dort. Und in dieser Listener-Funktion habe ich die globalen Standardwerte der Axios mit meinem neu abgerufenen Token festgelegt.

So sieht mein neuer api.js aus:

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

Vielleicht kann es weiter verbessert werden, da es momentan etwas unelegant erscheint. Was ich später tun könnte, ist, meinem Shop eine Middleware hinzuzufügen und das Token dann und dort festzulegen.

30
Subodh Pareek

Sie können ein store-Objekt verwenden, das von der createStore-Funktion zurückgegeben wird (die bereits in Ihrem Code bei der Initialisierung der App verwendet werden sollte). Dann können Sie dieses Objekt verwenden, um den aktuellen Status mit der store.getState()-Methode abzurufen, oder store.subscribe(listener), um Updates zu speichern. 

Sie können dieses Objekt sogar in der window-Eigenschaft speichern, um von jedem Teil der Anwendung darauf zuzugreifen, wenn Sie es wirklich möchten (window.store = store).

Weitere Informationen finden Sie in der Redux-Dokumentation .

12
trashgenerator

Anscheinend ist Middleware der richtige Weg.
Siehe die offizielle Dokumentation und diese Ausgabe in ihrem Repo

8
sanchit

Wie bei @sanchit vorgeschlagene Middleware ist eine Nice-Lösung wenn Sie definieren bereits Ihre Axios-Instanz global.

Sie können eine Middleware erstellen wie:

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;

Und benutze es so:

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

Es wird das Token für jede Aktion festgelegt. Sie können jedoch nur auf Aktionen achten, die beispielsweise das Token ändern.

5
erksch

Für TypeScript 2.0 würde es so aussehen:

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

Diese Frage ist ziemlich alt, aber ich denke, es lohnt sich, meine Idee mitzuteilen.

Anstatt das Token im redux store zu speichern, speichere ich es im Speicher.
* Wenn die App geladen ist, lesen Sie TOKEN in AsyncStorage (oder an einem anderen Ort) und legen Sie fest, dass es eingestellt wird 

import {updateToke} from 'path_to_the_below_module';

updateToken({your token}). 

Hier ist ein Ausschnitt, den ich gemacht habe.

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;

Einfach um es zu benutzen

import networkRequest from '...pathtothemodule';

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

Beachten Sie, dass Sie bei jeder Aktualisierung des Tokens unbedingt updateToken ({Ihr Token}) aufrufen müssen, um es auf dem neuesten Stand zu halten.

Dies ist eine weitere Alternative, die ich in meinem Projekt verwendet habe, und ich wünsche, dass Sie Ihre Ideen einholen.

0
Leu

Eine einfache Möglichkeit, auf das Token zuzugreifen, besteht darin, das Token in LocalStorage oder AsyncStorage with React Native abzulegen.

Unten ein Beispiel mit einem React Native Projekt

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

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

Für das Web können Sie Window.localStorage anstelle von AsyncStorage verwenden

0
Denis Zheng