之前学到的memo是用来优化函数组件的重渲染问题,当传入的属性值都没变化时就不会触发组件的重渲染,否则组件就会重渲染。和类组件中的PureComponent
组件是类似。
useMemo功能是判断组件中的函数逻辑是否重新执行,用来优化性能。
import React, { useState, useMemo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
function Count(props) {
return <div>子组件count值:{props.count}</div>
}
function App(props) {
const [count, setCount] = useState(0);
//第一个参数是要执行的函数
//第二个参数是执行函数依赖的变量组成的数据
//这里只有count发生变化double才会重新计算
const double = useMemo(() => {
return count * 2;
}, [count])
return (
<div className="app">
<p>父组件count值:{count}</p>
<Count count={count} />
<button
onClick={() => {
setCount(count + 1)
}}>
Add
</button>
<div>double值:{double}</div>
</div>
)
}
export default App;
效果:
现在修改传入函数的值:
const double = useMemo(() => {
return count * 2;
}, [count===3])
效果:
count===3]
会从false变为true,再变成false。中间发生了两次变化。所以double的值发生了两次变化。
接着优化组件渲染问题:
import React, { useState, memo, useMemo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
const Count = memo(function Count(props) {
console.log('count render')
return <div>子组件count值:{props.double}</div>
})
function App(props) {
const [count, setCount] = useState(0);
//第一个参数是要执行的函数
//第二个参数是执行函数依赖的变量组成的数据
//这里只有count发生变化double才会重新计算
const double = useMemo(() => {
return count * 2;
}, [count === 3])
return (
<div className="app">
<p>父组件count值:{count}</p>
<Count double={double} />
<button
onClick={() => {
setCount(count + 1)
}}>
Add
</button>
<div>double值:{double}</div>
</div>
)
}
export default App;
效果:
接着向Count子组件中传入onClick方法,看Count子组件是否会被重新渲染:
import React, { useState, memo, useMemo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';
const Count = memo(function Count(props) {
console.log('count render')
return <div onClick={props.onClick}>子组件count值:{props.double}</div>
})
function App(props) {
const [count, setCount] = useState(0);
//第一个参数是要执行的函数
//第二个参数是执行函数依赖的变量组成的数据
//这里只有count发生变化double才会重新计算
const double = useMemo(() => {
return count * 2;
}, [count === 3])
const onClick = () =>{
console.log('click')
}
return (
<div className="app">
<p>父组件count值:{count}</p>
<Count double={double} onClick={onClick}/>
<button
onClick={() => {
setCount(count + 1)
}}>
Add
</button>
<div>double值:{double}</div>
</div>
)
}
export default App;
效果:
App父组件发生变化重新渲染后,onClick句柄也发生了变化,导致Count子组件每次也会被重渲染,即使传入的double值没有发生变化。此时就需要让onClick句柄不发生变化。让onClick为useMemo
的返回值:
// const onClick = () =>{
// console.log('click')
// }
//这样返回的函数就会在组件重渲染时产生相同的句柄
const onClick = useMemo(() => {
//这里返回的依然是函数
return () => {
console.log('click')
}
}, []);
这次当传入Count组件的count和onClick都没有发生变化时就不会做无意义的重渲染:
如果useMemo
返回的是一个函数,则可以用useCallback
省略顶层的函数。 将包裹的onClick函数用useCallback
包裹:
// const onClick = () =>{
// console.log('click')
// }
//这样返回的函数就会在组件重渲染时产生相同的句柄
//useMemo使用
// const onClick = useMemo(() => {
// //这里返回的依然是函数
// return () => {
// console.log('click')
// }
// }, []);
//useCallback使用
const onClick = useCallback(() => {
console.log('click')
}, []);
因此useCallback
是useMemo
的变体。
useMemo(()=>return fn);
//等价
useCallback(fn);