如何通过回调避免父组件状态变更时子组件重复渲染?
解决React父组件更新时子组件过度重渲染的问题
嘿,我来帮你搞定这个子组件频繁重渲染的问题!你已经用了useCallback来稳定deleteUser的引用,这一步做的很对,但还需要给子组件加一层“防护”,让它在props没变化的时候跳过重渲染。
核心问题:默认情况下子组件会跟着父组件重渲染
React的默认行为是,当父组件状态更新重渲染时,所有子组件都会跟着重渲染——哪怕子组件的props根本没变化。你现在的User组件就是这样,哪怕某个用户的数据和回调函数都没变,只要父组件的users数组更新,它还是会重新渲染一次。
解决方案:用React.memo包裹子组件
React.memo是一个高阶组件,它会帮我们缓存子组件的渲染结果。当组件的props和上一次渲染时的props做浅比较后没有变化,就直接复用之前的渲染结果,不会触发新的渲染。
只需要把你的User组件用React.memo包裹起来就行,修改后的代码如下:
const App = () => { const [users, setUsers] = useState([ { id: 1, name: "Cory" }, { id: 2, name: "Meg" }, { id: 3, name: "Bob" } ]); const deleteUser = useCallback(id => { setUsers(users => users.filter(user => user.id !== id)); }, []); const renderUser = user => { return <User key={user.id} user={user} onClick={deleteUser} />; }; return ( <div> <h1>Users</h1> <ul>{users.map(renderUser)}</ul> </div> ); }; // 用React.memo包裹User组件 const User = React.memo(props => { const onDeleteClick = () => { props.onClick(props.user.id); }; console.log(`${props.user.name} just rendered`); return ( <li> <input type="button" value="Delete" onClick={onDeleteClick} /> {props.user.name} </li> ); });
为什么这样有效?
- 你已经用
useCallback让deleteUser的引用保持稳定——它的依赖是空数组,所以只会在组件初始化时创建一次,后续不会因为父组件重渲染而改变。 - 当你删除某个用户时,父组件的
users数组更新,但未被删除的用户对象的引用还是原来的(因为filter返回的新数组里,保留的元素是原数组的引用)。 React.memo会对比User组件的user和onClickprops,发现没变化的话,就不会触发重渲染——只有被删除的那个用户对应的User组件会被卸载,其他的都不会重新渲染。
额外说明
如果你的user对象是会被重新创建的(比如父组件每次都生成新的对象实例),那浅比较可能会失效,这时候可以给React.memo传第二个参数,自定义props的比较逻辑:
const User = React.memo((props) => { // 组件内容 }, (prevProps, nextProps) => { // 自定义比较逻辑,返回true表示不需要重渲染 return prevProps.user.id === nextProps.user.id && prevProps.onClick === nextProps.onClick; });
不过在你当前的代码场景里,这个额外的比较函数是不需要的,浅比较就足够了。
内容的提问来源于stack exchange,提问作者Pierre
相关产品推荐
相关产品推荐

