cache 允许缓存数据获取或者计算的结果,它能够包装一个函数,以避免在相同的参数传递给他们时重新计算结果

cache 目前仅支持在服务器组件中使用

使用方式

Demo1

import { cache, useState, useReducer } from "react"
const alertCounter = cache((id) => {
	alert(id);
});
function App() {
	const [counter, setCounter] = useState(0);
	const [_, rerender] = useReducer(() => ({}), {});
    
	alertCounter(counter);
    return (
		<div>
			<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
            <!-- Force a re-render to see the alert -->
			<button onClick={rerender}>Rerender</button>
            <!-- To verify that we're actually re-rendering, any input value should disappear between renders -->
			<input key={Math.floor(Math.random() * 10)} />
		</div>
	);
}

在上面这段代码中,如果不使用 cache 时,每次点击 Rerender 使 App rerender 时 alertCounter 都会执行一次,使用 cache 包裹后,如果传入的 id 与上一次结果相同,那么就不会再执行 alert(id).

Demo2

const cacheFn = cache(fn);
import { cache } from 'react';
 
function getUser(id: number) {
	const userId = `user-${id}`;
	console.log('getUser 执行');
	return userId;
}
 
const cacheGetUser = cache(getUser);
 
function Profile({ id }: { id: number }) {
	const userId = cacheGetUser(id);
	return <div>profile {userId}</div>;
}
 
function BaseInfo({ id }: { id: number }) {
	const userId = cacheGetUser(id);
	return <div>base: {userId}</div>;
}
 
export default function User() {
	const userId = cacheGetUser(1);
	return (
		<div>
			{userId}
			<Profile id={1} />
			<BaseInfo id={1} />
		</div>
	);
}

👆上面的代码我们使用 cache API 创建了一个包裹 getUser 函数的函数,并且在三个不同的地方调用了 cacheGetUser 函数,在正常不使用 cache 函数的情况下,getUser 中的 console.log 应该会被打印 3 次,
而使用 cache 执行,实际上 getUser 中的 console.log 只会打印一次

注意点

  • cache 仅可以在服务器组件中使用
  • React 会在每次服务器请求时使所有的记忆化函数的缓存失效
  • 每次调用 cache 都会创建一个新函数。意味着多次使用相同的函数调用 cache 将返回不共享相同缓存的不同记忆化函数
  • cachedFn 还会缓存错误,如果对于某些参数fn 抛出错误,错误将被缓存,当时用相同参数调用 cacheFn 时,相同的错误将被重新抛出

场景

共享数据快照 (跨组件结果缓存)

上面👆的代码就是一个 共享数据快照的真实案例,可以将 getUser 看做一个 fetch 请求,这样在每个组件中都使用 getUser 请求时,实际上只会请求一次。

预加载数据

缓存昂贵的数据计算

如果某些数据计算非常昂贵,例如 数据装换等,可以使用 cache 来缓存计算的结果

缓存错误信息

const getIsEven = cache((number) => {
  alert("I am checking if " + number + " is even or not");
  if (number % 2 === 0) {
    throw "Number is even";
  }
});
// Even if you render this component multiple times, it will only perform the
// calculation needed to throw the error once.
function ThrowAnErrorIfEven({ number, instance }) {
  getIsEven(number);
  return <p>I am instance #{instance}</p>;
}
function App = () => {
    const [counter, setCounter] = useState(0);
    return (
        <div>
            <p>You should only see one alert when you push the button below</p>
			<button onClick={() => setCounter((v) => v + 1)}>Add to {counter}</button>
            <ErrorBoundary>
                <ThrowAnErrorIfEven number={counter} instance={1} />
            </ErrorBoundary>
            <ErrorBoundary>
                <ThrowAnErrorIfEven number={counter} instance={2} />
            </ErrorBoundary>
            <ErrorBoundary>
                <ThrowAnErrorIfEven number={counter} instance={3} />
            </ErrorBoundary>
            <ErrorBoundary>
                <ThrowAnErrorIfEven number={counter} instance={4} />
            </ErrorBoundary>
        </div>
    )
}
 

与 memo 和 useMemo 的区别

  • memo 用于通过基于props的记忆化函数组件来避免在组件中重新渲染
  • useMemocache的功能看起来相差无几,但是 cache相比于 useMemo的优点在于
    • 无需修改组件代码本身来缓存函数结果
    • 跨组件缓存结果

何时使用 useMemo 、 memo 和 cache

  • useMemo 一般用于在客户端组件跨渲染时缓存昂贵的计算
  • cache 一般用于在服务器组件以记忆化可以跨组件共享的工作
  • memo 一般用于防止组件在 props 未更改时重新渲染

参考