背景:避免React 组件非必要的重新渲染是写React应用最重要的问题之一,以下会列出几种常见的避免React 组件非必要的重新渲染的方法。
1. memo
2. useCallback
3. PureComponent
4. 类组件生命周期 shouldComponentUpdate
以下会对前两种方法写代码示例:
1.memo
父组件
"use client";
import { useState } from "react";
import Child from "./Child";
export default function Home() {
const [num, setNum] = useState(0);
const [name, setName] = useState("Lucy");
return (
<>
<button
onClick={() => {
setNum(e => e + 1);
}}
>
click
</button>
<br />
<button
onClick={() => {
setName(e => e + "i");
}}
>
click2 {name}
</button>
<br />
{num}
<Child num={num} />
</>
);
}
子组件
"use client";
import { memo, useState } from "react";
interface props {
num: number;
}
export default memo(function Child({ num }: props) {
const [count, setCount] = useState(0);
console.log(count, setCount);
return (
<>
Im child component #{count} {num}
</>
);
});
结果发现只有点击 click 的时候改变num的值的时候,也就是memo的依赖的时候,子组件才会重新渲染,当点击click2的时候,并没有改变memo的依赖的时候,子组件并没重新渲染。
2.useCallback
在例子1上增加部分代码: 父组件 增加一个changeName函数 并传递给子组件
"use client";
import { useState } from "react";
import Child from "./Child";
export default function Home() {
const [num, setNum] = useState(0);
const [name, setName] = useState("Lucy");
function changeName() {
setName(e => e + "k");
}
return (
<>
<button
onClick={() => {
setNum(e => e + 1);
}}
>
click
</button>
<br />
<button
onClick={() => {
setName(e => e + "i");
}}
>
click2 {name}
</button>
<br />
{num}
<Child num={num} changeName={changeName} />
</>
);
}
子组件接受props
"use client";
import { memo, useState } from "react";
interface props {
num: number;
changeName: () => void;
}
export default memo(function Child({ num, changeName }: props) {
const [count, setCount] = useState(0);
console.log(count, setCount);
return (
<>
<button onClick={changeName}>click to changeName</button>
Im child component #{count} {num}
</>
);
});
我们点击父组件的click2 我们惊奇的发现 子组件重新渲染了,原来我们多传了一个changeName函数导致的,memo对changeName函数也缓存了,所以当父组件点击button,父组件重新渲染,重新创建changeName函数,changeName函数的引用发生改变,memo就会导致子组件重新渲染!
这并不是我们想要的结果。
那怎么做呢,没错,运用到了useCallback。
我们尝试使用useCallback缓存起来这个函数,并且不写依赖项(不因依赖改变重新创建)
"use client";
import { useCallback, useState } from "react";
import Child from "./Child";
export default function Home() {
const [num, setNum] = useState(0);
const [name, setName] = useState("Lucy");
// function changeName() {
// setName(e => e + "k");
// }
const changeName = useCallback(function () {
setName(e => e + "k");
}, []);
return ...
我们使用useCallback把函数缓存起来,这样点击 click2 ,在父组件重新渲染的时候,我们就不会导致changeName重新创建新的引用,memo的依赖项changeName没有发生改变,另一个依赖项num也没有发生改变,子组件自然不会重新渲染了。
另外两种防止组件重新渲染的方法是基于类组件的方法,这里便不再举例说明了,感兴趣可了解~