hooks缓存
react渲染页面是通过render把虚拟结点渲染到页面,通过对比fiber树,re-render重新渲染页面,为了更好的性能,我们一般提倡尽可能地减少react重新渲染页面的次数,这时候,usememo()和useCallback()就应运而生。
useMemo()
const cachedValue= useMemo(calculateValue,dependencies)
可以在react每次重新渲染的时候缓存计算的结果,其中:
- calculateValue: 缓存计算值的函数,且要为没有任何参数的纯函数,可以返回任意类型。react只会在首次渲染的时候调用该函数,之后如果dependencies没有发生变化时,react就直接返回相同的数值,避免了重复调用该函数;如果dependencies变化,才再调用该函数。
- dependencies: 依赖项,为calculateValue中所使用到的响应式变量组成的数组,包括props\state和所有直接在组件中定义的函数和变量。React 使用
Object.is将每个依赖项与其之前的值进行比较。
Object.is()
Object.is() 与 == 运算符并不等价。== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 "" == false 的结果是 true,但是 Object.is() 不会对其操作数进行类型转换。
Object.is() 也不等价于 === 运算符。Object.is() 和 === 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。
注意:
和其他hooks一样,只能在组件的顶层和自定义hooks中使用,不能在循环或条件语句中调用。
用法
-
缓存组件,避免反复重新加载整个组件
function Todolist({todos,tab}){ const visibleTodos=filterTodos(todos,tab) }上面的组件需要利用filterTodos的方法过滤得到有效的todos,但由于利用到了props和state,所以每次更新都会重新运行filterTodos函数,因此,我们可以使用usememo进行改善:
function Todolist({todos,tab}){ const visibleTodos=useMemo(()=>filterTodos(todos,tab),[todos,tab]) } -
跳过组件的重新渲染
在默认情况下,当一个组件重新渲染时,react会递归地重新渲染它的所有子组件。依旧使用上述的例子,假设将visibleTodos传递给todoList的子组件:
function TodoList({todos,tab}){ const visibleTodos=filterTodos(todos,tab) //依然是建议使用useMemo() const visibleTodos=useMemo(()=>filterTodos(todos,tab),[todos,tab]) return( <div> <List items={visibleTodos} /> </div> ) }注意:对于一个直接依赖于在组件中创建的对象,需要对这个对象本身进行memo包裹
例如:这里的NotedTodos是在组件中创建的对象,当组件重新渲染时,组件中所有的代码都会再次运行,所以创建NotedTodos的代码也会在每次重新渲染时运行,而每次重新渲染的NotedTodos不一样,所以这里即便使用了useMemo, react也会重新渲染。
function TodoList({todos,tab,ischecked}){ const NotedTodos={noted:ischecked} const visibleTodos=useMemo(()=>{ return filterTodos(todos,tab,NotedTodos) },[todos,tab,NotedTodos]) }解决方式,使用memo包裹NotedTodos及使用了其作为依赖项的变量:
function TodoList({todos,tab,ischecked}){ const visibleTodos = useMemo(()=>{ const NotedTodos={noted:ischecked} return filterTodos(todos,tab,NotedTodos) },[todos,tab,ischecked])
useCallback( )
简单来说,可以看作是useMemo的一个语法糖,适用于缓存函数:
const visibleTodos=useMemo(()=>{
return ()=>{
todos.find(item=>item===tabs)
}
},[todos,tab])
}
//相当于
const visibleTodos=useCallback(()=>{
todos.find(item=>item===tabs)
} ,[todos,tab])
}
// 在 React 内部的简化实现
function useCallback(fn, dependencies) {
return useMemo(() => fn, dependencies);
}
-
二者区别:
useMemo缓存函数调用的结果。在这里,它缓存了调用computeRequirements(product)的结果。除非product发生改变,否则它将不会发生变化。这让你向下传递requirements时而无需不必要地重新渲染ShippingForm。必要时,React 将会调用传入的函数重新计算结果。(调用)useCallback缓存函数本身。不像useMemo,它不会调用你传入的函数。相反,它缓存此函数。从而除非productId或referrer发生改变,handleSubmit自己将不会发生改变。这让你向下传递handleSubmit函数而无需不必要地重新渲染ShippingForm。直至用户提交表单,你的代码都将不会运行。(创建)
使用场景
- usememo返回函数
- 优化自定义hooks