**
**要想学习 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 封装,会返回一个新的组件,调用新组件
| function Child(props) { console.log("child执行了"); return <div>child:{props.data}</div>; }
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)监听变量,第一个参数是函数,第二个参数是依赖,只有依赖变化时才会重新计算函数
| import React, { useMemo, useState } from "react"; import ReactDOM from "react-dom";
const onClickChild = useMemo(() => { return () => { console.log(m); }; }, [m]);
|
useCallback
也可以把 useMemo 替换成 useCallback,使用 useCallback 就不用写 return 函数了
| 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。