使用带有 JWT 令牌的 Axios 对 Redux saga 产生调用的无限循环
Posted
技术标签:
【中文标题】使用带有 JWT 令牌的 Axios 对 Redux saga 产生调用的无限循环【英文标题】:Infinite loop on Redux saga yield call using Axios with JWT tokens 【发布时间】:2021-05-15 15:29:57 【问题描述】:我一直在尝试通过 Redux-saga 使用 Redux-toolkit & react 使用 Axios 获取数据。似乎用令牌拦截 saga 调用会使 redux-saga 陷入无限循环?还是因为我的观察者?
我最近一直在学习如何编程,所以我在各个领域的技能都不是很好,希望你不要介意代码的编写方式,因为我一直在学习教程。
关于从 Header.tsx 到 dispatch
的 handleSubmitconst handleSubmit = (e) =>
e.preventDefault();
dispatch(getCurrentUser());
;
我的 rootSaga.tsx 包括所有 watcherSagas 通知 getCurrentUser()
的调度import takeLatest from "redux-saga/effects";
import
handleLogInUser,
handleGetCurrentUser,
handleSetCurrentUser,
from "./handlers/user";
import
logInUser,
getCurrentUser,
setCurrentUser,
from "../slices/user/userSlice";
export function* watcherSaga()
yield takeLatest(logInUser.type, handleLogInUser);
yield takeLatest(getCurrentUser.type, handleGetCurrentUser);
yield takeLatest(setCurrentUser.type, handleSetCurrentUser);
观察者为位于 user.tsx 文件 in handler 文件夹中的 saga 调用 handleGetCurrentUser:
import call, put from "redux-saga/effects";
import setCurrentUser from "../../slices/user/userSlice";
import requestLogInUser, requestGetCurrentUser from "../requests/user";
export function* handleLogInUser(action)
try
console.log(action + "in handleLogInUser");
yield call(requestLogInUser(action));
catch (error)
console.log(error);
export function* handleGetCurrentUser(action)
try
const response = yield call(requestGetCurrentUser);
const userData = response;
yield put(setCurrentUser( ...userData ));
catch (error)
console.log(error);
然后使用对 requestGetCurrentUser 的 yield 调用触发对以下 user.tsx in requests 文件夹的请求
import axiosInstance from "../../../axios/Axios";
export function requestGetCurrentUser()
return axiosInstance.request( method: "get", url: "/user/currentUser/" );
返回响应并放入 const userData,我 consoleLog()'d 处理程序并发现以下内容:
-
它将成功到达处理程序
进入收益电话
获取数据成功
将数据返回给处理程序
然后它会再次重新启动整个 yield 调用?
它也永远不会返回给 userSlice 以放置数据。
axiosInstance 在我的 axios.tsx 文件中,该文件包含拦截器并获取 access_token 并将其添加到标头中。
import axios from "axios";
const baseURL = "http://127.0.0.1:8000/api/";
const axiosInstance = axios.create(
baseURL: baseURL,
timeout: 5000,
headers:
Authorization: "Bearer " + localStorage.getItem("access_token"),
"Content-Type": "application/json",
accept: "application/json",
,
);
axiosInstance.interceptors.response.use(
(response) =>
return response;
,
async function (error)
const originalRequest = error.config;
if (typeof error.response === "undefined")
alert(
"A server/network error occurred. " +
"Looks like CORS might be the problem. " +
"Sorry about this - we will get it fixed shortly."
);
return Promise.reject(error);
if (
error.response.status === 401 &&
originalRequest.url === baseURL + "token/refresh/"
)
window.location.href = "/login/";
return Promise.reject(error);
if (
error.response.data.code === "token_not_valid" &&
error.response.status === 401 &&
error.response.statusText === "Unauthorized"
)
const refreshToken = localStorage.getItem("refresh_token");
if (refreshToken)
const tokenParts = JSON.parse(atob(refreshToken.split(".")[1]));
// exp date in token is expressed in seconds, while now() returns milliseconds:
const now = Math.ceil(Date.now() / 1000);
console.log(tokenParts.exp);
if (tokenParts.exp > now)
return axiosInstance
.post("/token/refresh/",
refresh: refreshToken,
)
.then((response) =>
localStorage.setItem("access_token", response.data.access);
localStorage.setItem("refresh_token", response.data.refresh);
axiosInstance.defaults.headers["Authorization"] =
"JWT " + response.data.access;
originalRequest.headers["Authorization"] =
"JWT " + response.data.access;
return axiosInstance(originalRequest);
)
.catch((err) =>
console.log(err);
);
else
console.log("Refresh token is expired", tokenParts.exp, now);
window.location.href = "/login/";
else
console.log("Refresh token not available.");
window.location.href = "/login/";
// specific error handling done elsewhere
return Promise.reject(error);
);
export default axiosInstance;
userSlice.tsx
import createSlice from "@reduxjs/toolkit";
const userSlice = createSlice(
name: "user",
initialState: ,
reducers:
logInUser(state, action) ,
getCurrentUser() ,
setCurrentUser(state, action)
const userData = action.payload;
console.log(userData + "we are now back in slice");
return ...state, ...userData ;
,
,
);
export const logInUser, getCurrentUser, setCurrentUser = userSlice.actions;
export default userSlice.reducer;
我发现如果我要删除授权令牌,它只会触发一次并退出无限循环,因为它会引发未经授权的错误。
任何建议将不胜感激,谢谢!
【问题讨论】:
您也可以发布您的操作创建者吗? 【参考方案1】:抱歉这么晚才回来,我在不久前设法修复它纯粹是偶然的,我不完全明白为什么。
但我相信解决它的原因有以下两点:
更改调度操作的 useEffect 并确保处理程序返回 useEffect 期望更新的数据。
在处理程序中,我将userData
解构为 userData
,我认为这意味着从axios请求返回的数据不是整个请求,而是实际返回的数据。
我的处理程序
export function* handleGetCurrentUser()
try
console.log("in request get user");
const response = yield call(requestGetCurrentUser);
const data = response;
yield put(setCurrentUser( ...data ));
catch (error)
console.log(error);
我忘记将我的 useEffect 添加到创建操作的帖子中。
App.tsx 中的我的useEffect
将在应用第一次呈现时调度调用。然而,由于返回的数据没有更新预期的内容,它一直在重新渲染。
我不记得我的 useEffect 是什么,但目前它是以下内容:
我在 App.tsx 中的 useEffect
const dispatch = useDispatch();
useEffect(() =>
dispatch(getCurrentUser());
, [dispatch]);
const user = useSelector((state) => state.user);
【讨论】:
以上是关于使用带有 JWT 令牌的 Axios 对 Redux saga 产生调用的无限循环的主要内容,如果未能解决你的问题,请参考以下文章