前言:
在我们渲染页面时,有很多的函数和变量在没有被调用,但确触发并执行,父组件内的一部分在渲染时,子组件也会重新渲染等,这样会造成大量的内存消耗。 所以 React-hooks 为我们提供了 useMemo 和 useCallback 来让我们对此进行优化处理,减少此类消耗,提高整体性能。
一、useMemo 的使用
1.不使用useMemo时
function App() {
const [count, setCount] = useState(1);
const [value, setValue] = useState('');
function getNum() {
console.log("getNum");
return count * 100
}
return (
<div>
<h4>总和:{getNum()}</h4>
<button onClick={() => setCount(count + 1)}>+1</button>
<h4>Value: {value}</h4>
<input value={value} onChange={event => setValue(event.target.value)} />
</div>
)
}
点击了2次 +1, 输入了 123 由此可见,上面这个组件,维护了两个state,可以看到 getNum 的计算只跟 count 有关,但是现在无论是 count 还是 value 变化,都会导致getNum重新计算,所以这里我们希望value修改的时候,getNum不需要再次计算,这种情况下我们可以使用useMemo。
2.使用useMemo
function App() {
const [count, setCount] = useState(1);
const [value, setValue] = useState('');
const getNum = useMemo(() => {
console.log("getNum");
return count * 100
}, [count])
return (
<div>
<h4>总和:{getNum}</h4>
<button onClick={() => setCount(count + 1)}>+1</button>
<h4>Value: {value}</h4>
<input value={value} onChange={event => setValue(event.target.value)} />
</div>
)
}
点击了2次 +1, 输入了 123 可以看出,使用了useMemo之后,value的改变不会触发getNum了。
3.useMemo的参数 和 返回值
useMemo的参数和上一篇将的useEffect的参数是一样的,有两个:
1.第一个参数是 (计算)函数:
内部一般都是存放 需要计算的值 的 计算逻辑(就比如上面的count,内部就进行了*100的操作)
2.第二个参数是 依赖项数组:
(1)如果 不传 第二个参数,则 useEffect 每一次渲染都会执行 (2)如果 传入一个空数组 ,则 useEffect 只会执行一次 (3)如果 传入的数组中有依赖项,则依赖项的值改变了,useEffect 才会执行
对于 依赖项数组 的改变,三种情况都是和 useEffect 一样的,这里就不一 一举例了。
3.返回值是一个值:
返回一个 memoized 值。在依赖参数不变的的情况返回的是上次第一次计算的值 可以用来储存内部函数计算出的值,这样每次调用时就不需要再运行内部的计算函数,以提高性能,除非依赖项的值改变了,否则不会重新计算。
二、useCallback 的使用
1.不使用useCallback 时
导致子组件也会重新渲染的原因:
父组件在重新渲染时,getNum 属性采用匿名函数赋值,导致每次的引用地址不一样,所以也触发了子组件的重新渲染**
function App() {
const [count, setCount] = useState(1);
const [value, setValue] = useState('');
const getNum = () => {
return count * 100
}
console.log("App render");
return (
<div>
<Child getNum={getNum} />
<button onClick={() => setCount(count + 1)}>+1</button>
<h4>Value: {value}</h4>
<input value={value} onChange={event => setValue(event.target.value)} />
</div>
)
}
const Child = React.memo(function ({ getNum }) {
console.log("Child render");
return <h4>总和:{getNum()}</h4>
})
点击了2次 +1, 输入了 123 由此可见,可以看到 子组件的重新渲染只跟 count 有关,但是现在无论是 count 还是 value 变化,都会导致子组件重新渲染,所以这里我们希望value修改的时候,子组件不要重新渲染,这种情况下我们可以使用useCallback 。
2.使用useCallback
function App() {
const [count, setCount] = useState(1);
const [value, setValue] = useState('');
const getNum = useCallback(() => {
return count * 100
}, [count])
console.log("App render");
return (
<div>
<Child getNum={getNum} />
<button onClick={() => setCount(count + 1)}>+1</button>
<h4>Value: {value}</h4>
<input value={value} onChange={event => setValue(event.target.value)} />
</div>
)
}
const Child = React.memo(function ({ getNum }) {
console.log("Child render");
return <h4>总和:{getNum()}</h4>
})
点击了2次 +1, 输入了 123 用了useCallback 后,子组件没有重新渲染。
3.useMemo的参数 和 返回值
useMemo的参数和上一篇将的useEffect的参数是一样的,有两个:
1.第一个参数是 内联回调函数:
内部一般都是存放 需要缓存逻辑。
2.第二个参数是 依赖项数组:
(1)如果 不传 第二个参数,则 useEffect 每一次渲染都会执行 (2)如果 传入一个空数组 ,则 useEffect 只会执行一次 (3)如果 传入的数组中有依赖项,则依赖项的值改变了,useEffect 才会执行
3.返回值是一个函数:
返回(第一个参数)回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
三、useMemo 和 useCallback 的异同
相同点:
useMemo和useCallback接收的参数都是一样,而且都是在其依赖项发生变化后才会重新执行
不同点:
1. useMemo 的返回值是一个 值(函数运行的结果) 2. useCallback 的返回值是一个 函数
|