UseCallback就能避免子组件渲染?Naive!

250 阅读1分钟

React做优化的一个点就是尽量避免子组件的重新渲染,现在看下面例子

例1

// parent.jsx
import React, { useCallback, useState } from "react";

export default () => {
const [cum, setCum] = useState(0);

const handler = useCallback(() => {
     setCum(cum + 1);
}, [cum]);

console.log("render-parent");
return (
    <>
        <h1 onClick={handler}>
            parent {cum}!
        </h1>
        <Child cb={handler}/>
    </>);

};
// child.jsx
import React, { useCallback, useState } from "react";

export default ({ cb }) => {
    console.log("render-child");
    return (
        <h1 >
            child!
        </h1>
    );
};

这里每点击一次parent,输出都是成对出现

image.png

例2

如果我们稍微修改下parent的代码看看预期输出是什么

// parent.jsx
import React, { useCallback, useState } from "react";

export default () => {
const [cum, setCum] = useState(0);

const handler = useCallback(() => {
     setCum(cum + 1);
}, [cum]);

const [cum2, setCum2] = useState(0);

const handler2 = useCallback(() => {
     setCum(cum2 + 1);
}, [cum2]);

console.log("render-parent");
return (
    <>
        <h1 onClick={handler}>
            parent {cum}!
        </h1>
        <Child cb={handler2}/>
    </>);

};

按理每次点击handler2的引用没有改变才对,但实际仍然输出成对出现。

例3

使用 React.memo 包裹子组件

// parent.jsx
import React, { useCallback, useState } from "react";

export default () => {
const [cum, setCum] = useState(0);

const handler = useCallback(() => {
     setCum(cum + 1);
}, [cum]);

const [cum2, setCum2] = useState(0);

const handler2 = useCallback(() => {
     setCum(cum2 + 1);
}, [cum2]);

console.log("render-parent");
return (
    <>
        <h1 onClick={handler}>
            parent {cum}!
        </h1>
        <h1 onClick={handler2}>
            parent2 {cum2}!
        </h1>
        <Child cb={handler2}/>
    </>);

};
// child.jsx
import React, { useCallback, useState } from "react";

function Child({ cb }) {
    console.log("render-child");
    return (
        <h1 >
            child!
        </h1>
    );
};
export default React.memo(Child)

结果:点击parent 5次, 点击parent2 两次,输出如下:

image.png

可以看到当点击parent时,子组件没有重新渲染了。

所以答案揭晓,功劳有他的一半↓

React.memo

功能

为高阶组件,在传给组件的 props 的属性和值没有发生改变的情况下,它会使用最近一次缓存的结果,而不会进行重新的渲染,实现跳过组件渲染的效果

比较算法

props浅比较,有时候我们可以给React.memo传第二个参数用于自定义比较算法:

const isEqual(prevProps, nextProps) {
    // 自定义比较
}

export default React.memo(child, isEqual)