概念
useMemo 是一个 React Hook,它允许你在两次重新渲染之间缓存计算结果。
原型
const cachedValue = useMemo(calculateValue, dependencies)
用法
在组件的顶层调用 useMemo
以缓存重新渲染之间的计算。
参数
calculateValue:计算要缓存的值的函数。它应该是纯函数,不应包含任何参数,并且应返回任何类型的值。React 将在初始渲染期间调用你的函数。在下一次渲染时,如果依赖项
自上次渲染以来没有改变,React 将再次返回相同的值。否则,它将调用 calculateValue
,返回其结果,并存储它以便以后可以重用。
dependencies:依赖项列表, calculateValue 代码中引用的所有响应值的列表。响应式值包括 props、state 以及直接在组件体内声明的所有变量和函数。React 将使用 Object.is 比较将每个依赖项与其之前的值进行比较。
场景:繁重复杂的运算
假设我们正在构建一个工具来帮助用户查找介于 0 和 selectedNum
之间的所有质数,其中 selectedNum
是用户提供的值。质数是一个只能被 1 和自身整除的数字,比如 17。实现的代码可能如下:
import React from 'react';
function App() {
// We hold the user's selected number in state.
const [selectedNum, setSelectedNum] = React.useState(100);
// We calculate all of the prime numbers between 0 and the
// user's chosen number, `selectedNum`:
const allPrimes = [];
for (let counter = 2; counter < selectedNum; counter++) {
if (isPrime(counter)) {
allPrimes.push(counter);
}
}
return (
<>
<form>
<label htmlFor="num">Your number:</label>
<input
type="number"
value={selectedNum}
onChange={(event) => {
// To prevent computers from exploding,
// we'll max out at 100k
let num = Math.min(100_000, Number(event.target.value));
setSelectedNum(num);
}}
/>
</form>
<p>
There are {allPrimes.length} prime(s) between 1 and {selectedNum}:
{' '}
<span className="prime-list">
{allPrimes.join(', ')}
</span>
</p>
</>
);
}
// Helper function that calculates whether a given
// number is prime or not.
function isPrime(n){
const max = Math.ceil(Math.sqrt(n));
if (n === 2) {
return true;
}
for (let counter = 2; counter <= max; counter++) {
if (n % counter === 0) {
return false;
}
}
return true;
}
export default App;
此代码需要大量的计算。 如果用户选择了一个较大的 selectedNum
,我们将需要遍历数以万计的数字,检查每个数字是否都是质数。
当我们需要在此基础上,显示一个数据时钟时,代码可能如下:
import React from 'react';
import format from 'date-fns/format';
function App() {
const [selectedNum, setSelectedNum] = React.useState(100);
// `time` is a state variable that changes once per second,
// so that it's always in sync with the current time.
const time = useTime();
// Calculate all of the prime numbers.
// (Unchanged from the earlier example.)
const allPrimes = [];
for (let counter = 2; counter < selectedNum; counter++) {
if (isPrime(counter)) {
allPrimes.push(counter);
}
}
return (
<>
<p className="clock">
{format(time, 'hh:mm:ss a')}
</p>
<form>
<label htmlFor="num">Your number:</label>
<input
type="number"
value={selectedNum}
onChange={(event) => {
// To prevent computers from exploding,
// we'll max out at 100k
let num = Math.min(100_000, Number(event.target.value));
setSelectedNum(num);
}}
/>
</form>
<p>
There are {allPrimes.length} prime(s) between 1 and {selectedNum}:
{' '}
<span className="prime-list">
{allPrimes.join(', ')}
</span>
</p>
</>
);
}
function useTime() {
const [time, setTime] = React.useState(new Date());
React.useEffect(() => {
const intervalId = window.setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
window.clearInterval(intervalId);
}
}, []);
return time;
}
function isPrime(n){
const max = Math.ceil(Math.sqrt(n));
if (n === 2) {
return true;
}
for (let counter = 2; counter <= max; counter++) {
if (n % counter === 0) {
return false;
}
}
return true;
}
export default App;
我们的应用程序现在有两个 state, selectedNum 和 time。每秒更新一次 time 变量以反映当前时间,该值用于呈现右上角的数字时钟。
问题是: 每当这些 state 变量中的任何一个发生变化时,我们都会重新运行所有这些昂贵的质数计算。由于时间每秒更改一次,这意味着我们会不断重新生成该素数列表,即使用户选择的数字没有更改!
在 JavaScript 中,我们只有一个主线程,我们通过一遍又一遍地运行这些代码来保持它的超级繁忙。这意味着当用户尝试执行其他作时,应用程序可能会感觉迟缓,尤其是在低端设备上。
但是,如果我们可以 “跳过” 这些计算呢? 如果我们已经有给定数字的质数列表,为什么不重用该值,而不是每次都从头开始计算呢?
这正是 useMemo 允许我们做的。
const allPrimes = React.useMemo(() => {
const result = [];
for (let counter = 2; counter < selectedNum; counter++) {
if (isPrime(counter)) {
result.push(counter);
}
}
return result;
}, [selectedNum]);
通过以上示例,能够很好地理解 useMemo 的作用。
以上代码和图片,均来自于:Understanding useMemo and useCallback • Josh W. Comeau