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,输出都是成对出现
例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 两次,输出如下:
可以看到当点击parent时,子组件没有重新渲染了。
所以答案揭晓,功劳有他的一半↓
React.memo
功能
为高阶组件,在传给组件的 props 的属性和值没有发生改变的情况下,它会使用最近一次缓存的结果,而不会进行重新的渲染,实现跳过组件渲染的效果
比较算法
props浅比较,有时候我们可以给React.memo传第二个参数用于自定义比较算法:
const isEqual(prevProps, nextProps) {
// 自定义比较
}
export default React.memo(child, isEqual)