ajh*_*ery 14 reactjs axios next.js
我已经构建了一个带有拦截器的 axios 私有实例来管理身份验证请求。
\n系统有一个自定义的axios实例:
\nconst BASE_URL = 'http://localhost:8000';\nexport const axiosPrivate = axios.create({\n baseURL: BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n },\n withCredentials: true,\n});\n
Run Code Online (Sandbox Code Playgroud)\n自定义 useRefreshToken 挂钩使用刷新令牌返回 accessToken:
\nconst useRefreshToken = () => {\n const { setAuth } = useAuth();\n\n const refresh = async () => {\n const response = await refreshTokens();\n // console.log('response', response);\n const { user, roles, accessToken } = response.data;\n setAuth({ user, roles, accessToken });\n // return accessToken for use in axiosClient\n return accessToken;\n };\n\n return refresh;\n};\n\nexport default useRefreshToken;\n
Run Code Online (Sandbox Code Playgroud)\naxios 拦截器在 useAxiosPrivate.js 文件中附加到此 axios 实例,以附加 accessToken,以请求并在过期时使用刷新令牌刷新 accessToken。
\nconst useAxiosPrivate = () => {\n const { auth } = useAuth();\n const refresh = useRefreshToken();\n\n useEffect(() => {\n const requestIntercept = axiosPrivate.interceptors.request.use(\n (config) => {\n // attach the access token to the request if missing\n if (!config.headers['Authorization']) {\n config.headers['Authorization'] = `Bearer ${auth?.accessToken}`;\n }\n return config;\n },\n (error) => Promise.reject(error)\n );\n\n const responseIntercept = axiosPrivate.interceptors.response.use(\n (response) => response,\n async (error) => {\n const prevRequest = error?.config;\n // sent = custom property, after 1st request - sent = true, so no looping requests\n if (error?.response?.status === 403 && !prevRequest?.sent) {\n prevRequest.sent = true;\n const newAccessToken = await refresh();\n prevRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;\n return axiosPrivate(prevRequest);\n }\n return Promise.reject(error);\n }\n );\n\n // remove the interceptor when the component unmounts\n return () => {\n axiosPrivate.interceptors.response.eject(responseIntercept);\n axiosPrivate.interceptors.request.eject(requestIntercept);\n };\n }, [auth, refresh]);\n\n return axiosPrivate;\n};\n\nexport default useAxiosPrivate;\n
Run Code Online (Sandbox Code Playgroud)\n现在,这个私有 axios 实例在功能组件 - PanelLayout 中调用,它用于环绕页面并提供布局。
\n在这里,我尝试在 axios 中使用 AbortControllers 在组件挂载后终止请求。
\nfunction PanelLayout({ children, title }) {\n const [user, setUser] = useState(null);\n const axiosPrivate = useAxiosPrivate();\n const router = useRouter();\n\n useEffect(() => {\n let isMounted = true;\n const controller = new AbortController();\n const signal = controller.signal;\n\n const getUserProfile = async () => {\n try {\n const response = await axiosPrivate.get('/api/identity/profile', {\n signal,\n });\n console.log(response.data);\n isMounted && setUser(response.data.user);\n } catch (error) {\n console.log(error);\n router.push({\n pathname: '/seller/auth/login',\n query: { from: router.pathname },\n });\n }\n };\n getUserProfile();\n\n return () => {\n isMounted = false;\n controller.abort();\n };\n }, []);\n\n console.log('page rendered');\n\n return (\n <div className='flex items-start'>\n <Sidebar className='h-screen w-[10rem]' />\n <section className='min-h-screen flex flex-col'>\n <PanelHeader title={title} classname='left-[10rem] h-[3.5rem]' />\n <main className='mt-[3.5rem] flex-1'>{children}</main>\n </section>\n </div>\n );\n}\n\nexport default PanelLayout;\n
Run Code Online (Sandbox Code Playgroud)\n但是,上面的代码抛出以下错误:
\nCanceledError {message: 'canceled', name: 'CanceledError', code: 'ERR_CANCELED'}\ncode: "ERR_CANCELED"\nmessage: "canceled"\nname: "CanceledError"\n[[Prototype]]: AxiosError\nconstructor: \xc6\x92 CanceledError(message)\n__CANCEL__: true\n[[Prototype]]: Error\n
Run Code Online (Sandbox Code Playgroud)\n请建议如何避免上述错误并使 axios 正常工作。
\nErn*_*bua 15
我也遇到了同样的问题,我认为我的逻辑存在一些缺陷,导致组件被安装了两次。经过一番挖掘后,我发现 React 显然在 StrictMode 的新版本 18 中添加了此功能,其中 useEffect 运行了两次。这是文章的链接,清楚地解释了这种新行为。
解决此问题的一种方法是从应用程序中删除 StrictMode(临时解决方案)
另一种方法是使用 useRef 挂钩来存储一些状态,这些状态会在应用程序第二次安装时更新。
// CODE BEFORE USE EFFECT
const effectRun = useRef(false);
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
const signal = controller.signal;
const getUserProfile = async () => {
try {
const response = await axiosPrivate.get('/api/identity/profile', {
signal,
});
console.log(response.data);
isMounted && setUser(response.data.user);
} catch (error) {
console.log(error);
router.push({
pathname: '/seller/auth/login',
query: { from: router.pathname },
});
}
};
// Check if useEffect has run the first time
if (effectRun.current) {
getUserProfile();
}
return () => {
isMounted = false;
controller.abort();
effectRun.current = true; // update the value of effectRun to true
};
}, []);
// CODE AFTER USE EFFECT
Run Code Online (Sandbox Code Playgroud)
从YouTube视频中找到了解决方案。
归档时间: |
|
查看次数: |
9961 次 |
最近记录: |