使用 thunk 中间件从非反应组件调度异步 redux 操作
Dispatch async redux action from non-react component with thunk middleware
我正在构建一个 react/redux webapp,我正在使用一个服务来进行我所有的 API 调用。每当 API returns 401 - Unauthorized
我想向我的 redux 商店发送注销操作时。
现在的问题是我的 api-service 不是 React 组件,所以我无法获得对 dispatch
或 actions
的引用。
我首先做的是导出商店并手动调用 dispatch
,但正如我在这里读到的那样 这似乎是一种不好的做法,因为它要求商店是单例的,这使得测试和渲染变得困难在服务器上是不可能的,因为我们需要为每个用户提供不同的商店。
我已经在使用 react-thunk
(https://github.com/gaearon/redux-thunk) 但我没有t see how I can inject
dispatch` 到非反应组件中。
我需要做什么?或者从 React 组件外部调度动作通常是一种不好的做法?
这就是我的 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;
};
谢谢!
如果您使用的是 redux-thunk,您可以 return 来自 action creator 的函数,它有 dispatch
有参数:
const doSomeStuff = dispatch => {
fetch(…)
.then(res => res.json())
.then(json => dispatch({
type: 'dostuffsuccess',
payload: { json }
}))
.catch(err => dispatch({
type: 'dostufferr',
payload: { err }
}))
}
另一种选择是对远程内容使用中间件。这是可行的,中间可以测试动作的类型,然后将其转换为一个或多个其他动作。看看here,也差不多,虽然基本都是动画,答案最后还是解释一下如何使用中间件进行远程请求
你应该让你的 api 调用完全独立于 redux。它应该 return 一个承诺(就像它目前所做的那样),在满意的情况下解决并用一个告诉状态的参数拒绝。像
if (statusAsString === '401') {
reject({ logout: true })
}
reject({ logout: false });
然后在你的 action creator 代码中你会做:
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(...)
}
}
也许你可以尝试使用中间件来捕获错误并发送注销操作,
但在那种情况下,问题是你必须在需要检查日志状态的动作创建者中发送错误
api: 抛出错误
if (statusAsString === '401') {
// !!!!!-> here I need to dispatch the logout action
throw new Error('401')
}
action creator:捕获来自 api 的错误,并调度错误 action
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())
}
}
您还可以使用 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())
}
我正在构建一个 react/redux webapp,我正在使用一个服务来进行我所有的 API 调用。每当 API returns 401 - Unauthorized
我想向我的 redux 商店发送注销操作时。
现在的问题是我的 api-service 不是 React 组件,所以我无法获得对 dispatch
或 actions
的引用。
我首先做的是导出商店并手动调用 dispatch
,但正如我在这里读到的那样
我已经在使用 react-thunk
(https://github.com/gaearon/redux-thunk) 但我没有t see how I can inject
dispatch` 到非反应组件中。
我需要做什么?或者从 React 组件外部调度动作通常是一种不好的做法?
这就是我的 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;
};
谢谢!
如果您使用的是 redux-thunk,您可以 return 来自 action creator 的函数,它有 dispatch
有参数:
const doSomeStuff = dispatch => {
fetch(…)
.then(res => res.json())
.then(json => dispatch({
type: 'dostuffsuccess',
payload: { json }
}))
.catch(err => dispatch({
type: 'dostufferr',
payload: { err }
}))
}
另一种选择是对远程内容使用中间件。这是可行的,中间可以测试动作的类型,然后将其转换为一个或多个其他动作。看看here,也差不多,虽然基本都是动画,答案最后还是解释一下如何使用中间件进行远程请求
你应该让你的 api 调用完全独立于 redux。它应该 return 一个承诺(就像它目前所做的那样),在满意的情况下解决并用一个告诉状态的参数拒绝。像
if (statusAsString === '401') {
reject({ logout: true })
}
reject({ logout: false });
然后在你的 action creator 代码中你会做:
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(...)
}
}
也许你可以尝试使用中间件来捕获错误并发送注销操作, 但在那种情况下,问题是你必须在需要检查日志状态的动作创建者中发送错误
api: 抛出错误
if (statusAsString === '401') {
// !!!!!-> here I need to dispatch the logout action
throw new Error('401')
}
action creator:捕获来自 api 的错误,并调度错误 action
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())
}
}
您还可以使用 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())
}