memo 和 useMemo 的作用

**

**要想学习 useMemo 必须要先知道 React.memo

这两者都有一定的优化作用

memo 的作用

当数据变化时,代码会重新执行一遍,但是子组件数据没有变化也会执行,这个时候可以使用memo将子组件封装起来,让子组件的数据只在发生改变时才会执行

案例

点击按钮改变 n 的值,m 不变,验证程序会不会执行 m 的代码?

不使用 memo 的情况

只改变 n 的值时,虽然说 m 的值没变,但是也执行了 Child 的打印语句

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
function App() {
const [n, setN] = useState(0);
const [m, setM] = useState(0);
const add = () => {
setN((i) => i + 1);
};
const addChild = () => {
setM((i) => i + 1);
};
return (
<div>
<div>
n:{n}
<button onClick={add}>n+1</button>
<button onClick={addChild}>m+1</button>
</div>
<Child data={m} />
</div>
);
}
function Child(props) {
console.log("child执行了");
return <div>child:{props.data}</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));

使用 memo 进行封装

将 Child 用 memo 封装一下,就可以使 m 不变就不执行 Child,这个时候只要 m 的值不变,就不会执行 Child 组件

使用 React.memo 封装,会返回一个新的组件,调用新组件

1
2
3
4
5
6
7
function Child(props) {
console.log("child执行了");
return <div>child:{props.data}</div>;
}
// 封装child
const Child2 = React.memo(Child);
ReactDOM.render(<App />, document.getElementById("root"));

但是此时还有一个 bug,如果在子组件 Child 上添加一个监听函数,无论修改 m 的值与否,都会执行 Child 组件

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
function App() {
const [n, setN] = useState(0);
const [m, setM] = useState(0);
const add = () => {
setN((i) => i + 1);
};
const addChild = () => {
setM((i) => i + 1);
};
const onClickChild = () => {};

return (
<div>
<div>
n:{n}
<button onClick={add}>n+1</button>
<button onClick={addChild}>m+1</button>
</div>
+ <Child2 data={m} onClick={onClickChild} />
</div>
);
}
function Child(props) {
console.log("child执行了");
console.log("这里有很多代码");
return <div onClick={props.onClick}>child:{props.data}</div>;
}
const Child2 = React.memo(Child);

当点击 n 时,就会重新执行 App 代码,onClickChildn空函数的地址会发生改变,onClick 的函数变了,也就是 props 更新了,所以说此时还是会执行 Child 的

而 useMemo 正是解决这个问题的

useMemo 的作用

解决因函数更新而渲染自己的问题,就可以使用 useMemo,使用它将函数重新封装

const onClickChild=useMemo(fn,array)监听变量,第一个参数是函数,第二个参数是依赖,只有依赖变化时才会重新计算函数

1
2
3
4
5
6
7
8
9
10
11
12
import React, { useMemo, useState } from "react";
import ReactDOM from "react-dom";

//其他的代码不变,只需要重写m的点击函数

//使用useMemo重新写监听函数,当m变化时才会执行此代码
const onClickChild = useMemo(() => {
// 一般直接 return 一个 reactNode
return () => {
console.log(m);
};
}, [m]);

useCallback

也可以把 useMemo 替换成 useCallback,使用 useCallback 就不用写 return 函数了

1
2
3
4
5
6
7
8
9
10
const onClickChild = useMemo(() => {
return () => {
console.log(m);
};
}, [m]);

//等价于
const onClickChild = useCallback(() => {
console.log(m);
}, [m]);

注意:

  • 如果你的 value 是个函数,那么你就要写成useMemo(()=>(x)=> console.log(x))
  • 这是一个返回函数的函数,比较复杂;于是就有了useCallback,你可以使用useCallback

总结

useCallback(组件的方法, [依赖] )、useMemo(组件的属性, [属性] )需要和 memo(组件) 搭配使用才可以避免组件的渲染;单单使用这两个 hook 不能减少组件的渲染的,但是可以减少一些昂贵的计算。

memo + useMemo + useCallback:memo 包裹的组件只有在 props 变的时候才会重新渲染,useMemo、useCallback 可以防止 props 不必要的变化。在组件中**memo + useCallback、useMemo 是搭配着来的,少了任何一方,都会使优化失效。**不过当用来缓存计算结果等场景的时候,也可以单独用 useMemo、useCallback。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!