useMemo
一、作用
useMemo
和 memo
作用相同,都是用来做性能优化的,不会影响业务逻辑。
memo
函数,针对的是一个组件
的渲染,是否重复执行。(
)
useMemo
,定义了一段函数
逻辑,是否重复执行。(() => {}
)
本质都是利用同样的算法,判断依赖
是否发生改变,进而决定是否触发特定逻辑。
二、使用语法
import React, { useMemo } from 'react'
useMemo(() => {}, [] )
这里有几个注意点:
-
useMemo
的第一个参数是函数
,第二个参数一般都为数组
。 - 如果不传第二个参数,与
useEffect
类似,意味着每一次都会执行第一个函数参数,那么使用useMemo
的意义就没有了。 - 如果第二个参数传的是空数组
[]
,与useEffect
类似,只执行一次。 -
useMemo
与useEffect
有不一样的一点就是调用时机 ——useEffect
执行的是副作用,所以一定是在渲染之后运行的;而useMemo
是需要有返回值的,返回值会参与渲染,所以useMemo
是是在渲染期间完成的。
三、具体demo代码
import React, { useState, useMemo } from 'react';
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
return (
count: {count}
double: {double}
)
}
export default App;
这里使用了count === 3
这个表达式的结果(布尔值,true/false),作为是否重新渲染Double组件的判断依据。
所以随着count
从0开始的每一次的加一,Double
组件最终也只会重新渲染两次:
- 当count值为3的时候,
count === 3
这个表达式的结果为true
,发生改变了。 - 当count值为4的时候,
count === 3
这个表达式的结果为false
,发生改变了。
结果演示:
三、useMemo中依赖的值,也可以是一个useMemo
但千万要注意,不要循环依赖!
import React, { useState, useMemo } from 'react';
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
const half = useMemo(() => {
return count / 2
}, [double])
return (
count: {count} double: { double } half: { half }
)
}
export default App;
四、优化子组件重渲染的场景
1. 先展示还没优化前的场景
可以观察到,每一次App
组件里的count
加1,Counter
子组件就会重新渲染一次(控制台输出)。
import React, { useState, useMemo } from 'react';
function Counter(props) {
console.log('Counter');
return (
Counter: {props.counter}
)
}
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
return (
count: {count}
)
}
export default App;
2. 使用memo来优化Counter子组件
可以观察到,经过这一步骤优化后,只有当App
组件里的count
值为3
和4
的时候,Counter
子组件才会重新渲染(控制台输出)。
import React, { useState, useMemo, memo } from 'react';
const Counter = memo(function Counter(props) {
console.log('Counter');
return (
Counter: {props.counter}
)
})
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
return (
count: {count}
)
}
export default App;
3.给Counter组件传递onClick属性
尽管Counter
子组件里的counter
值,只会在App
组件的count
值为3和4的时候才会更新。但是每一次count
值变化,Counter
组件都会重新渲染,这是意料之外的场景。说明每一次App
的变化,都会到处onClick
也被连带重新渲染了。
import React, { useState, useMemo, memo } from 'react';
const Counter = memo(function Counter(props) {
console.log('Counter');
return (
Counter: {props.counter}
)
})
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
const onClick = () => {
console.log('click')
}
return (
count: {count}
)
}
export default App;
4. 使用useMemo来让onClick不会每次都渲染
使用useMemo(() => { return onClick }, [])
来包裹onClick函数,让它不会每一次都重新渲染,就只渲染一次。
可以发现优化成功了,只有当App
组件里的count
值为3和4的时候,Counter
组件才有重新渲染。
import React, { useState, useMemo, memo } from 'react';
const Counter = memo(function Counter(props) {
console.log('Counter');
return (
Counter: {props.counter}
)
})
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
const onClick = useMemo(() => {
return () => {
console.log('click')
}
}, [])
return (
count: {count}
)
}
export default App;
5. 使用useCallback来省略顶层的useMemo函数
刚刚第4点的优化中,onClick的具体优化代码为:
const onClick = useMemo(() => {
return () => {
console.log('click')
}
}, [])
可以观察到这里有两层函数。
useCallback
useCallback
就是useMemo
可以省略一层函数的写法。
useMemo(() => fn, [])
就等于 useCallback(fn, [])
。
可以观察到这里有两层函数:
const onClick = useMemo(() => {
return () => {
console.log('click')
}
}, [])
使用useCallback
来简写:
const onClick = useCallback(() => {
console.log('click')
}, [])
完整代码:
import React, { useState, useMemo, memo, useCallback } from 'react';
const Counter = memo(function Counter(props) {
console.log('Counter');
return (
Counter: {props.counter}
)
})
function App() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2
}, [count === 3])
const onClick = useCallback(() => {
console.log('click')
}, [])
return (
count: {count}
)
}
export default App;