Memo
使用场景:一个App组件里有m和n数据。还有一个Child子组件,它接受一个m作为它的外部数据。当点击把n+1的按钮时,我们知道,App会再次执行,那么Child子组件也会再次执行吗?
function App() {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
const onClick = () => {
setN(n + 1);
};
return (
<div className="App">
<div>
<button onClick={onClick}>update n {n}</button>
</div>
<Child data={m}/>
</div>
);
}
function Child(props) {
console.log("child 执行了");
console.log('假设这里有大量代码')
return <div>child: {props.data}</div>;
}
复制代码
通过在Child函数组件里打log,我们发现,改变n,Child也会执行一次。可是明明Child这个组件只依赖m,n变化它为什么要跟着执行呢?怎么解决这个问题?
就可以使用 React.memo
封装Child
const Child2 = React.memo(Child);
复制代码
把Child放在 React.memo
里,得到一个Child2,Child2是Child优化之后的函数。在App里渲染这个Child2
结果:改变n,Child不执行了,只有改变它依赖的外部数据m时,才会执行。
使代码更简洁:直接把匿名函数组件放在memo里作为参数,得到Child组件。
const Child = React.memo((props)=>{
console.log("child 执行了");
console.log('假设这里有大量代码')
return <div>child: {props.data}</div>;
});
复制代码
结论
React默认会有多余的render,当改变组件里的数据时,由于组件本身会再次执行,就导致这个组件的子组件也会再次执行。
React.memo
使得一个组件只有在它依赖的props变化时,才会执行。
memo有一个bug
如果子组件接受了一个函数props,这个函数在父组件里定义,给子组件传进去。当我改变父组件里的n时,App()肯定会再次执行。就会导致定义在里边的这个函数也执行了。
function App() {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
const onClick = () => {
setN(n + 1);
};
const onClickChild=()=>{} // 再次执行时,虽然函数内容一样,但是地址不同了
return (
<div className="App">
<div>
<button onClick={onClick}>update n {n}</button>
</div>
<Child data={m} onClickChild={onClickChild}/>
</div>
);
}
const Child = React.memo((props)=>{
console.log("child 执行了");
return <div onClick={props.onClickChild}>child: {props.data}</div>;
});
复制代码
结果:点击按钮,改变n。Child明明不依赖n,但是Child也执行了。
就是因为App再次执行时,onClickChild
这个函数也被重新定义了,虽然内容不会变,但是地址不同了,React就认为这个函数变了。Child接受的这个props变了,自然就会再次执行。
怎么办呢?能不能让这个函数在某种情况下,不要重新生成,继续使用上一次的缓存结果
用useMemo,实现函数重用
const onClickChild = useMemo(() => {
return () => {};
}, [m]);
复制代码
把这个函数经过 useMemo
优化。useMemo
接受两个参数,第一个参数是一个返回函数的函数,返回的就是该函数本来的逻辑。第二个参数是依赖,表示只有当m变化时,才重新生成函数。如果依赖的m没变,就复用上一次的缓存,不生成新的函数。
点击n+1的按钮后,发现Child组件没有执行了。因为我们使用 useMemo
规定了,只有当m变化时,才重新生成props函数。m没变,即使App再次执行,这个函数也不会生成一个新的。
useMemo
经常用来缓存一些在两次新旧组件迭代的时候,希望使用上一次的值。
有时要缓存的,也就是return 的不一定是函数,也可以是对象
useMemo的第一个参数:()=> value
,没有形参,返回一个value。第二个参数:依赖 [m,n]
。只有当依赖变化时,才会计算新的value。依赖没变,就重用之前的value
useMemo的特点:
- 第一个参数是()=>value
- 第二个参数是依赖
[m,n]
- 只有当依赖变化时,才会重新计算出新的value
- 如果依赖不变,那么就重用之前的value
- 类似Vue2的computed
注意:如果你的value是一个函数,那么就要写成
useMemo(()=>(x)=>console.log(x))
复制代码
这是一个返回函数的函数
是不是觉得很难用,那么 useCallback
来了
useCallback的用法(useMemo的语法糖)
useCallback(x=>console.log(x),[m])
复制代码
等价于
useMemo(()=>(x)=>console.log(x),[m])
复制代码