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

React Router刷新页面时组件多次调用渲染问题排查

问题现象

刷新页面时组件被多次调用,挂载逻辑重复触发,相关代码和日志如下:

路由配置文件 MainLayout.tsx

import { FC, ReactElement, useEffect } from 'react'
import { Routes, Route, BrowserRouter } from 'react-router-dom'
import { createBrowserHistory } from 'history'
import { useLocation } from 'react-router-dom'
import { Navigate } from 'react-router-dom'
import { Outlet } from 'react-router-dom'
import { IntroductionPage } from '../pages/introduction/introduction-page'
import { useTranslation } from 'react-i18next'
import { storage } from '../utils/storage'

export const history = createBrowserHistory()

export const MainLayout: FC = () => {
const { t } = useTranslation()

useEffect(() => {
  console.log('MainLayout:: constructor')
}, [])

const RequireAuth = (): ReactElement => {
  const { token } = storage.getState().authReducer
  let location = useLocation()
  if (!token) return <Navigate to="/login" state={{ from: location }} />
  return <Outlet />
}

return (
  <BrowserRouter history={history}>
    <div className="main-wrapper">
      <div className="content-wrapper">
        <div className="main-content">
          <Routes>
            <Route path="/" element={<Login />} />
            <Route path="/login" element={<Login />} />
            <Route element={<RequireAuth />}>
              <Route path="/acceptance" element={<AcceptancePage />} />
              <Route path="/introduction/:page" element={<IntroductionPage />} />
            </Route>
          </Routes>
        </div>
      </div>
    </div>
  </BrowserRouter>
)
}

介绍页逻辑 IntroductionPage

useEffect(() => {
console.log('IntroductionPage:: constructor')
setIntroduction(introductions[+page - 1])
}, [])

控制台异常日志

刷新介绍页时输出:

IntroductionPage:: constructor
MainLayout.tsx:23 MainLayout:: constructor
IntroductionPage:: constructor
IntroductionPage:: constructor
问题根因

三个问题共同导致组件重复挂载:

  • 最核心的问题:RequireAuth路由守卫组件被定义在MainLayout组件内部。每次MainLayout触发重渲染,都会生成一个全新引用的RequireAuth函数,React做组件diff时会判定这是完全不同的组件类型,直接卸载旧的RequireAuth下挂载的所有子路由(包括IntroductionPage),再重新挂载新组件,直接导致子组件的挂载副作用重复执行。
  • 如果使用React 18开发环境,默认开启的严格模式会在开发环境对组件执行「挂载-卸载-重新挂载」的双调用流程,用来提前检测不规范的副作用写法,这会额外触发一次组件挂载日志,生产环境不会出现该双调用行为。
  • BrowserRouter的props传参错误:react-router-dom v6版本的BrowserRouter本身不支持history入参,自定义history实例需要使用官方提供的HistoryRouter,错误传入不识别的props会导致路由内部状态同步异常,触发额外的重渲染和重挂载流程。
修复方式
  • 将RequireAuth组件移动到MainLayout组件外部定义,禁止在组件渲染函数内部声明新的子组件,避免组件引用随渲染变化导致的整棵子树重挂载。
  • 移除BrowserRouter上错误传入的history属性,如果需要全局操作路由跳转,直接使用useNavigate钩子即可,不要依赖外部导出的history实例。
  • 介绍页的useEffect存在闭包陷阱:空依赖数组内使用了page变量,后续page变化时不会触发介绍内容更新,需要将page加入useEffect的依赖数组。
  • 若不需要开发环境的严格模式双检测,可以在项目入口文件移除<StrictMode>包裹,不建议为了规避日志直接关闭,该检测能提前暴露很多副作用写法问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 16:42:16