我正在构建一个react / redux webapp,我正在使用一个服务来进行所有的API调用。每当API返回401 - Unauthorized
时,我都想将注销操作发送到我的redux商店。
现在问题是我的api-service没有反应组件,因此我无法获得对dispatch
或actions
的引用。
我首先做的是导出商店并手动调用dispatch
,但正如我在这里阅读How to dispatch a Redux action with a timeout?这似乎是一个不好的做法,因为它要求商店是一个单身,这使得测试很难和渲染在服务器上不可能,因为我们需要为每个用户提供不同的商店。
我已经在使用react-thunk
(https://github.com/gaearon/redux-thunk),但我不会t see how I can inject
发送给非反应组件。
我需要做什么?或者从反应组件外部调度操作通常是一种不好的做法?
这就是我api.services.ts
现在的样子:
... other imports
// !!!!!-> I want to get rid of this import
import {store} from '../';
export const fetchWithAuth = (url: string, method: TMethod = 'GET', data: any = null): Promise<TResponseData> => {
let promise = new Promise((resolve, reject) => {
const headers = {
"Content-Type": "application/json",
"Authorization": getFromStorage('auth_token')
};
const options = {
body: data ? JSON.stringify(data) : null,
method,
headers
};
fetch(url, options).then((response) => {
const statusAsString = response.status.toString();
if (statusAsString.substr(0, 1) !== '2') {
if (statusAsString === '401') {
// !!!!!-> here I need to dispatch the logout action
store.dispatch(UserActions.logout());
}
reject();
} else {
saveToStorage('auth_token', response.headers.get('X-TOKEN'));
resolve({
data: response.body,
headers: response.headers
});
}
})
});
return promise;
};
谢谢!
答案 0 :(得分:1)
如果您正在使用redux-thunk,则可以从动作创建者返回一个函数,其中dispatch
具有参数:
const doSomeStuff = dispatch => {
fetch(…)
.then(res => res.json())
.then(json => dispatch({
type: 'dostuffsuccess',
payload: { json }
}))
.catch(err => dispatch({
type: 'dostufferr',
payload: { err }
}))
}
另一种选择是将中间件用于远程内容。这样做的方式,中间可以测试一个动作的类型,然后将其转换为on或多个其他动作。看看here,它是相似的,即使基本上是关于动画,答案最后还有关于如何使用中间件进行远程请求的一些解释。
答案 1 :(得分:0)
你应该让你的api调用完全独立于redux。它应该返回一个promise(就像它当前那样),在happy case中解析并拒绝一个告诉状态的参数。像
这样的东西if (statusAsString === '401') {
reject({ logout: true })
}
reject({ logout: false });
然后在您的动作创建者代码中执行:
function fetchWithAuthAction(url, method, data) {
return function (dispatch) {
return fetchWithAuth(url, method, data).then(
({ data, headers }) => dispatch(fetchedData(data, headers)),
({ logout }) => {
if(logout) {
dispatch(UserActions.logout());
} else {
dispatch(fetchedDataFailed());
}
);
};
}
修改:
如果您不想在任何地方编写错误处理代码,可以创建帮助程序:
function logoutOnError(promise, dispatch) {
return promise.catch(({ logout }) => {
if(logout) {
dispatch(UserActions.logout());
}
})
}
然后你可以在你的动作创作者中使用它:
function fetchUsers() {
return function (dispatch) {
return logoutOnError(fetchWithAuth("/users", "GET"), dispatch).then(...)
}
}
答案 2 :(得分:0)
也许您可以尝试使用中间件来捕获错误并发送注销操作, 但在这种情况下,问题是你必须在动作创建者中发送需要检查日志状态的错误
api:抛出错误
if (statusAsString === '401') {
// !!!!!-> here I need to dispatch the logout action
throw new Error('401')
}
动作创建者:从api捕获错误,并发送错误操作
fetchSometing(ur)
.then(...)
.catch(err => dispatch({
type: fetchSometingError,
err: err
})
中间件:使用401消息捕获错误,并发送注销操作
const authMiddleware = (store) => (next) => (action) => {
if (action.error.message === '401') {
store.dispatch(UserActions.logout())
}
}
答案 3 :(得分:0)
您还可以使用axios(拦截器)或apisauce(监视器)并在他们前往其处理程序之前拦截所有呼叫,并在此时使用
// this conditional depends on how the interceptor works on each api.
// In apisauce you use response.status
if (response.status === '401') {
store.dispatch(UserActions.logout())
}