Skip to main content

useMemo的使用场景

 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

Popular posts from this blog

Valgrind

  Using Valgrind to check memory How to use Valgrind -To valgrind the program, run the valgrind command and give it our program name as an argument. -For example, if we want to run ./myProgram hello 42, we can simply run Valgrind ./myProgram hello 42.  Uninitialized Values -When we run the program, we may use uninitialized values. It needs to be fixed. Valgrind can tell us about the use of uninitialized values. But it only tell when the control flow of the program depends on the unitialized value. For example, uninitialized value appears in the conditional expression of an if, or a loop, or in the switch statement. -If we want to know where the uninitialized value is from, we can use Valgrind    --track-origins=yes ./myProgram -Using -fsanitize=address can find a lot of problems that Memcheck cannot.  -We can use Valgrind with GDB to debug. We can run: --vgdb=full --vgdb-error=0., then Valgrind will stop on the first error that it encounters and give control to ...

Error Handling and Exceptions

  Error Handling and Exceptions - The worst possible way to deal with any problem is for the program to produce the wrong answer without informing the user - a silent failure.  - When the program deals with an error by aborting, it should do so with the explicit intention of the programmer. (i.e. the programmer should call assert or check a condition then call abort) And the program should give an error message to the user. Simply segmentation fault due to an invalid memory access is never appropriate. - Sometimes we would prefer to handle the error more gracefully. Graceful error handling requires making the user aware of the problem, as well as allowing the program to continue to function normally. - For example, we would present the user with some options to remedy the situation, like entering a different input, select a different file, retry a failed operation, etc.  - As our programming skills develop, we should get into the practice of writing bullet proof code - co...

ADTs

  ADTs 1. Queue -FIFO We can implement in this way:  template<typename T> class Queue { public:     void enqueue(const T & item);     T dequeue();     T & peek()     const T & peek() const;     int numbebrOfItem() const; }; - In the actual C++ STL library, it has the following functions: push() pop() peek() 2. Stack - LIFO We can implement in this way: template<typename T> class Stack { public:     void push(const T & item);     T pop();     T & peek();     const T & peek() const;     int numberOfItem() const; }; In the C++ STL library, it has similar functions as queue. 3. Sets - Add - Test if an item is in the set - Check if the set is empty - Take the union of two set - intersect two sets A variant of this ADT is a multiset. (Also called a bag) It allows the same element to appear multiple times, whereas a true set either has an object or n...