使用React.memo与useCallback仍出现组件重复渲染问题求助
我在父组件中使用了React.memo和useCallback,想避免点击按钮时组件出现不必要的重复渲染,但每次点击按钮后,控制台仍输出大量重复渲染日志:
Rendering Title Title.js:4 Rendering Title Count.js:4 Rendering Age Count.js:4 Rendering Age Button.js:4 Rendering button - Increment Age Button.js:4 Rendering button - Increment Age Count.js:4 Rendering Salary Count.js:4 Rendering Salary Button.js:4 Rendering button - Increment Salary Button.js:4 Rendering button - Increment Salary
相关组件代码
父组件代码
import React, { useCallback, useState } from 'react'; import Title from './Title'; import Count from './Count'; import Button from './Button'; function ParentComponent() { const [age, setAge] = useState(25); const [salary, setSalary] = useState(50000); const incrementAge = useCallback(() => { setAge(age + 1); }, [age]); const incrementSalary = useCallback(() => { setSalary(salary + 1000); }, [salary]); return ( <> <Title /> <Count text="Age" count={age} /> <Button handleClick={incrementAge}>Increment Age</Button> <Count text="Salary" count={salary} /> <Button handleClick={incrementSalary}>Increment Salary</Button> </> ); } export default ParentComponent;
Title.js代码
import React from 'react'; function Title() { console.log("Rendering Title"); return ( <div>Title</div> ); } export default React.memo(Title);
Count.js代码
import React from 'react'; function Count({ text, count }) { console.log(`Rendering ${text}`); return ( <div>{text} - {count}</div> ); } export default React.memo(Count);
Button.js代码
import React from 'react' //children -- props.children -- Increment age -- function Button({handleClick , children}) { console.log('Rendering button - ', children) return ( <button onClick={handleClick}>{children}</button> ) } export default React.memo(Button);
问题原因分析
useCallback依赖项导致回调引用更新:
当前incrementAge的依赖项包含age,每次age变化时,这个回调函数的引用会重新生成,导致接收它的<Button>Increment Age</Button>组件因为handleClickprops变化而重新渲染;同理incrementSalary依赖salary,每次薪资变化时对应按钮也会重新渲染。React StrictMode的双重渲染机制:
日志中每个渲染信息重复出现两次,是因为开发环境下开启了React严格模式(StrictMode)。该模式会故意执行两次渲染、副作用函数等操作,用于检测代码中潜在的问题,生产环境下不会有这种重复行为。必要的渲染无法避免:
比如当age变化时,展示年龄的<Count text="Age" count={age} />组件因为countprops更新,必然需要重新渲染,这属于正常的业务逻辑渲染,无法也不应该阻止。
解决方案
1. 优化useCallback,使用函数式更新消除依赖
将状态更新改为函数式写法,让useCallback的依赖项为空数组,保证回调函数的引用始终不变,从而避免按钮组件因回调变化而渲染:
const incrementAge = useCallback(() => { // 使用函数式更新,获取最新的prev状态 setAge(prevAge => prevAge + 1); }, []); // 依赖项为空数组 const incrementSalary = useCallback(() => { setSalary(prevSalary => prevSalary + 1000); }, []);
2. 忽略StrictMode的开发环境重复日志
如果是开发环境下的重复渲染日志,无需担心,这是StrictMode的正常调试行为,生产环境打包后会自动消失。
3. 确认React.memo的生效条件
React.memo只会浅比较组件的props,只要props没有变化,就会阻止组件重新渲染。优化后的回调引用不变,Button组件的props(handleClick和children)都不会变化,React.memo就能正常生效,阻止不必要的渲染。
内容的提问来源于stack exchange,提问作者Prakhar Gupta

