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

React自定义受保护路由加载子组件时传递的userInfo props为空如何解决

问题原因

  • useEffect回调不支持直接传入async函数,React要求useEffect的返回值只能是清理函数,async函数返回的Promise会被当作无效的清理函数,存在运行隐患。
  • 核心导致子组件拿到空userInfo的原因是状态更新顺序错误:你在收到接口200响应后,先执行了setIsLoggedIn(true),此时React会触发组件重渲染、渲染受保护的子组件,而此时你还没有执行await r.json()获取用户信息,userInfo还是初始的空对象。等后续拿到用户信息执行setUserInfo后,才会第二次触发重渲染,给子组件传入正确的userInfo,所以子组件第一次挂载拿到的是空对象。
  • 缺少加载状态处理:接口请求未返回前,isLoggedIn为初始false,如果你的路由逻辑有未登录跳转的规则,会先触发跳转再收到登录状态,逻辑异常。

修复方案

1. 基础修复(解决子组件拿到空userInfo的问题)

调整状态更新顺序,先拿到用户信息后再同时更新登录状态和用户信息,同时修正useEffect的异步写法:

const UserAuthenticatedRoute = ({component: Component, ...rest}) => {
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    const [userInfo, setUserInfo] = useState({});
    const [loading, setLoading] = useState(true); // 新增加载状态

    useEffect(() => {
        // 把异步逻辑写在内部函数里,不要直接给useEffect传async
        const checkAuth = async () => {
            try {
                const r = await userService.isUserLoggedIn();
                if (r.status === 200){
                    const userInfo = await r.json();
                    // 拿到所有数据后再同时更新状态
                    setUserInfo(userInfo);
                    setIsLoggedIn(true);
                } else {
                    setIsLoggedIn(false);
                    // 未登录可以在这里跳转到登录页,比如用useNavigate跳转
                }
            } catch (e) {
                setIsLoggedIn(false);
                // 接口报错也按未登录处理
            } finally {
                setLoading(false); // 请求完成后关闭加载态
            }
        }
        checkAuth();
    }, []);

    // 请求中先渲染加载态,不要提前渲染路由内容
    if (loading) return <div>加载中...</div>

    return (
        <Route {...rest}  render={props => (
            <main>
                {isLoggedIn && <Component {...props} userInfo={userInfo}/>}
            </main>
        )}
        />
    );
};

改完之后,只有等用户信息拿到之后才会把isLoggedIn设为true,此时渲染子组件拿到的就是完整的userInfo。

2. 优化方案(避免逐层传userInfo)

如果受保护路由下的多层级组件都需要用到userInfo,建议用React Context来做全局状态共享,不用每层都通过props传递:

  1. 先创建UserContext
import { createContext, useContext, useState, useEffect } from 'react';
const UserContext = createContext(null);

// 封装Context Provider
export const UserProvider = ({children}) => {
    const [userInfo, setUserInfo] = useState({});
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        const checkAuth = async () => {
            // 和上面的checkAuth逻辑一致,拿到数据后更新状态
            try {
                const r = await userService.isUserLoggedIn();
                if (r.status === 200){
                    const user = await r.json();
                    setUserInfo(user);
                    setIsLoggedIn(true);
                }
            } catch(e) {
                setIsLoggedIn(false);
            } finally {
                setLoading(false);
            }
        }
        checkAuth();
    }, [])

    if (loading) return <div>加载中...</div>

    return <UserContext.Provider value={{isLoggedIn, userInfo}}>{children}</UserContext.Provider>
}

// 封装自定义hook方便子组件取数据
export const useUser = () => useContext(UserContext);
  1. 改造受保护路由,直接用Context里的状态:
const UserAuthenticatedRoute = ({component: Component, ...rest}) => {
    const {isLoggedIn, userInfo} = useUser();

    return (
        <Route {...rest}  render={props => (
            <main>
                {isLoggedIn && <Component {...props} userInfo={userInfo}/>}
            </main>
        )}
        />
    );
};
  1. 根组件包裹UserProvider:
root.render(
    <UserProvider>
        <BrowserRouter>
            {/* 你的所有路由逻辑 */}
        </BrowserRouter>
    </UserProvider>
)

之后所有子组件都可以直接用const {userInfo} = useUser()拿到用户信息,不用逐层传props。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:15:05