使用带有 JWT 令牌的 Axios 对 Redux saga yield 调用进行无限循环
Infinite loop on Redux saga yield call using Axios with JWT tokens
我一直在尝试通过 Redux-saga 使用 Axios 使用 Redux-toolkit 获取数据并做出反应。似乎在无限循环中拦截带有令牌的 saga 调用会得到 redux-saga?还是因为我的关注者?
我最近一直在学习如何编程,所以我的各个方面的技能还不是很好,希望你不要介意代码的编写方式,因为我一直在学习教程。
On handleSubmit 从 Header.tsx 到 dispatch
const handleSubmit = (e) => {
e.preventDefault();
dispatch(getCurrentUser());
};
my 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);
}
watcher 调用 handleGetCurrentUser 用于 handler 文件夹中 user.tsx 文件中的 saga:
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 调用,在 requests 文件夹
import axiosInstance from "../../../axios/Axios";
export function requestGetCurrentUser() {
return axiosInstance.request({ method: "get", url: "/user/currentUser/" });
}
响应被返回并放入 const userData,我对处理程序进行了 consoleLog() 处理并发现了以下内容:
- 它将成功到达处理程序
- 转到 yield 调用
- 获取数据成功
- return 返回处理程序的数据
- 然后它再次重新启动整个 yield 调用?
它也永远不会返回到 userSlice 以放置数据。
axiosInstance 在我的 axios.tsx 文件中,其中包含拦截器并获取 access_token 并将其添加到header.
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;
我发现如果我要删除授权令牌,它只会触发一次并退出无限循环,因为它会抛出未经授权的错误。
如有任何建议,将不胜感激,谢谢!
很抱歉这么晚才回来,我刚刚偶然修复了它,但我不太明白为什么。
但我相信解决它的是以下两件事:
更改调度操作的 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 添加到创建动作的 post。
my useEffect
in the App.tsx 将在 App 首次呈现时调度调用。然而,由于返回的数据没有更新预期的内容,它一直在重新渲染。
我记不清我的 useEffect 是什么了,但目前是这样的:
我在 App.tsx
中的 useEffect
const dispatch = useDispatch();
useEffect(() => {
dispatch(getCurrentUser());
}, [dispatch]);
const user = useSelector((state) => state.user);
我一直在尝试通过 Redux-saga 使用 Axios 使用 Redux-toolkit 获取数据并做出反应。似乎在无限循环中拦截带有令牌的 saga 调用会得到 redux-saga?还是因为我的关注者?
我最近一直在学习如何编程,所以我的各个方面的技能还不是很好,希望你不要介意代码的编写方式,因为我一直在学习教程。
On handleSubmit 从 Header.tsx 到 dispatch
const handleSubmit = (e) => {
e.preventDefault();
dispatch(getCurrentUser());
};
my 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);
}
watcher 调用 handleGetCurrentUser 用于 handler 文件夹中 user.tsx 文件中的 saga:
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 调用,在 requests 文件夹
import axiosInstance from "../../../axios/Axios";
export function requestGetCurrentUser() {
return axiosInstance.request({ method: "get", url: "/user/currentUser/" });
}
响应被返回并放入 const userData,我对处理程序进行了 consoleLog() 处理并发现了以下内容:
- 它将成功到达处理程序
- 转到 yield 调用
- 获取数据成功
- return 返回处理程序的数据
- 然后它再次重新启动整个 yield 调用?
它也永远不会返回到 userSlice 以放置数据。
axiosInstance 在我的 axios.tsx 文件中,其中包含拦截器并获取 access_token 并将其添加到header.
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;
我发现如果我要删除授权令牌,它只会触发一次并退出无限循环,因为它会抛出未经授权的错误。
如有任何建议,将不胜感激,谢谢!
很抱歉这么晚才回来,我刚刚偶然修复了它,但我不太明白为什么。
但我相信解决它的是以下两件事:
更改调度操作的 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 添加到创建动作的 post。
my useEffect
in the App.tsx 将在 App 首次呈现时调度调用。然而,由于返回的数据没有更新预期的内容,它一直在重新渲染。
我记不清我的 useEffect 是什么了,但目前是这样的:
我在 App.tsx
中的 useEffect const dispatch = useDispatch();
useEffect(() => {
dispatch(getCurrentUser());
}, [dispatch]);
const user = useSelector((state) => state.user);