React中无变化子组件重复渲染的原因及解决方法
问题解析:子组件无props变化却随父组件重渲染的原因与解决办法
先贴一下你的代码方便对照:
import React, { useState, useEffect } from 'react'; const Test = () => { const [ count, setCount ] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>Count up!</button> <ChildComponent /> </div> ) } const ChildComponent = () => { useEffect(() => { console.log('render!'); return () => console.log('unmounted...'); }); return ( <div>children</div> ) }; export default Test;
为什么点击按钮后ChildComponent会重新渲染?
这是React的默认渲染规则导致的:当父组件的状态或props更新触发自身重渲染时,它所有的子组件(不管有没有接收props、props有没有变化)都会跟着一起重渲染。
在你的代码里,点击按钮后Test组件的count状态更新,触发了Test的重新渲染,作为子组件的ChildComponent自然也会被连带重渲染——哪怕它完全不依赖父组件的任何状态或props。而useEffect在组件重渲染时,会先执行上一次渲染的清理函数(也就是return里的console.log('unmounted...')),再执行新的effect回调(console.log('render!')),所以你会看到控制台输出那两行内容。
怎么避免这种不必要的重新渲染?
最直接的方案是用React.memo包裹子组件,它会对组件的props做浅比较,如果props没有变化,就跳过该组件的重渲染。
修改后的代码如下:
import React, { useState, useEffect, memo } from 'react'; const Test = () => { const [ count, setCount ] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>Count up!</button> <ChildComponent /> </div> ) } // 用memo包裹子组件 const ChildComponent = memo(() => { useEffect(() => { console.log('render!'); return () => console.log('unmounted...'); }); return ( <div>children</div> ) }); export default Test;
这样修改后,点击“Count up!”按钮时,Test组件依然会正常重渲染,但ChildComponent因为没有props变化,React.memo会阻止它的不必要重渲染,控制台也就不会再输出那两行内容了。
如果之后你的ChildComponent需要接收父组件的props,且props里包含函数、对象这类引用类型的值,那你可能还需要配合useCallback或useMemo来缓存这些引用,确保React.memo的浅比较能正常生效——不过就当前的代码来说,只用React.memo就足够解决问题啦。
内容的提问来源于stack exchange,提问作者Yamada JP
相关产品推荐
相关产品推荐

