react现在函数式组件大行其道,但少有人将优化函数式组件的memo讲得简单明了。下文将通过案例来讲解memo。
案例
先看代码:
import React, { memo } from "react";
const Demo = () => {
let [food1, setFood1] = React.useState(20);
let [food2, setFood2] = React.useState(20);
const reduce1 = () => {
setFood1(food1 > 0 ? food1 - 1 : 0);
};
const reduce2 = () => {
setFood2(food2 > 0 ? food2 - 1 : 0);
};
console.log("父组件被渲染了");
return (
<>
<h3>肉包还有{food1}个</h3>,<button onClick={reduce1}>肉包卖了一个</button>
<br />
<h3>菜包还有{food2}个</h3>,<button onClick={reduce2}>菜包卖了一个</button>
<br />
<br />
<Food1 count={food1} />
<Food2 count={food2} />
</>
);
};
export default Demo;
const Food1 = memo(
(props) => {
console.log("子组件food1被渲染了");
return <h4>肉包只有{props.count}个了!</h4>;
},
(preProps, nextProps) => preProps.count === nextProps.count
);
const Food2 = (props) => {
console.log("子组件food2被渲染了");
return <h4>菜包只有{props.count}个了!</h4>;
};
讲解
上面的代码创建了1个父组件和2个子组件,其中子组件Food1使用了memo函数,子组件Food2未使用memo函数。
接下来通过sudo yarn start
来启动项目,打开控制台,认真观察日志。
日志详解
首次打开页面
首次打开页面时日志显示如下内容,说明所有组件首次挂载到页面全部会渲染。
父组件被渲染了
子组件food1被渲染了
子组件food2被渲染了
点击按钮一
点击“肉包卖了一个”按钮后,可以看到父组件和2个子组件都渲染了。按理来说父组件和子组件food1的数据有变动,这2个组件渲染是合理的,但子组件food2也被渲染,这是不合理的,可以进行优化,避免无谓的页面渲染。
父组件被渲染了
子组件food1被渲染了
子组件food2被渲染了
点击按钮二
请注意看认真,点击“菜包卖了一个”按钮按钮后,可以看到父组件和food2子组件被渲染了,子组件food1未被渲染!!!
父组件被渲染了
子组件food2被渲染了
对比代码
请认真对比food1和food2子组件的代码差异:
food1的代码
const Food1 = memo(
(props) => {
console.log("子组件food1被渲染了");
return <h4>肉包只有{props.count}个了!</h4>;
},
(preProps, nextProps) => preProps.count === nextProps.count
);
food2的代码
const Food2 = (props) => {
console.log("子组件food2被渲染了");
return <h4>菜包只有{props.count}个了!</h4>;
};
讲解
Food1子组件可以做到props不变时页面不重复渲染的秘诀在于该子组件被高阶函数memo包裹。
memo是一个高阶函数,它有两个参数。
- 参数一是必须是函数组件;
- 参数二是可选参数:
- 不传参时,memo会对函数组件中的state、props做浅比较,有变化才调用函数组件(参数一)进行渲染,没变化则不调用函数组件(参数一)进行渲染。
- 若传参时,该参数必须是返回true或false的函数。当返回值为true时,memo则不会调用参数一,即组件不会重新渲染;当返回值为false时,memo会调用参数一,即组件会重新渲染。
结果
希望各位读者参照组件Food1对组件Food2进行优化,最终达到点击按钮“肉包卖了一个”时组件Food2不渲染、点击按钮“菜包卖了一个”时组件Food1不渲染。希望要理解memo的读者一定要亲手尝试敲代码!!!敲过了才能真正理解!!
Food子组件优化版
const Food2 = memo(
(props) => {
console.log("子组件food2被渲染了");
return <h4>菜包只有{props.count}个了!</h4>;
}, // 参数二缺省,按默认情况做浅比较
);