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

React组件重复渲染两次问题求助:路由挂载函数重复调用

问题:React组件重复渲染两次

每次AppRoutes中定义的任意组件渲染时,getUser()钩子以及组件挂载时调用的所有函数都会执行两次,本质上就是组件被渲染了两次,找不到原因,求技术见解。


项目入口容器:HomeContainer

function HomeContainer() {
  const { user, authenticated } = useUser();
  const [toggled, setToggled] = useState(false);
  const location = useLocation();

  // const showHeader = !!location.pathname.endsWith("recording");
  const showHeader = recordingPathRegex.test(location.pathname);

  return (
    <Box>
      {user && authenticated ? <>
        <AppHeader user={user} setToggled={setToggled} toggled={toggled} reducedHeader={showHeader} />
          <Box sx={styles.container}>
            <Box sx={{ display: { md: 'none' } }}><SideNav toggled={toggled} setToggled={setToggled}/></Box>
            <Box component={'main'} sx={styles.mainSection}>
              <AppRoutes user={user} />
            </Box>
          </Box>
        </> : 
        <Box component={'main'} sx={styles.mainSection}>
            <AppRoutes user={null}/>
        </Box>
      }
      </Box>
    )
}

export default HomeContainer

认证状态钩子:useUser

该钩子用于检查认证状态,返回{user, authenticated}对象:

export function useUser() {
  const [user, setUser] = useState(null);
  const [authenticated, setAuthenticated] = useState(false);
  const navigate = useNavigate();
  const location = useLocation();
  const subscribe = location.pathname.split("/")[1]
  useEffect(() => {
    async function getUserDetails() {
      const { authenticated, user } = await getAuthenticatedUser();
      if (!authenticated && location.pathname !== '/signup') {
        navigate('/signin');
        return;
      }
      else if (authenticated && (location.pathname === '/signup' || location.pathname === '/signin')) {
        navigate('/meetings');
        return;
      }
      setUser(user);
      setAuthenticated(authenticated);
    }
      getUserDetails();
  }, [navigate]);

  return { user, authenticated };
}

认证用户获取函数:getAuthenticatedUser

该函数从浏览器localStorage中查找认证token,处理用户认证逻辑:

export async function getAuthenticatedUser() {
  const defaultReturnObject = { authenticated: false, user: null };
  try {
    const token = getTokenFromLocalStorage();
    if (!token) {
      return defaultReturnObject;
    }
    const response = await axios({
      method: 'GET',
      url: API_ROUTES.GET_USER,
      headers: {
        Authorization: `Token ${token}`
      }
    });
    const authenticated = response.data.status;
    return authenticated ? {authenticated: response.data.status, user: response.data.data} : false;
  }
  catch (err) {
    console.log('getAuthenticatedUser, Something Went Wrong', err);
    localStorage.clear();
    window.location.reload();
    return defaultReturnObject;
  }
}

路由配置组件:AppRoutes

定义认证/未认证用户可访问的路由:

function AppRoutes(props) {
  const { user } = props;

  return (
    <>
      {user ? <Routes>
        <Route path='/dashboard' element={<Dashboard />} />
        ...
      </Routes>
        : <Routes>
          <Route path='' element={<LandingPage />} /> 
          <Route path='/signin' element={<Signin />} />
          <Route path='/signup' element={<Signup />} />
          <Route path='/reset-password' element={<ForgotPassword />} />
          <Route path='/faq' element={<FAQ />} />
        </Routes>
      }
    </>
  )
}

export default AppRoutes

可能的原因及解决方案

1. React StrictMode 触发的双重渲染

这是开发环境下最常见的原因:React的StrictMode会刻意触发两次渲染(包括useEffect执行),用来检测副作用泄漏等潜在问题。

  • 解决:检查项目入口文件(如index.js),若有<StrictMode>包裹组件,可临时移除验证(生产环境下StrictMode自动失效,双重渲染问题会消失)。

2. useUser钩子依赖项缺失

useUser中的useEffect依赖数组只加了navigate,但内部用到了location.pathname,会导致依赖不完整,触发异常更新和额外渲染。

  • 解决:将location或location.pathname加入依赖数组,同时移除未使用的subscribe变量:
    useEffect(() => {
      // 原逻辑不变
    }, [navigate, location]);
    

3. 条件渲染导致AppRoutes重复挂载

HomeContainer中根据认证状态渲染两个不同的AppRoutes实例(一个传user,一个传null),状态切换时会销毁重建组件,引发额外渲染。

  • 解决:统一AppRoutes实例,将认证判断移到组件内部:
    // HomeContainer中修改为:
    <Box component={'main'} sx={styles.mainSection}>
      <AppRoutes user={user} authenticated={authenticated} />
    </Box>
    
    // AppRoutes内部调整:
    function AppRoutes({ user, authenticated }) {
      return (
        <Routes>
          <Route path='/faq' element={<FAQ />} />
          {authenticated ? (
            <>
              <Route path='/dashboard' element={<Dashboard />} />
              {/* 其他认证路由 */}
            </>
          ) : (
            <>
              <Route path='' element={<LandingPage />} />
              <Route path='/signin' element={<Signin />} />
              {/* 其他未认证路由 */}
            </>
          )}
        </Routes>
      )
    }
    

4. getAuthenticatedUser返回值类型不一致

函数中当!authenticated时返回false,其他场景返回对象类型,导致useUser中状态更新异常,触发额外渲染。

  • 解决:统一返回值类型,将return false改为return defaultReturnObject:
    return authenticated ? {authenticated: response.data.status, user: response.data.data} : defaultReturnObject;
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:04:56