React 中的 Memoization在 React 应用的上下文中,Memoization 是一种手段,每当父组件重新渲染时,子组件仅在它所依赖的 props 发生变化时才会重新渲染 。 如果子组件所依赖的 props 中没有更改,则它不会执行 render 方法,并将返回缓存的结果 。 由于渲染方法未执行,因此不会有虚拟 DOM 创建和差异检查,从而实现性能的提升 。
现在,让我们看看如何在类和函数组件中实现 Memoization,以避免这种不必要的重新渲染 。
类组件实现 Memoization为了在类组件中实现 Memoization,我们将使用 React.PureComponent 。 React.PureComponent
实现了 shouldComponentUpdate(),它对 state
和 props
进行了浅比较,并且仅在 props 或 state 发生更改时才重新渲染 React 组件 。
将子组件更改为如下所示的代码:
//Child.jsclass Child extends React.PureComponent { // 这里我们把 React.Component 改成了 React.PureComponent render() { console.log("Child render"); return ( <div> <h2>{this.props.name}</h2> </div> ); }}export default Child;此示例的完整代码显示在这个 sandbox 中 。
父组件保持不变 。 现在,当我们在父组件中增加 count
时,控制台中的输出如下所示:
Parent renderChild renderParent renderParent render对于首次渲染,它同时调用父组件和子组件的 render
方法 。
对于每次增加 count
后的重新渲染,仅调用父组件的 render
函数 。 子组件不会重新渲染 。
函数组件实现 Memoization为了在函数组件中实现 Memoization,我们将使用 React.memo() 。 React.memo()
是一个高阶组件(HOC),它执行与 PureComponent
类似的工作,来避免不必要的重新渲染 。
以下是函数组件的代码:
//Child.jsexport function Child(props) { console.log("Child render"); return ( <div> <h2>{props.name}</h2> </div> );}export default React.memo(Child); // 这里我们给子组件添加 HOC 实现 Memoization同时还将父组件转换为了函数组件,如下所示:
【聊聊怎么利用Memoization提高React性能】//Parent.jsexport default function Parent() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; console.log("Parent render"); return ( <div> <button onClick={handleClick}>Increment</button> <h2>{count}</h2> <Child name={"joe"} /> </div> );}此示例的完整代码可以在这个 sandbox 中看到 。
现在,当我们递增父组件中的 count
时,以下内容将输出到控制台:
Parent renderChild renderParent renderParent renderParent renderReact.memo() 存在的问题在上面的示例中,我们看到,当我们对子组件使用 React.memo()
HOC 时,子组件没有重新渲染,即使父组件重新渲染了 。
但是,需要注意的一个小问题是,如果我们将函数作为参数传递给子组件,即使在使用 React.memo()
之后,子组件也会重新渲染 。 让我们看一个这样的例子 。
我们将更改父组件,如下所示 。 在这里,我们添加了一个处理函数,并作为参数传递给子组件:
//Parent.jsexport default function Parent() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; const handler = () => { console.log("handler"); // 这里的 handler 函数将会被传递给子组件 }; console.log("Parent render"); return ( <div className="App"> <button onClick={handleClick}>Increment</button> <h2>{count}</h2> <Child name={"joe"} childFunc={handler} /> </div> );}子组件代码将保持原样 。 我们不会在子组件中使用父组件传递来的函数:
//Child.jsexport function Child(props) { console.log("Child render"); return ( <div> <h2>{props.name}</h2> </div> );}export default React.memo(Child);现在,当我们递增父组件中的 count
时,它会重新渲染并同时重新渲染子组件,即使传递的参数中没有更改 。
那么,是什么原因导致子组件重新渲染的呢?答案是,每次父组件重新渲染时,都会创建一个新的 handler
函数并将其传递给子组件 。 现在,由于每次重新渲染时都会重新创建 handle
函数,因此子组件在对 props 进行浅比较时会发现 handler
引用已更改,并重新渲染子组件 。
接下来,我们将介绍如何解决此问题 。
通过 useCallback()
来避免更多的重复渲染导致子组件重新渲染的主要问题是重新创建了
推荐阅读
- Angular怎么结合Git Commit进行版本处理
- Win11怎么设置关机键?Win11快捷键关机的设置方法
- Win11提示打印机错误0x00000040指定的网络名不再可用怎么办?
- 抠图怎么抠更干净?一键抠图快速教搞定
- Win10开机自动断电重启怎么回事?
- Win10临时文件夹无写入权限,不能安装怎么办?
- 旧年代老照片怎么修复?一键完成老照片人脸修复的方法
- Win10定时关机命令不起作用怎么办?
- 孩子不爱吃饭,能做什么?
- 孩子便便干结,又挑食怎么办