useMemo Hook 概述
- useMemo 用于优化代码, 可以让对应的函数只有在依赖发生变化时才返回新的值
其实我们可以把 useMemo 看成是 useCallback 底层的实现,如下:
function useCallback(fn, arr) {
return useMemo(() => {
return fn;
}, arr);
}
使用 useMemo:
import React, {useState, memo, useMemo} from 'react';
function Home(props) {
console.log('Home被渲染了');
return (
<div>
<p>Home</p>
<button onClick={() => {
props.handler()
}}>增加Home
</button>
</div>
)
}
function About(props) {
console.log('About被渲染了');
return (
<div>
<p>About</p>
<button onClick={() => {
props.handler()
}}>减少About
</button>
</div>
)
}
const MemoHome = memo(Home);
const MemoAbout = memo(About);
export default function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const [countState, setCountState] = useState(0);
function increment() {
setNumState(numState + 1);
}
const decrement = useMemo(() => {
return () => {
setCountState(countState - 1);
};
}, [countState]);
return (
<div>
<p>numState = {numState}</p>
<p>countState = {countState}</p>
<MemoHome handler={increment}/>
<MemoAbout handler={decrement}/>
</div>
)
}
如上的代码当中 decrement
方法,只要 countState 没有发生变化, 那么 useMemo 返回的永远都是同一个值,那么永远都是同一个值,相关对应的组件也就会不会重新渲染,因为值没有发生改变,state 是不会进行重新渲染。
useCallback 和 useMemo 的区别
- useCallback 返回的永远是一个函数
- useMemo 返回的是 return 返回的内容
那么,博主先来改造一下如上的这个案例:
import React, {useState, memo} from 'react';
function Home(props) {
console.log('Home被渲染了');
return (
<div>
<p>Home</p>
<button onClick={() => {
props.handler()
}}>增加Home
</button>
</div>
)
}
function About(props) {
console.log('About被渲染了');
return (
<div>
<p>About</p>
<p>{props.user.name}</p>
<p>{props.user.age}</p>
</div>
)
}
const MemoHome = memo(Home);
const MemoAbout = memo(About);
export default function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const user = {name: 'BNTang', age: 18};
function increment() {
setNumState(numState + 1);
}
return (
<div>
<p>numState = {numState}</p>
<MemoHome handler={increment}/>
<MemoAbout user={user}/>
</div>
)
}
大致改造的内容就是说,删除了之前的 decrement
与 countState
然后定义了一个对象,把这个对象传递给了 About 然后在 About 当中进行显示,然后运行项目点击 增加Home
你会发现,所有组件又重新渲染了,那么这个原因在之前博主已经详细的介绍过了,因为点击了增加按钮之后父组件重新渲染了,然后对象又会重新的进行定义那么这回的这个对象肯定是与上次的是不一样的,那么组件就会被重新渲染了,那么这个问题就可以利用 useCallback 和 useMemo 的区别来解决, useMemo 有一个特点就是 返回的是 return 返回的内容
那么我们如果将对象通过这种方式进行返回,不依赖任何属性那么在点击增加按钮,这个对象就是还是同一个这回 About 就不会进行重新渲染了,代码如下:
import React, {useState, memo, useMemo} from 'react';
function Home(props) {
console.log('Home被渲染了');
return (
<div>
<p>Home</p>
<button onClick={() => {
props.handler()
}}>增加Home
</button>
</div>
)
}
function About(props) {
console.log('About被渲染了');
return (
<div>
<p>About</p>
<p>{props.user.name}</p>
<p>{props.user.age}</p>
</div>
)
}
const MemoHome = memo(Home);
const MemoAbout = memo(About);
export default function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
function increment() {
setNumState(numState + 1);
}
const user = useMemo(() => {
return {name: 'BNTang', age: 18}
}, []);
return (
<div>
<p>numState = {numState}</p>
<MemoHome handler={increment}/>
<MemoAbout user={user}/>
</div>
)
}
注意:[]
代表的是不依赖任何属性数据。
利用 useMemo 对实际代码优化
假如现在有这么一个场景,就是定义了一个函数,然后你调用这个函数可以拿到一个结果值,函数当中包含了大量的运算,然后, 你在根组件渲染的时候需要展示该函数渲染的值,代码如下:
import React, {useState} from 'react';
function calculate() {
console.log('calculate被执行了');
let total = 0;
for (let i = 0; i < 999; i++) {
total += i;
}
return total;
}
export default function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const total = calculate();
return (
<div>
<p>{total}</p>
<p>{numState}</p>
<button onClick={() => {
setNumState(numState + 1)
}}>增加
</button>
</div>
)
}
运行结果如下:
通过运行结果可以发现只要每增加一次计算函数就被重新运行了一次,这是非常耗性能的那么如果该函数以后的计算更大呢,是吧,博主这里只是单纯的模拟而已,那么这个问题就可以利用 useMemo 来进行优化掉了,那么它怎么计算最终得到的结果都是一样的,那么我们就不用依赖任何的属性即可优化之后的代码如下:
import React, {useState, useMemo} from 'react';
function calculate() {
console.log('calculate被执行了');
let total = 0;
for (let i = 0; i < 999; i++) {
total += i;
}
return total;
}
export default function App() {
console.log('App被渲染了');
const [numState, setNumState] = useState(0);
const total = useMemo(() => {
return calculate();
}, []);
return (
<div>
<p>{total}</p>
<p>{numState}</p>
<button onClick={() => {
setNumState(numState + 1)
}}>增加
</button>
</div>
)
}
再次查看运行结果: