无依赖项useEffect触发Browser Router路由组件重复渲染问题求助
问题:路由组件控制台日志重复打印
场景说明
我有一个categories.js组件,在App.js中调用。组件内包含以下逻辑:
- 用
useEffect异步获取分类数据:
useEffect(() => { const fetchCategories = async () => { console.log('fetchCategories'); try { const response = await apiClient.get('/category/group'); console.log(response.data.data); setCategories(response.data.data); } catch (error) { console.log(error); } }; fetchCategories(); console.log('use effect 123'); }, []);
- 返回部分配置了Browser Router路由,挂载内部定义的
OpenPostsOverlayByRoute组件:
return ( <div> <Routes> <Route path="/posts/:postId/page/:page" element={<OpenPostsOverlayByRoute />} /> </Routes> {Object.keys(categories).map((group) => ( // 分类渲染逻辑 ))} </div> );
OpenPostsOverlayByRoute组件定义如下:
const OpenPostsOverlayByRoute = () => { const { postId, page } = useParams(); console.log('OpenPostsOverlayByRoute: ' + postId); /*useEffect(() => { // Update the state using the provided setter setRoutePostId(postId); console.log('OpenPostsOverlayByRoute: ' + postId); }, [postId);*/ };
当访问https://localhost:3000/posts/235/page/2时,控制台会两次打印OpenPostsOverlayByRoute: 235。
原因分析
重复打印主要有两个原因:
- React 18开发模式特性:React 18在严格模式(Strict Mode)下,会自动执行两次组件渲染、
useEffect等逻辑,目的是检测潜在的副作用问题,这是开发环境的正常行为,生产环境不会出现。 - 父组件状态更新触发重渲染:
categories.js中的setCategories更新状态后,会导致整个组件重渲染,进而让路由挂载的OpenPostsOverlayByRoute也跟着重新渲染一次。
解决方案
方案1:针对开发环境Strict Mode的重复渲染
如果只是开发环境下的重复打印,无需特殊处理,上线后生产环境会自动消失。如果想在开发环境屏蔽这个特性,可以找到项目入口文件(通常是index.js),去掉<React.StrictMode>的包裹:
// 原代码 ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <App /> </React.StrictMode> ); // 修改后 ReactDOM.createRoot(document.getElementById('root')).render( <App /> );
方案2:优化组件避免不必要重渲染
针对父组件状态更新导致的重渲染,可以用React.memo包裹OpenPostsOverlayByRoute组件,让它仅在props或路由参数变化时才重新渲染:
import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; // 用React.memo包裹组件 const OpenPostsOverlayByRoute = React.memo(() => { const { postId, page } = useParams(); console.log('OpenPostsOverlayByRoute: ' + postId); // 如果需要执行副作用逻辑,放在useEffect中,明确依赖项 useEffect(() => { // 这里写需要根据postId/page执行的逻辑,比如更新状态等 console.log('路由参数变化:', postId, page); }, [postId, page]); return ( // 组件的JSX内容 ); }); export default OpenPostsOverlayByRoute;
关于临时方案的说明
你提到的临时方案通过状态isRoute控制逻辑,虽然能临时避免重复执行,但并不是最优解——它没有解决组件重复渲染的根本问题,反而增加了状态管理的复杂度,建议替换为上面的优化方案。
内容的提问来源于stack exchange,提问作者hkguile
相关产品推荐
相关产品推荐

