useMemo是用来缓存计算属性的。
计算属性是函数的返回值,或者说那些以返回一个值为目标的函数。
有些函数会需要我们手动去点击,有些函数是直接在渲染的时候就执行,在DOM区域被当作属性值一样去使用。后者被称为计算属性。
e.g.
const Component = () => {
const [params1, setParams1] = useState(0);
const [params2, setParams2] = useState(0);
//这种是需要我们手动去调用的函数
const handleFun1 = () => {
console.log("我需要手动调用,你不点击我不执行");
setParams1((val) => val + 1);
};
//这种被称为计算属性,不需要手动调用,在渲染阶段就会执行的。
const computedFun2 = () => {
console.log("我又执行计算了");
return params2;
};
return (
<div onClick={handleFun1}>
//每次重新渲染的时候我就会执行 computed:{computedFun2()}
</div>
);
};
上面的代码中,在每次点击div的时候,因为setParams1的缘故,导致params1改变,整个组件都需要重新渲染,也导致comptedFunc2()也需要重新计算。如果computedFunc2的计算量很大,这时候重新计算会比较浪费。
可以使用useMemo:
const Com = () => {
const [params1, setParams1] = useState(0);
const [params2, setParams2] = useState(0);
//这种是需要我们手动去调用的函数
const handleFun1 = () => {
console.log("我需要手动调用,你不点击我不执行");
setParams1((val) => val + 1);
};
//这种被称为计算属性,不需要手动调用,在渲染阶段就会执行的。
const computedFun2 = useMemo(() => {
console.log("我又执行计算了");
return params2;
}, [params2]);
return (
<div onClick={handleFun1}>
//现在,我被useMemo保护,只有在组件初始化和params2改变的时候会执行
computed:{computedFun2}
</div>
);
};
此后computedFunc2只有在初始化和params2改变的时候会执行
useMemo用的越多越好?
并不是的。因为缓存需要成本。所有被useMemo保护的函数都会被加入useMemo的工作队列。
在组件使用了useMemo之后,为了校验该组件内被保护的这个计算属性是否需要重新计算,会先去useMemo的工作队列中找到这个函数,然后去校验这个函数的依赖项是否被更改。其中寻找到这个计算属性和进行校验都需要时间成本。
所以并不适合大量的使用useMemo。
所以,useMemo不能滥用。那么什么情况才不算是滥用呢?
1) 需要大量计算的时候
比如说有一百万个循环,每次其中进行一些计算操作
2)子组件依赖副组件的某一个依赖计算属性并且子组件使用了React.memo进行优化了的时候。
e.g.,
import {useMemo,memo} from 'react';
/**父组件**/
const Parent = () => {
const [parentState,setParentState] = useState(0); //父组件的state
//需要传入子组件的函数
const toChildComputed = () => {
console.log("需要传入子组件的计算属性");
return 1000;
}
return (<div>
<Button onClick={() => setParentState(val => val+1)}>
点击我改变父组件中与Child组件无关的state
</Button>
//将父组件的函数传入子组件
<Child computedParams={toChildComputed()}></Child>
<div>)
}
/**被memo保护的子组件**/
const Child = memo(() => {
consolo.log("我被打印了就说明子组件重新构建了")
return <div><div>
})
React.memo检测props中的数据的栈的地址是否改变。所以上面的代码改变了parentState的值,导致组件重新渲染,计算属性也重新计算,所以返回了新的值,栈的地址改变。
所以,尽管memo保护着子组件,子组件也还是重新渲染了。
所以,使用useMemo缓存那个计算属性,在子组件里面便不会检测到栈的变化,也就不会重新渲染了。
useCallback类似,不过是用来保存函数实例的。
Comments
Post a Comment