@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?
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.
const store = createStore(myReducer);
export store;
oder
const store = createStore(myReducer);
export default store;
import {store} from './MyStore'
store.dispatch(...)
oder wenn Sie default verwendet haben
import store from './MyStore'
store.dispatch(...)
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')
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.
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 .
Anscheinend ist Middleware
der richtige Weg.
Siehe die offizielle Dokumentation und diese Ausgabe in ihrem Repo
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.
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(...)}
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.
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