在react组件之外访问redux存储的最佳方法是什么?

当我试图访问react组件中的存储时,@connect工作得很好。但我该如何在其他代码中访问它呢。例如:让我们说我想使用授权令牌来创建我的axios实例,可以在我的应用程序中全局使用,实现这一点的最佳方法是什么?

这是我的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

现在我想从我的存储中访问一个数据点,下面是如果我试图使用@connect在react组件中获取它的样子

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

有什么见解或工作流模式吗?

197030 次浏览

你可以使用从createStore函数返回的store对象(在应用程序初始化的代码中应该已经使用了它)。然后你可以使用这个对象通过store.getState()方法或store.subscribe(listener)来获取当前状态来订阅存储更新。

你甚至可以将这个对象保存到window属性中,以便从应用程序的任何部分访问它(window.store = store)

更多信息可以在回来的文档中找到。

找到解决办法了。所以我在我的api util中导入store并在那里订阅它。在侦听器函数中,我用新获取的令牌设置了axios的全局默认值。

这是我的新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

也许它可以进一步改进,因为目前它似乎有点不优雅。我以后可以做的是向我的商店添加一个中间件,然后在那里设置令牌。

从调用createStore的模块导出存储。然后您可以放心,它将被创建并且不会污染全局窗口空间。

MyStore.js

const store = createStore(myReducer);
export store;

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

MyClient.js

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

或者如果你使用默认的

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

对于多个存储用例

如果你需要一个存储的多个实例,导出一个工厂函数。 我建议将其设置为async(返回promise)

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

在客户端(在async块中)

import {getUserStore} from './store'


const joeStore = await getUserStore('joe')

你可以根据如何在非react组件中访问存储?使用Middleware:

中间件

function myServiceMiddleware(myService) {
return ({ dispatch, getState }) => next => action => {
if (action.type == 'SOMETHING_SPECIAL') {
myService.doSomething(getState());
myService.doSomethingElse().then(result => dispatch({ type: 'SOMETHING_ELSE', result }))
}
return next(action);
}
}

使用

import { createStore, applyMiddleware } from 'redux'
const serviceMiddleware = myServiceMiddleware(myService)
const store = createStore(reducer, applyMiddleware(serviceMiddleware))

进一步的阅读: Redux Docs >中间件

对于TypeScript 2.0,它看起来是这样的:

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

就像@sanchit提议的中间件是一个很好的解决方案如果你已经全局定义了你的axios实例。

你可以创建一个这样的中间件:

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;

像这样使用它:

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

它将在每个操作上设置令牌,但您只能侦听例如更改令牌的操作。

访问令牌的一个简单方法是将令牌放在LocalStorage或AsyncStorage中,使用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;
}
};
...

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

对于web,你可以使用Window.localStorage代替AsyncStorage

用钩子做。我遇到了一个类似的问题,但我是使用react-redux与钩子。我不想在我的界面代码(例如,react组件)中添加大量专门用于从商店检索/向商店发送信息的代码。相反,我希望使用具有通用名称的函数来检索和更新数据。我的路径是把应用程序的

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

并在const之前添加export,并在store.js中添加常用的react-redux导入。文件。然后,我在应用程序级别导入index.js,然后用通常的import {store} from "./store.js"导入到index.js。子组件然后使用useSelector()useDispatch()钩子访问商店。

为了在非组件前端代码中访问存储,我使用了类似的导入(即import {store} from "../../store.js"),然后使用store.getState()store.dispatch({*action goes here*})来处理存储的检索和更新(呃,发送操作)。

这可能有点晚,但我认为最好的方法是使用axios.interceptors如下所示。导入url可能会根据项目设置而改变。

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

导出我的store变量

export const store = createStore(rootReducer, applyMiddleware(ReduxThunk));

在action文件或者你的文件中需要导入这个(store)

从“;./path…”导入{store};;

这一步用函数从存储变量中获取状态

const state = store.getState();

获取你的app的所有状态