React使用memo减少重渲染未达预期,求原因及子组件影响分析
React.memo优化无效与Redux相关重渲染问题分析
问题背景
尝试通过React.memo减少组件重渲染次数,但效果未达预期,推测与Redux reducer的多阶段更新(触发、成功、错误)及App中的额外dispatch有关。相关组件代码如下:
App.jsx(伪代码)
console.log("App"); return <div> <Switch> ... <Route render={Home} /> ... </Switch> </div> export default App;
Home.jsx
const Home = () => { console.log("Home"); return ( <div> <DashboardBill/> </div> ) } export default Home;
Dashboard.jsx
const Dashboard = () => { console.log("Dashboard"); return <div> <MyBill/> </div> } export default memo(Dashboard);
MyBill.jsx
const mapStateToProps = ({billData}) => ({ billData }); const mapDispatchToProps = (dispatch) => ({ loadData: () => dispatch(loadBillData) }); const MyBill = ({billData, loadData}) => { console.log("MyBill"); const [manipulatedData, setManipulatedData] = useState({}); useEffect(() => { !billData && loadData(); }, []); useEffect(() => { billData && setManipulatedData(someUtil.manipulate(billData)); }, [billData]); return <div>{manipulatedData}</div>; } export default connect(mapStateToProps, mapDispatchToProps)(memo(MyBill));
一、重渲染现象的核心原因
- Redux多阶段store更新:
loadBillData的dispatch会触发reducer的三个阶段(触发、成功/错误),每个阶段都会更新全局store。只要store变化,所有通过connect绑定的组件都会重新计算props,MyBill作为绑定组件会被多次触发重渲染。 - App的额外dispatch影响:App中存在的其他dispatch事件会导致App组件重渲染,进而触发未使用
memo包裹的Home组件重渲染。虽然Dashboard用了memo,但如果Home重渲染时传递给Dashboard的props发生变化(或Dashboard本身依赖store),也会引发Dashboard的重渲染,最终传导到MyBill。
二、React.memo未生效的关键因素
- mapDispatchToProps生成新函数引用:你使用了函数形式的
mapDispatchToProps,每次组件计算时都会返回一个新对象,其中的loadData是新创建的函数。React.memo默认做浅对比,会认为loadData的引用变化属于props变更,因此无法阻止重渲染。 - billData的引用频繁变化:reducer的多阶段更新会生成新的
billData对象引用(即使是“加载中”这类状态变更),浅对比会判定props发生变化,触发MyBill重渲染。 - 包裹顺序的隐性问题:虽然你写了
connect(...)(memo(MyBill)),但connect本身会在store变化时重新生成props,若props中存在不稳定的引用(比如上述的loadData),memo的对比逻辑会被绕过,无法发挥作用。
三、若MyBill包含修改Redux store的子组件的影响
- 进一步增加重渲染次数:子组件的dispatch会再次触发store更新,引发所有绑定组件的props重新计算,MyBill及其父组件都会被额外触发重渲染。如果子组件的dispatch同样有多个阶段(触发、成功、错误),重渲染次数会叠加。
- 放大现有优化失效问题:如果MyBill的
memo已经因为上述原因失效,子组件的store修改会让重渲染问题更严重;即使memo正常工作,只要子组件的dispatch导致MyBill的billData或loadDataprops变化,依然会触发MyBill重渲染。 - 可能引发连锁更新:子组件修改store后,若其他无关组件也绑定了该store数据,会导致无关组件的无意义重渲染,整体应用性能下降。
内容的提问来源于stack exchange,提问作者physicsboy
相关产品推荐
相关产品推荐

