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

React-Django应用登录用户状态持久化与受保护路由问题求助

解决React受保护路由初始用户状态为null导致的错误重定向问题

问题原因

当前AuthContext初始化时user默认设为null,而从localStorage读取用户数据的逻辑放在useEffect中——这个钩子是组件挂载后才执行的。所以App组件第一次渲染时,user还是null,触发了跳转到登录页的逻辑;等useEffect执行完更新用户状态时,重定向已经完成了。

解决方案一:初始化时直接读取localStorage

直接在初始化state时从localStorage获取用户数据,避免依赖useEffect的异步执行时机。

修改src/context/AuthContext.js:

export const AuthContext = createContext()

export const authReducer = (state, action) => {
    switch (action.type){
        case "LOGIN":  
            return { user: action.payload }
        case "LOGOUT": 
            localStorage.removeItem("mysUser")
            return { user: null }
        default: 
        return state
    }   
}

export const AuthContextProvider = ({ children }) =>{
    // 初始化时直接读取localStorage
    const initialUser = JSON.parse(localStorage.getItem("mysUser")) || null;
    
    const [state, dispatch] = useReducer(authReducer, {
        user: initialUser
    })

    // 保留useEffect处理后续可能的token验证等逻辑(可选)
    useEffect(() => {
        // 如果需要验证token有效性,可以在这里调用后端接口
        // 比如:
        // if (initialUser) {
        //     fetch('/api/validate-token', {
        //         headers: { 'Authorization': `Bearer ${initialUser.token}` }
        //     })
        //     .then(res => {
        //         if (!res.ok) dispatch({ type: "LOGOUT" })
        //     })
        // }
    }, [])

    console.log("AuthContext state: ", state)
    return (
        <AuthContext.Provider value={{ ...state, dispatch}}>
            { children }
        </AuthContext.Provider>
    )
}

这样App组件第一次渲染时,user就已经是localStorage中的值(如果存在),不会触发错误重定向。

解决方案二:添加加载状态,避免过早重定向

如果需要后端验证token有效性(防止localStorage中的过期token被滥用),可以在AuthContext中添加loading状态,在验证完成前不做重定向判断。

修改AuthContext.js

export const AuthContext = createContext()

export const authReducer = (state, action) => {
    switch (action.type){
        case "LOGIN":  
            return { ...state, user: action.payload, loading: false }
        case "LOGOUT": 
            localStorage.removeItem("mysUser")
            return { ...state, user: null, loading: false }
        case "SET_LOADING":
            return { ...state, loading: action.payload }
        default: 
        return state
    }   
}

export const AuthContextProvider = ({ children }) =>{
    const initialState = {
        user: JSON.parse(localStorage.getItem("mysUser")) || null,
        loading: true
    };
    
    const [state, dispatch] = useReducer(authReducer, initialState)

    useEffect(() => {
        const validateUser = async () => {
            const user = state.user;
            if (!user) {
                dispatch({ type: "SET_LOADING", payload: false });
                return;
            }
            // 调用Django后端的token验证接口
            try {
                const res = await fetch('/api/verify-token', {
                    headers: { 
                        'Authorization': `Bearer ${user.token}`,
                        'Content-Type': 'application/json'
                    }
                });
                if (!res.ok) {
                    throw new Error("Token无效");
                }
                // 验证通过,保持用户状态
                dispatch({ type: "LOGIN", payload: user });
            } catch (err) {
                // 验证失败,清除用户状态
                dispatch({ type: "LOGOUT" });
            }
        };

        validateUser();
    }, [])

    console.log("AuthContext state: ", state)
    return (
        <AuthContext.Provider value={{ ...state, dispatch}}>
            { children }
        </AuthContext.Provider>
    )
}

修改App.js中的受保护路由

function App() {
  const { user, loading } = useAuthContext()

  return (
    <div className="App">
      <BrowserRouter>
        <NavbarComponent/>
        <div>
          <Routes>
            {/* protected route */}
            <Route
                path="/about"
                element={
                    loading ? (
                        <div>加载中...</div> {/* 可以替换为自定义加载组件 */}
                    ) : user ? (
                        <About />
                    ) : (
                        <Navigate to="/login" />
                    )
                }
                exact
              />
            
            {/* non-protected routes */}
            <Route path="/" element={<Home />} />
            <Route path="/signup" element={<Signup />} exact />
            <Route path="/login" element={<Login/>} exact />
          </Routes>
        </div>
        <FooterComponent />
      </BrowserRouter>
    </div>
  );
}

export default App;

这种方式会在验证用户状态的过程中显示加载界面,避免因为初始user为null导致的错误重定向,同时保证只有有效的用户才能访问受保护路由。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 05:45:39