useMemo[性能优化]

作用

  • 缓存计算的值
  • 解决React.memo中当依赖的数组地址发生改变时所带来的重新渲染问题

性能优化的手段

  • 减少在一次渲染中所做的工作
  • 减少重新渲染的次数

参数

  • 参数一:需要被执行的任务,被包裹在一个函数里
  • 参数二:依赖列表

与React.memo的区别

​ React.memo主要用于缓存整个函数组件,使它具备PureComponent的效果。但是在某些情况下,React.memo缓存的组件还是会重新渲染,例如引用的情况下。

实例1:进行大计算量函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import React from 'react';
import format from 'date-fns/format';
function App(){
const [selectedNum, setSelectedNum] = React.useState(100);
const time = useTime();
const allPrimes = [];
for(let counter=2;counter<selectedNum;counter++)
if(isPrime(counter))
allPrimes.push(counter);
return (
<>
<p className="clock">
{format(time, 'hh:mm:ss a')}
</p>
<form>
<label htmlFor="num">Your number:</label>
<input
type="number"
value={selectedNum}
onChange={(event)=>{
let num=Math.min(100000, Number(event.target.value));
setSelectedNum(num);
}}/>
</form>
<p>
There are {allPrimes.length} prime between 1 and {selectedNum}:
<span className="prime-list">
{allPrimes.join(', ')}
</span>
</p>
</>
)
}
function useTime(){
const [time, setTime] = React.useState(new Date());
React.useEffect(()=>{
const intervalId = window.setInterval(()=>{
setTime(new Date());
}, 1000);
return ()=>{
window.clearInterval(intervalId);
}
}, []);
return time;
}
function isPrime(n){
const max = Math.ceil(Math.sqrt(n));
if(n===2)
return true;
for(let counter=2;counter<=max;counter++)
if(n%counter===0)
return false;
return true;
}
export default App;

​ 上述场景中存在如下问题,当界面中的时间发生改变时,就需要重新计算下面所有的质数,即使输入的数值不变。

存在问题

​ 使用useMemo进行改造:

1
2
3
4
5
6
7
8
const allPrimes = React.useMemo(()=>{
const result = [];
for(let i=2;i<selectedNum;i++)
if(isPrime(i))
result.push(i);
return result;
}, [selectedNum]);
// 其实useMemo跟纯函数有点像,只要输入不变输出就不变,既然输出不变就无需重新渲染

实例2:保存的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import React from 'react';
import Boxes from './Boxes';
function App(){
const [name, setName] = React.useState('');
const [boxWidth, setBoxWidth] = React.useState(1);
const boxes = [
{flex: boxWidth, background:'hsl(345deg 100% 505)'},
{flex:3,background:'hsl(260deg 100% 40%)'},
{flex:1,background:'hsl(50deg 100% 60%)'}
];
return(
<>
<Boxes boxes={boxes} />
<section>
<label htmlFor={`${id}-name`}>
Name:
</label>
<input
id={`${id}-name`}
type="text"
value={name}
onChange={(event) => {
setName(event.target.value);
}}
/>
<label htmlFor={`${id}-box-width`}>
First box width:
</label>
<input
id={`${id}-box-width`}
type="range"
min={1}
max={5}
step={0.01}
value={boxWidth}
onChange={(event) => {
setBoxWidth(Number(event.target.value));
}}
/>
</section>
</>
)
}

useCallback

与useMemo的区别

基本相同,useCallback是useMemo的语法糖。useMemo的功能比useCallback更齐全,useCallback主要用于缓存函数的。

示例

1
2
3
4
5
const handleMegaBoost = React.useMemo(()=>{
return function(){
setCount((currentValue)=>currentValue+1234);
}
},[])

useMemo和useCallback的原理

​ 他们内部是使用纯函数实现的。因为对于给定输入需要得出给定的输出,在输出保持不变的情况下就无需重新渲染。

useContext

作用

​ 父组件传值给后代组件

性能问题

​ 父组件每次执行都会引发子组件的渲染