授权完成后,反应受保护的路由不会重定向到 children
React protected route not redirecting to children when authorisation finished
我正在尝试在我的受保护路由中加入一层身份验证,我一直在使用以下方法进行此操作:
function ProtectedRoute ({ children }) {
const { user } = useContext(AuthContext);
return user.isVerifying ? (
<p>Is loading...</p>
) : user.verified ? children : <Navigate to="/login" />;
}
export default ProtectedRoute;
我的用户上下文已使用以下信息初始化:
{
"user": null,
"verified": false,
"isVerifying": false
}
目前,用户授权状态由我的 AuthContext.Provider
内部的一个函数 (authUser
) 检查,该函数目前在我的组件的每个渲染器上运行(如下所示) .这是一个异步函数,它进行 API 调用以检查存储在 localStorage 中的用户 JWT 令牌是否有效。
import React, { createContext, useEffect, useState } from 'react';
import axios from 'axios';
const AuthContext = createContext();
function AuthProvider({children}) {
const [user, setUser] = useState({
"user": null,
"verified": false,
"isVerifying": true
});
async function authUser() {
const userLocal = localStorage.getItem('user');
if (userLocal) {
const userLocalJSON = JSON.parse(userLocal);
const verifyResponse = await verifyToken(userLocalJSON);
if (verifyResponse) {
setUser({"user": userLocalJSON, "verified": true, "isVerifying": false});
} else {
setUser({"user": userLocalJSON, "verified": false, "isVerifying": false});
}
}
}
async function verifyToken(userJSON) {
try {
setUser({"user": null, "verified": false, "isVerifying": true});
// Make API call
if (response.status === 200) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
useEffect(() => {
authUser();
}, [])
return (
<AuthContext.Provider value={{user, authUser}}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
export { AuthProvider };
验证令牌后,用户上下文将更新为 verified = true
,其中包含有关用户的信息。在此过程中,用户上下文 isVerifying = true
(相当于 isLoading
)并在 API 调用完成后设置回 false。
现在有一些问题:
在 DOM 的每个 re-render 上,上下文信息都被设置回其默认值。有没有办法让 useEffect()
只在 localStorage 发生变化时触发?我已经尝试实施以下 solution,它向 localStorage 添加一个侦听器,然后在更改时触发 useEffect。我无法在 localStorage 更改时启动它。也许我做错了什么(见下文)?
useEffect(() => {
window.addEventListener('storage', authUser)
return () => {
window.removeEventListener('storage', authUser)
}
}, [])
因为初始 isVerifying = false
和到 true 的切换不会在呈现 DOM 之前发生,所以受保护的路由导航到登录页面。即使最终设置了用户上下文,它仍保留在登录页面上。永远不会显示加载页面,而是立即重定向到登录页面。不幸的是,我最初无法设置 isVerifying = true
,因为 non-logged 用户会卡在加载页面上。
任何建议都会很有帮助。
解决问题 2:
你的三元数并没有完全按照你的意愿行事。这有点像说“如果 user.isVerifying 或 !user.verified,导航”。这不是我们想要的。
嵌入式三元组可能很棘手,我通常会尽可能避免使用它们。由于这个组件非常小,实际上您根本不需要使用三元组来执行此操作。我认为这应该有效:
设置 isVerifying
从 true
开始。那么:
function ProtectedRoute ({ children }) {
const { user } = useContext(AuthContext);
if (user.isVerifying) {
return <p>Is loading...</p>
}
if (user.verified === false) {
return <Navigate to="/login" />
}
return children
}
export default ProtectedRoute;
编辑:您还需要更新 authUser
中的验证状态。请注意,如果 userLocal
为真,您如何仅更新加载状态。最好的办法是开始加载,并始终在最后调用 setUser。这不是唯一的方法,但我认为它应该有效:
async function authUser() {
setUser(currentState => {...currentState, isVerifying: true})
const userLocal = localStorage.getItem('user');
if (!userLocal) {
setUser({
user: null,
verified: false,
isVerifying: false,
})
}
const userLocalJSON = JSON.parse(userLocal);
const verifyResponse = await verifyToken(userLocalJSON);
setUser({
user: userLocalJSON,
verified: verifyResponse,
isVerifying: false,
})
}
我正在尝试在我的受保护路由中加入一层身份验证,我一直在使用以下方法进行此操作:
function ProtectedRoute ({ children }) {
const { user } = useContext(AuthContext);
return user.isVerifying ? (
<p>Is loading...</p>
) : user.verified ? children : <Navigate to="/login" />;
}
export default ProtectedRoute;
我的用户上下文已使用以下信息初始化:
{
"user": null,
"verified": false,
"isVerifying": false
}
目前,用户授权状态由我的 AuthContext.Provider
内部的一个函数 (authUser
) 检查,该函数目前在我的组件的每个渲染器上运行(如下所示) .这是一个异步函数,它进行 API 调用以检查存储在 localStorage 中的用户 JWT 令牌是否有效。
import React, { createContext, useEffect, useState } from 'react';
import axios from 'axios';
const AuthContext = createContext();
function AuthProvider({children}) {
const [user, setUser] = useState({
"user": null,
"verified": false,
"isVerifying": true
});
async function authUser() {
const userLocal = localStorage.getItem('user');
if (userLocal) {
const userLocalJSON = JSON.parse(userLocal);
const verifyResponse = await verifyToken(userLocalJSON);
if (verifyResponse) {
setUser({"user": userLocalJSON, "verified": true, "isVerifying": false});
} else {
setUser({"user": userLocalJSON, "verified": false, "isVerifying": false});
}
}
}
async function verifyToken(userJSON) {
try {
setUser({"user": null, "verified": false, "isVerifying": true});
// Make API call
if (response.status === 200) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
useEffect(() => {
authUser();
}, [])
return (
<AuthContext.Provider value={{user, authUser}}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
export { AuthProvider };
验证令牌后,用户上下文将更新为 verified = true
,其中包含有关用户的信息。在此过程中,用户上下文 isVerifying = true
(相当于 isLoading
)并在 API 调用完成后设置回 false。
现在有一些问题:
在 DOM 的每个 re-render 上,上下文信息都被设置回其默认值。有没有办法让
useEffect()
只在 localStorage 发生变化时触发?我已经尝试实施以下 solution,它向 localStorage 添加一个侦听器,然后在更改时触发 useEffect。我无法在 localStorage 更改时启动它。也许我做错了什么(见下文)?useEffect(() => { window.addEventListener('storage', authUser) return () => { window.removeEventListener('storage', authUser) } }, [])
因为初始
isVerifying = false
和到 true 的切换不会在呈现 DOM 之前发生,所以受保护的路由导航到登录页面。即使最终设置了用户上下文,它仍保留在登录页面上。永远不会显示加载页面,而是立即重定向到登录页面。不幸的是,我最初无法设置isVerifying = true
,因为 non-logged 用户会卡在加载页面上。
任何建议都会很有帮助。
解决问题 2:
你的三元数并没有完全按照你的意愿行事。这有点像说“如果 user.isVerifying 或 !user.verified,导航”。这不是我们想要的。
嵌入式三元组可能很棘手,我通常会尽可能避免使用它们。由于这个组件非常小,实际上您根本不需要使用三元组来执行此操作。我认为这应该有效:
设置 isVerifying
从 true
开始。那么:
function ProtectedRoute ({ children }) {
const { user } = useContext(AuthContext);
if (user.isVerifying) {
return <p>Is loading...</p>
}
if (user.verified === false) {
return <Navigate to="/login" />
}
return children
}
export default ProtectedRoute;
编辑:您还需要更新 authUser
中的验证状态。请注意,如果 userLocal
为真,您如何仅更新加载状态。最好的办法是开始加载,并始终在最后调用 setUser。这不是唯一的方法,但我认为它应该有效:
async function authUser() {
setUser(currentState => {...currentState, isVerifying: true})
const userLocal = localStorage.getItem('user');
if (!userLocal) {
setUser({
user: null,
verified: false,
isVerifying: false,
})
}
const userLocalJSON = JSON.parse(userLocal);
const verifyResponse = await verifyToken(userLocalJSON);
setUser({
user: userLocalJSON,
verified: verifyResponse,
isVerifying: false,
})
}