useMemo
和 useCallback
是 React 的两个高级 Hooks,分别用于优化渲染性能和避免不必要的重新渲染,和vue中的计算属性computed相似(缓存)
1.useMemo
useMemo
用于避免在每次渲染时都执行高开销的计算。它会返回一个 memoized(缓存)的值,这个值仅在其依赖项发生变化时才会重新计算。
import React, { useState, useEffect, useMemo } from 'react';
function App() {
const [x, setx] = useState(0)
const [y, sety] = useState(0)
console.time("xxx")
const computedValue = useMemo(() => {
let a = 0;
for (let i = 0; i < 1000000000; i++) {
a += y
}
return a
}, [y])
console.timeEnd("xxx")
console.log(computedValue)
return (
<div>
<div> x:{x};;;;y{y}</div>
<div>
<button onClick={() => setx(x + 1)}>xxx</button>
<button onClick={() => sety(y + 1)}>yyy</button>
</div>
</div>
)
}
export default App
当y发生变化时computedValue会重新计算,x变化时候由于computedValue没有依赖x,所以不会重新计算
2.useCallback
useCallback
用于返回一个记忆化的回调函数。只有当其依赖项发生变化时,该函数才会更新。这有助于避免在每次渲染时都创建新的函数实例,从而提高性能并减少不必要的重新渲染。
import React, { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
// 使用 useCallback 来记忆化这个回调函数
// 注意这里我们没有将 count 作为依赖项,因此 handleIncrement 不会随着 count 的变化而变化 const handleIncrement = useCallback(() => {
setCount(count => count + 1);
}, []);
// 空数组意味着 handleIncrement 不会基于任何 props 或 state 的变化而重新创建
return (
<div> <p>Count: {count}</p>
<ChildComponent onIncrement={handleIncrement} />
<button onClick={handleIncrement}>Increment</button>
</div>
);
}
function ChildComponent({ onIncrement }) {
// ChildComponent 可能会基于 onIncrement 的引用是否改变来决定是否重新渲染
// 使用 useCallback 可以确保即使 ParentComponent 重新渲染,onIncrement 的引用也不会改变(除非我们显式地将它作为依赖项)
return (
<div> <button onClick={onIncrement}>Increment from Child</button> </div> );
}
export default ParentComponent;
ParentComponent
有一个状态 count
和一个用于增加 count
的回调函数 handleIncrement
。handleIncrement
是通过 useCallback
创建的,并且没有依赖任何变量(依赖项数组是空的 []
)。这意味着,无论 ParentComponent
渲染多少次,handleIncrement
的引用都将保持不变。
ChildComponent
接收 onIncrement
作为 prop,并可能基于这个 prop 的引用是否变化来决定是否重新渲染。因为 handleIncrement
是通过 useCallback
创建的,并且没有依赖任何变量,所以即使 ParentComponent
重新渲染,ChildComponent
接收到的 onIncrement
引用也会保持不变,这有助于避免不必要的渲染。