reactjs - helmet - ¿Cuál es la mejor manera de acceder a la tienda redux fuera de un componente reactivo?
react meta tags (7)
Al igual que @sanchit, el middleware propuesto es una buena solución si ya está definiendo su instancia de axios a nivel mundial.
Puedes crear un 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;
Y úsalo así:
import { createStore, applyMiddleware } from ''redux'';
const store = createStore(reducer, applyMiddleware(axiosAuth))
Establecerá el token en cada acción, pero solo puede escuchar acciones que cambien el token, por ejemplo.
@connect
funciona muy bien cuando intento acceder a la tienda dentro de un componente de reacción.
Pero, ¿cómo debo acceder a él en algún otro bit de código?
Por ejemplo: supongamos que quiero usar un token de autorización para crear mi instancia de axios que pueda usarse globalmente en mi aplicación, ¿cuál sería la mejor manera de lograrlo?
Esta es mi
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
Ahora quiero acceder a un punto de datos desde mi tienda, así es como se vería si estuviera tratando de recuperarlo dentro de un componente de reacción 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() {...}
}
¿Alguna idea o patrones de flujo de trabajo por ahí?
Encontró una solución. Así que importo la tienda en mi api util y me suscribo allí. Y en esa función de escucha configuré los valores predeterminados globales de los axios con mi token recién obtenido.
Así es como se ve mi nuevo
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
Tal vez se pueda mejorar aún más, porque actualmente parece un poco poco elegante. Lo que podría hacer más tarde es agregar un middleware a mi tienda y configurar el token allí mismo.
Exporte la tienda desde el módulo con el que llamó
createStore
.
Entonces tiene la seguridad de que ambos se crearán y no contaminarán el espacio de la ventana global.
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 si usaste por defecto
import store from ''./MyStore''
store.dispatch(...)
Para casos de uso de múltiples tiendas
Si necesita varias instancias de una tienda, exporte una función de fábrica.
Recomendaría hacerlo
async
(devolver una
promise
).
async function getUserStore (userId) {
// check if user store exists and return or create it.
}
export getUserStore
En el cliente (en un bloque
async
)
import {getUserStore} from ''./store''
const joeStore = await getUserStore(''joe'')
Para TypeScript 2.0 se vería así:
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(...)}
Parece que
Middleware
es el camino a seguir.
Consulte
la documentación oficial
y
este problema
en su repositorio
Puede usar el objeto de
store
que se devuelve desde la función
createStore
(que ya debería usarse en su código en la inicialización de la aplicación).
Entonces puede usar este objeto para obtener el estado actual con el método
store.getState()
o
store.subscribe(listener)
para suscribirse a las actualizaciones de la tienda.
Incluso puede guardar este objeto en la propiedad de
window
para acceder a él desde cualquier parte de la aplicación si realmente lo desea (
window.store = store
)
Se puede encontrar más información en la documentación de Redux .
Una manera fácil de tener acceso al token es colocar el token en LocalStorage o AsyncStorage con React Native.
Debajo de un ejemplo con un proyecto 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;
}
};
...
y
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 la web, puede usar
Window.localStorage
lugar de AsyncStorage