把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
系列目录
- React新特性-Context(一)
- React新特性-Lazy和Suspense(二)
- React新特性-memo(三)
- React新特性-Hooks之useState(四)
- React新特性-Hooks之useEffect和useLayoutEffect(五)
- React新特性-Hooks之useContext(六)
- React新特性-Hooks之useRef(七)
- React新特性-Hooks之useMemo和useCallback(八)
- React新特性-Hooks之useReducer(九)
- React新特性-Hooks之自定义Hook(十完结)
useMemo的作用
正如第三章中React新特性-memo(三),memo
优化函数组件的渲染行为,当传入组件值不变的情况下都不会重新渲染组件,否则组件就会重新渲染。
而useMemo则定义了一段函数逻辑是否重复执行,本质都是利用了同样的算法来判断依赖是否改变,既而判断是否触发特定逻辑。useMemo
减少不必要的计算,实现优化。
useMemo的使用
//TestInput.js
import React, { useState } from 'react'
import Button from './Button'
const TestInput = () => {
const [inputValue, setInputValue] = useState('')
const [visible,setVisible] = useState(true)
const onVisible = ()=>{
setVisible((visible)=>!visible)
}
return (
<div>
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> {inputValue}
{ visible && <Button onVisible={onVisible}/>}
</div>
)
}
export default TestInput
//Button.js
import React,{memo} from 'react'
const Button = (props) => {
console.log('我被重新渲染了')
return (
<div>
<button onClick={props.onVisible}>点击隐藏</button>
</div>
)
}
//结合memo来优化渲染
export default memo(Button)
这里的例子:当input
输入的时候函数组件TestInput
就会重新渲染,而Button
组件并未用到TestInput
中的状态但是也会重新,这显然就需要优化。
这里加入export default memo(Button)
来做优化。来自动对比传入的props是否会改变。
但是这样还会再次渲染因为传入Button函数组件中的props.onVisible
是个函数,当TestInput
重新渲染onVisible
这个函数也会重新生成,这样引用地址变化就导致对比出新的数据。而我们在class组件中的解决方式是:
{ visible && <Button onVisible={this.onVisible}/>}
class组件中this.onVisible
是类中的一个方法函数,所以就不会再次生成新的函数。但是函数组件中就没法做到这一点。所以对下面代码做些改变:
const TestInput = () => {
//...
const onVisible = useMemo(() => {
return () => {
setVisible((visible) => !visible)
}
}, [])
return (
<div>
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> {inputValue}
{visible && <Button onVisible={onVisible} />}
</div>
)
}
//...
useMemo
和useEffect
的语法是有一样的,第一个参数是要执行的逻辑函数,第二个参数是依赖对比变量的数组。如果不传第二个参数就跟useEffect
一样,每次渲染都会执行,如果传入空数组只会运行一次。
一般
useMemo
都会传入第二个参数,不然使用useMemo
的意义就不存在了。
useMemo和useEffect执行的时机
useMemo
和useEffect
存在的巨大的差异就是调用时机。useEffect
执行的是副作用,所以一定会在渲染之后执行,但useMemo
是需要有返回值的,返回值可以直接参与渲染,所以useMemo
是在渲染期间完成的。
import React,{useState,useMemo} from 'react'
export default function Counter() {
const [count, setCount] = useState(0);
const doubleCount = useMemo(()=>{
return count * 2;
},[count])
return (
<>
<h1>count: {count}, doubleCount: {doubleCount}</h1>
<button onClick={()=>setCount((count)=>count+1)}>增加</button>
</>
);
}
随着count
的递增,doubleCount
也随着变化。而如果是useEffect
的话count
值就是上一次的count
值。这就是和useEffect
的差异。
useMemo和useCallback关系
使用过useMemo
后再来看useCallback
就非常简单了,如果useMemo
返回的是个函数,那么就可以使用useCallback
来省略顶层的函数。
//使用useMemo实现
const TestInput = () => {
//...
const onVisible = useMemo(() => {
return () => {
setVisible((visible) => !visible)
}
}, [])
return (
<div>
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> {inputValue}
{visible && <Button onVisible={onVisible} />}
</div>
)
}
//使用useCallback实现
const TestInput = () => {
//...
const onVisible = useCallback(() => {
setVisible((visible) => !visible)
}, [])
return (
<div>
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> {inputValue}
{visible && <Button onVisible={onVisible} />}
</div>
)
}
也就是说useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
。useMemo
返回缓存的变量,useCallback
返回缓存的函数。
注意:依赖项数组不会作为参数传给回调函数。