前言
默认情况下, 父组件的状态(state)发生变化,不仅会重新渲染自己,还会重新渲染其子组件。
因此,React 发明了 useMemo & useCallback 来帮助你减少不必要的渲染
。
此外还有, shouldComponentUpdate / React.PureComponent/ React.memo 在干类似的事情。
useMemo
const UseMemoPage = props => {
const [counter, setCounter] = useState(0)
const [value, setValue] = useState('')
const total = () => {
console.log('computed')
let result = 0
for (let i = 0; i < counter; i++) {
result += i
}
return result
}
console.log(UseMemoPage.name, 'render')
return (<>
<p>{value}</p>
/* 当你在更改 input 的值得时候, 也会 触发 total 不必要的运算, */
<input value={value} onChange={(e) => setValue(e.target.value)} />
{total()}
<button onClick={() => setCounter(counter + 1)}>add</button>
</>)
}
// console.log UseMemoPage render
// console.log computed
- 通过useMemo改进, value 的改变不会触发的重新计算
此外useMemo还可以返回一个React组件,有相同的效果
const total = useMemo(() => {
console.log('computed')
let result = 0
for (let i = 0; i < counter; i++) {
result += i
}
return result
}, [counter])
// console.log UseMemoPage render
useCallback
const UseCallbackPage = props => {
const [counter, setCounter] = useState(0)
const [value, setValue] = useState('')
const addClick = () => {
console.log(addClick.name, 'render')
setCounter(state => state + 1)
}
console.log(UseCallbackPage.name, 'render')
return (<>
<p>{value}</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<button onClick={() => setCounter(counter + 1)}>add</button>
<Child counter={counter} addClick={addClick}></Child>
</>)
}
const Child = React.memo((props) => {
console.log('Child', 'render')
return (<div onClick={props.addClick}>{props.counter}</div>)
})
// console.log UseMemoPage render
// console.log Child render
- 虽然你使用React.memo , 但是value更新时, 都会生成新的addClick(只是名字一样), 所以会造成Child组件的渲染, 此时就是useCallback 登场的时机, 它能缓存你定义的function
const addClick = useCallback(() => {
console.log(addClick.name, 'render')
setCounter(state => state + 1)
}, [])
// console.log UseCallbackPage render
总结:
- useCallback, useMemo 看起来就像是React的补丁, 但能很好的
减少不必要的渲染
- useMemo 返回的是个object, 一般适用于组件本身
- useCallback 返回的是一个function, 可以配合React.memo来减少Child组件的渲染
- 所以子组件尽量为无状态组件, 方便 开发 / 测试 / 优化