前言
我们在使用React开发过程中经常会遇到父组件引入子组件的情况,在不做任何优化处理的时候,往往会造成子组件不必要的重复渲染。看下面一个简单的例子
// 例子1:这种情况下,每次父组件更新count值的时候,子组件也会重复渲染
function Child(props){
console.log('子组件渲染')
return(
<div>{props.title}</div>
)
}
function Parent(){
const [count, setCount] = useState(1);
return(
<div>
<Child title="我是子组件" />
<button onClick={() => setCount(count+1)}>add</button>
<div>count:{count}</div>
</div>
)
}
React.memo
1、React.memo为高阶组件,它和React.PureComponent挺相似的
2、用法:React.memo(component, Func),第一个参数是自定义组件,第二个参数是一个函数,用来判断组件需不需要重新渲染。如果省略第二个参数,默认会对该组件的props进行浅比较
// 改造例子1:这种情况下,Child的props经浅比较无变化,则不重复渲染
function Child(props){
console.log('子组件渲染')
return(
<div>{props.title}</div>
)
}
const NewChild = React.memo(Child, (prevProps, nextProps) => {
// 自定义对比方法,也可忽略不写
// return true 则不重新渲染
// return false 重新渲染
})
function Parent(){
const [count, setCount] = useState(1);
return(
<div>
<NewChild title="我是子组件" />
<button onClick={() => setCount(count+1)}>add</button>
<div>count:{count}</div>
</div>
)
}
然而在某些情况下,光靠React.memo包裹子组件,子组件还是会进行不必要的重复渲染更新,这时useMemo和useCallback则可以进行更细粒度的性能优化。看以下这个例子
// 例子2
function Child({title, onChangeTitle}){
console.log('子组件渲染')
return(
<>
<div>{title.value}</div>
<button onClick={() => onChangeTitle('我是新的title')}>change title</button>
</>
)
}
const NewChild = React.memo(Child);
function Parent(){
const [count, setCount] = useState(1);
const [title, setTitle] = useState('我是子组件');
const onChangeTitle = (text) => {
setTitle(text)
}
return(
<div>
<NewChild title={{value: title}} onChangeTitle={onChangeTitle}>
<button onClick={() => setCount(count+1)}>add</button>
<div>count:{count}</div>
</div>
)
}
这种情况下,每次父组件更新的时候,子组件的title和onChangeTitle值都会生成新的内存地址,所以即使title没改变,子组件也会重新渲染,memo失效
useMemo
第一次渲染时执行,缓存变量,之后只有在依赖项改变时才会重新计算记忆值
useCallback
第一次渲染时执行,缓存函数,之后只有在依赖项改变时才会更新缓存
//改造例子2
function Child({title, onChangeTitle}){
console.log('子组件渲染')
return(
<>
<div>{title.value}</div>
<button onClick={() => onChangeTitle('我是新的title')}>change title</button>
</>
)
}
const NewChild = React.memo(Child);
function Parent(){
const [count, setCount] = useState(1);
const [title, setTitle] = useState('我是子组件');
const onChangeTitle = useCallback((text) => {
setTitle(text)
},[])
const memoTitle = useMemo(() => ({value: title}), [title])
return(
<div>
<NewChild title={memoTitle} onChangeTitle={onChangeTitle} />
<button onClick={() => setCount(count+1)}>add</button>
<div>count:{count}</div>
</div>
)
}
总结
1、useMemo和useCallback用法差不多,都是在第一次渲染的时候执行,然后在依赖项改变时再次执行,不同点在于,useMemo返回缓存的变量,useCallback返回缓存的函数
2、在实际的业务组件开发过程中,肯定比上面举出的例子要复杂的多得多,因而造成不必要的重复渲染的影响因素也会有很多,我们需要好好利用React.memo、useMemo和useCallback等来优化组件,同时也不要盲目使用