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
相关产品推荐
相关产品推荐

