背景
首先我们定义一个Compound Component:
const App = () => {
console.log('App render.')
const [count, setCount] = useState(0)
const someValue = 'someValue'
return (
<div>
<button onClick={() => setCount(prev=>prev+1)}>
click to rerender
</button>
<CountDemo count={count} />
<Demo someValue={someValue} />
</div>
)
}
两个子组件分别为:
const CountDemo = ({count}) => {
console.log('count render.')
return (
<div>
<p>{count}</p>
</div>
)
}
const Demo = () => {
console.log('Demo render.')
return (
<div>Demo</div>
)
}
当我们点击按钮时,控制台会显示:
"App render."
"count render."
"Demo render."
可以看到CountDemo和Demo都会重新渲染,而实际上,需要更新的只有CountDemo。
memo
可以使用React.memo避免Demo组件重复渲染:
// 使用React.memo
const Demo = memo(() => {
console.log('Demo render.')
return (
<div>React.memo</div>
)
})
点击按钮,控制台显示:
"App render."
"count render."
避免对demo进行不必要的重渲染。
useCallback
如果传入子组件的不是基本类型数据,而是一个函数呢?
const App = () => {
...
const someValue = 'someValue'
const someFunc = () => someValue
...
<Demo someFunc={someFunc} />
...
}
const Demo = memo(({someFunc}) => {
...
return (
<div>{someFunc()}</div>
)
})
点击按钮,控制台显示:
"App render."
"count render."
"Demo render."
因为React.memo默认使用的是浅比较(shallowly compare),而函数是引用型数据,父组件每次重新渲染时都会生成不一样的someFunc,从而导致demo会发生重渲染。
所以,可以使用useCallback来返回一个memoized callback:
const App = () => {
...
// 使用useCallback,当someValue改变时才会改变
const someFunc = useCallback(() => someValue, [someValue])
...
}
此时点击按钮,demo组件不会出现不必要的重复渲染。
useMemo
若是传入一个对象,而对象也是引用型数据,那么与useCallback类似,我们可以使用useMemo避免不必要的重渲染:
const App = () => {
...
const someValue = 'someValue'
const someObj = useMemo(() => ({ value: someValue }), [someValue])
...
<Demo someFunc={someFunc} />
...
}
const Demo = memo(({someObj}) => {
...
return (
<div>{someObj.value}</div>
)
})
线上示例
See the Pen React Demo by eehong (@eugene930) on CodePen.