You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件state不更新:受保护路由loggedIn状态始终为false问题

受保护路由无法正确获取登录状态导致错误重定向

我正在创建仅对已登录用户开放的受保护路由,但在ProtectedRoutes组件中无法正确获取loggedIn状态,该状态始终为false,导致页面被重定向至"/login"。

App.tsx

interface loginContextInterface {
    loggedIn: boolean;
    setLoggedIn: (value: (((prevState: boolean) => boolean) | boolean)) => void;
}

export const LoginContext = createContext({} as loginContextInterface);

export default function App() {
    const [ loggedIn, setLoggedIn ] = useState(false)

     useEffect(() => {
        console.log("before", loggedIn)
        isLoggedIn().then((r) => {
            console.log("R", r)
            setLoggedIn(r)
        })
        console.log("after", loggedIn)
    }, [loggedIn])

    return (
        <LoginContext.Provider value={{loggedIn, setLoggedIn}}>
            <Router>
                <MenuHeader />
                <Routes>
                    <Route path="/" element={<Home/>}/>
                    <Route path="/tasks" element={<ProtectedRoutes/>}>
                        <Route path="/" element={<Tasks/>}/>
                    </Route>
                    <Route path="/login" element={<Login />}/>
                    <Route path="/logout" element={<Logout />}/>
                    <Route path="/register" element={<Register/>}/>
                </Routes>
            </Router>
        </LoginContext.Provider>
  );
}

ProtectedRoutes.tsx

export const ProtectedRoutes = () =>{
    const location = useLocation();
    const {loggedIn} = useContext(LoginContext)

    console.log("protected", loggedIn)

    return (
        loggedIn ? <Outlet/> : <Navigate to="/login" replace state={{location}}/>
    );
}

补充说明

isLoggedIn函数通过调用服务端API,利用Cookie验证用户是否登录。添加日志后,访问/tasks路由并被重定向至/login时,日志输出如下:

VM109:236 protected false
App.tsx:21 before false
App.tsx:26 after false
App.tsx:21 before false
App.tsx:26 after false
2App.tsx:23 R true
App.tsx:21 before true
App.tsx:26 after true
App.tsx:23 R true

请问我哪里理解有误?


问题分析与解决

核心问题

  1. 异步请求时序偏差:组件挂载时loggedIn初始值为false,isLoggedIn()是异步API请求,在请求返回结果前,ProtectedRoutes已经完成渲染,读取到的是初始false状态,直接触发重定向逻辑。
  2. useEffect依赖错误:将loggedIn作为useEffect的依赖项,导致每次loggedIn状态更新都会重新执行isLoggedIn()请求,造成API重复调用(日志中两次出现R true就是这个原因)。

修复步骤

1. 修正useEffect的依赖数组

把useEffect的依赖改为空数组,确保登录状态验证只在组件挂载时执行一次:

useEffect(() => {
    console.log("before", loggedIn)
    isLoggedIn().then((r) => {
        console.log("R", r)
        setLoggedIn(r)
    })
    console.log("after", loggedIn)
}, []) // 移除loggedIn依赖

2. 增加加载状态处理

在登录状态验证的过程中,添加加载状态,避免路由提前执行重定向逻辑:

export default function App() {
    const [ loggedIn, setLoggedIn ] = useState(false)
    const [ isLoading, setIsLoading ] = useState(true) // 新增加载状态

     useEffect(() => {
        isLoggedIn().then((r) => {
            setLoggedIn(r)
        }).finally(() => {
            setIsLoading(false) // 请求完成后结束加载
        })
    }, [])

    // 加载过程中显示提示,阻止路由渲染
    if (isLoading) {
        return <div>加载中...</div>
    }

    return (
        <LoginContext.Provider value={{loggedIn, setLoggedIn}}>
            {/* 原有路由代码 */}
        </LoginContext.Provider>
    );
}

3. (可选)在ProtectedRoutes中处理加载状态

如果不想在App层统一处理加载,也可以将isLoading加入Context,在ProtectedRoutes中判断:

// 更新Context接口
interface loginContextInterface {
    loggedIn: boolean;
    setLoggedIn: (value: (((prevState: boolean) => boolean) | boolean)) => void;
    isLoading: boolean; // 新增加载状态字段
}

// App中更新Provider的值
<LoginContext.Provider value={{loggedIn, setLoggedIn, isLoading}}>

// 修改ProtectedRoutes组件
export const ProtectedRoutes = () =>{
    const location = useLocation();
    const {loggedIn, isLoading} = useContext(LoginContext)

    if (isLoading) {
        return <div>加载中...</div>
    }

    return (
        loggedIn ? <Outlet/> : <Navigate to="/login" replace state={{location}}/>
    );
}

内容的提问来源于stack exchange,提问作者Fifips

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:26:16