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

路由导航时useContext变量重置为undefined的问题排查

React useContext 路由导航后状态重置为undefined问题排查与解决

问题描述

在React项目中使用useContext管理全局用户状态,登录成功后设置状态能正常读取,但通过FMApp组件内导航栏跳转路由后,appUserCtx会变为undefined,无法获取之前设置的上下文值。

相关代码片段

App.js

function App() {
    const [appUserCtx, setAppUserCtx] = useState();
    return (
        <AppUserContext.Provider value={{ appUserCtx, setAppUserCtx }}>
            <Router>
                <Switch>
                    <Route path={['/admin/*', '/admin']} component={Admin} />
                    /**Login */
                    <Route exact path="/fmApp" component={Login} />
                    <Route exact path={'/fmApp/customer/:id'} component={CustomerLogin} />
                    <Route path={'/fmApp/*'} component={FMApp} />
                    <Route exact path={Constants.ERROR_PAGE} component={ErrorPage} />
                </Switch>
            </Router >
        </AppUserContext.Provider>
    );
}

AppUserContext.js

import { createContext, useState } from "react";

const AppUserContext = createContext({});

export default AppUserContext;

useAuth.js

import { useContext, useDebugValue } from "react";
import AppUserContext from "./context/AppUserContext";

const useAuth = () => {
    return useContext(AppUserContext);
}

export default useAuth;

CustomerLogin.js 登录状态设置代码

UserService.verifyLogin(axiosPrivate, appUser).
    then(response => {
        localStorage.setItem('authUser', JSON.stringify(response.data));
        setAppUser(response.data);
        setAppUserCtx(response.data);
        setSubmitted(false);
        props.history.push({ pathname: "/fmApp/dailySalesList" });
    }).catch(e => {
        setSubmitted(false);
        setModelVariant('error')
        setMessage(e.response.data);
        autoRef.current.setModelOpen(true);
    })

FMApp.js 导航组件代码

const FMApp = (props) => {
  const history = useHistory();

  const rediretToAmdin = () => {
    window.location.href = "/admin";
  }

  const {appUserCtx, setAppUserCtx} = useAuth();

  return (
      <div>
        <Navbar className='navbar'  variant="dark" expand="lg" sticky="top">
          <Navbar.Brand href="#home" className='p-0' style={{marginTop:'-12px'}}> <FMLogo height='30'/></Navbar.Brand>
          <Navbar.Toggle aria-controls="basic-navbar-nav" />
          <Navbar.Collapse id="basic-navbar-nav">
            <Nav className="mr-auto">
              <NavDropdown title="Stocks" id="basic-nav-dropdown">
                <NavDropdown.Item href="/fmApp/fuelStockList">Fuel Stock</NavDropdown.Item>
              </NavDropdown>
              <div><Logout className="float-left" /></div>
            </Nav>
          {/* show admin access link only if user role is as admin enabled*/}
            {appUserCtx.userRoleMO?.isAdmin === 1 && <div> <StylesProvider injectFirst>  <Link className="adminLink" onClick={rediretToAmdin}>Go to admin</Link></StylesProvider></div>}
            <div> <ROHeader></ROHeader></div>
          </Navbar.Collapse>
        </Navbar>

        <div className="container mt-3">
          <Switch>
            //stock
               <Route exact path="/fmApp/fuelStockList" component={FuelStockList} />
          </Switch>
        </div>
      </div>
  );
}

export default FMApp;

问题原因

  1. 状态未持久化:useContext + useState的状态仅存在于内存中,页面刷新会导致App组件重新挂载,状态重置为初始值undefined。
  2. 错误的导航方式:rediretToAmdin使用window.location.href跳转,会触发页面全量刷新,丢失内存中的全局状态。
  3. 初始状态未读取本地存储:App组件初始化时未从localStorage读取已保存的用户信息,刷新后状态为空。
  4. 导航栏链接触发页面刷新:NavDropdown.Item的href属性会触发浏览器原生跳转,导致页面刷新。

解决方案

1. 初始化时从localStorage恢复状态

修改App.js,在useState初始化时读取localStorage中的用户数据:

function App() {
    // 从localStorage读取已保存的用户,作为初始状态
    const initialUser = localStorage.getItem('authUser') 
        ? JSON.parse(localStorage.getItem('authUser')) 
        : undefined;
    const [appUserCtx, setAppUserCtx] = useState(initialUser);
    
    return (
        <AppUserContext.Provider value={{ appUserCtx, setAppUserCtx }}>
            <Router>
                <Switch>
                    <Route path={['/admin/*', '/admin']} component={Admin} />
                    <Route exact path="/fmApp" component={Login} />
                    <Route exact path={'/fmApp/customer/:id'} component={CustomerLogin} />
                    <Route path={'/fmApp/*'} component={FMApp} />
                    <Route exact path={Constants.ERROR_PAGE} component={ErrorPage} />
                </Switch>
            </Router >
        </AppUserContext.Provider>
    );
}

2. 使用React Router导航替代原生跳转

修改FMApp中的rediretToAmdin函数,用history.push代替window.location.href,避免页面刷新:

const FMApp = (props) => {
  const history = useHistory();

  const rediretToAmdin = () => {
    // 替换为React Router的导航方法
    history.push("/admin");
  }

  const {appUserCtx, setAppUserCtx} = useAuth();

  return (
      // 其余代码保持不变
  );
}

3. 替换导航栏链接为React Router的Link组件

修改FMApp中的NavDropdown.Item,使用React Router的Link组件避免页面刷新:

// 先导入Link组件
import { Link } from 'react-router-dom';

// 替换原有NavDropdown.Item
<NavDropdown.Item as={Link} to="/fmApp/fuelStockList">Fuel Stock</NavDropdown.Item>

4. 可选:封装状态更新逻辑,自动同步localStorage

在useAuth钩子中封装状态更新函数,确保状态变更时自动同步到localStorage:

import { useContext, useCallback } from "react";
import AppUserContext from "./context/AppUserContext";

const useAuth = () => {
    const { appUserCtx, setAppUserCtx } = useContext(AppUserContext);
    
    // 封装带持久化的状态更新函数
    const setAuthUser = useCallback((user) => {
        setAppUserCtx(user);
        user 
            ? localStorage.setItem('authUser', JSON.stringify(user))
            : localStorage.removeItem('authUser');
    }, [setAppUserCtx]);

    return { appUserCtx, setAuthUser };
}

export default useAuth;

然后在CustomerLogin中使用封装后的setAuthUser:

// 导入useAuth并获取setAuthUser
const { setAuthUser } = useAuth();

UserService.verifyLogin(axiosPrivate, appUser).
    then(response => {
        setAppUser(response.data);
        setAuthUser(response.data); // 使用封装后的函数
        setSubmitted(false);
        props.history.push({ pathname: "/fmApp/dailySalesList" });
    }).catch(e => {
        // 错误处理代码保持不变
    })

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:24:54