当我试图访问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
现在我想从我的商店访问一个数据点,这是什么样子,如果我试图在一个react组件中使用@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() {...}
}
有什么见解或工作流模式吗?
找到解决办法了。所以我在我的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;
or
const store = createStore(myReducer);
export default store;
MyClient.js
import {store} from './MyStore'
store.dispatch(...)
或者如果你使用默认
import store from './MyStore'
store.dispatch(...)
对于多个存储用例
如果需要一个存储的多个实例,可以导出一个工厂函数。
我建议将其设置为异步(返回承诺)。
async function getUserStore (userId) {
// check if user store exists and return or create it.
}
export getUserStore
在客户端(在异步块中)
import {getUserStore} from './store'
const joeStore = await getUserStore('joe')
你可以根据我如何访问非反应组件的商店使用中间件?:
中间件
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 >中间件
像@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。
下面是一个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}从“./store.js”导入到index.js,然后子组件使用useSelector()和useDispatch()钩子访问商店。
为了在非组件前端代码中访问存储,我使用了类似的导入(即从“../../store.js”导入{store}),然后使用store. getstate()和store.js。分派({*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)
);
}
我也遇到过类似的问题——我想建立一个全局Axios配置,可以从React内部的任何地方访问(组件,组件外部,thunk action内部..)
我最终编写了一个返回配置对象的thunk。好处是,由于getState(),坦克可以访问存储,所以您不必重新发明轮子。也许这个解决方案可以帮助某些人;)
1. 铛
export const getAxiosConfig = (payload) => {
return (dispatch, getState) => {
const { app } = getState();
const axiosConfig: AxiosRequestConfig = {
baseURL: `${process.env.BACKEND_API}`,
headers: {
Authorization: `Bearer ${app.token}`
}
};
return axiosConfig;
}
}
2. 获取配置
const axiosConfig = dispatch(getAxiosConfig(null));
3.使用config调用API
const {data} = await axios.get(/resource/${resourceId},
axiosConfig
);