React的重新渲染问题

背景:避免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也没有发生改变,子组件自然不会重新渲染了。

另外两种防止组件重新渲染的方法是基于类组件的方法,这里便不再举例说明了,感兴趣可了解~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值