cache
const cachedFn = cache(fn);
cache 会以函数实参为键,缓存首次函数调用的结果,后续的函数调用也将直接使用缓存值。
cache只能在服务端组件中使用,缓存的生命周期为单次服务器请求。fn可以是异步函数。若fn抛出错误,该错误也会被缓存。cache会通过Object.is来区分函数传参。cachedFn可以跨组件使用并共享缓存。缓存访问是通过 context 实现的,所以在组件外调用cachedFn不会读取或更新缓存。
如果 fn 是个比较缓慢的异步函数(比如数据请求),则可以先在组件的头部位置执行 cachedFn :
const getData = cache(fetchData);
async function MyComponent() {
    getData();
    // 利用获取数据的时间做一些别的工作
    await getData();
    // ...
}
startTransition
startTransition(scope);
scope应当是个同步的函数,内部可以调用多个 setState 状态更新。- 使用 
startTransition的状态更新会被标记为 transition 。transition 更新会被别的 state 更新打断。 - 不要将 transition 更新用于控制文本输入等行为中。
 
import { startTransition } from 'react';
function TabContainer() {
    const [tab, setTab] = useState('about');
    function selectTab(nextTab) {
        startTransition(() => {
            setTab(nextTab);
        });
    }
    // ...
}
scope 中应当同步调用状态更新。一些错误使用:
startTransition(() => {
    setTimeout(() => {
        setPage('/about');
    }, 1000);
});
startTransition(async () => {
    await someAsyncFunction();
    setPage('/about');
});
useTransition
const [isPending, startTransition] = useTransition();
isPending- 是否有待处理的 transition 更新。
useDeferredValue
const deferredValue = useDeferredValue(value);
对于一些较慢的组件,可以用 useDeferredValue 优化,可以避免高频率的更新。
value- 想延迟的值。value更新时,React 仍会先用旧值渲染,然后在后台用新值渲染。这个后台渲染是可中断的,如果渲染期间value再次被更新,React 则会丢弃先前的渲染并重新渲染。useDeferredValue本身不会设立一个延迟的值,一旦value更新就会立即在后台渲染。在提交到屏幕之前useDeferredValue不会触发 effect 。- 性能开销较大的组件,一般也需要对其用 
memo包裹,才能得到真正的优化。 
import { useState, useDeferredValue } from 'react';
function SearchPage() {
    const [query, setQuery] = useState('');
    const deferredQuery = useDeferredValue(query);
    // ...
}
当 query 更新时:
- 首先,React 会使用新的 
query和旧的deferredQuery重新渲染。 - 在后台,React 尝试用新的 
deferredQuery渲染。如果中途遇到了 suspense ,或者query有了新的更新,则会中断放弃此次渲染(但期间产生的网络请求并不会因此中断)。 
Suspense
<Suspense fallback={<Loading />}>
    <SomeComponent />
</Suspense>
下文中的「挂起」指的是英文 suspense 。
- 若 Suspense 被再次挂起,将会展示 
fallback,除非是startTransition或useDeferredValue引起的更新。 - 为了确保测量 DOM 布局的 effect 正常工作,Suspense 在挂起时会清理 
children里的 layout effect ,再次展示时也会重新触发 layout effect 。 - Suspense 允许嵌套,当组件挂起时会查找其最近的 Suspense 挂起。
 
在 children 加载数据时,Suspense 会被挂起。这里的 数据加载 仅限以下方式:
- 在支持 Suspense 的框架(如 Relay 或 Next.js)下请求数据(需参阅相应文档);
 - 使用 
lazy加载组件代码; - 使用 
use读取 Promise 值。 
function App() {
    const [query, setQuery] = useState('');
    return (
        <>
            <label>
                Search albums:
                <input value={query} onChange={e => setQuery(e.target.value)} />
            </label>
            <Suspense fallback={<h2>Loading...</h2>}>
                <SearchResults query={query} />
            </Suspense>
        </>
    );
}
如果想在更新时展示旧数据(而不是 fallback),可以用 useDeferredValue 或 transition :
export default function App() {
    const [query, setQuery] = useState('');
    const deferredQuery = useDeferredValue(query);
    return (
        <>
            <label>
                Search albums:
                <input value={query} onChange={e => setQuery(e.target.value)} />
            </label>
            <Suspense fallback={<h2>Loading...</h2>}>
                <SearchResults query={deferredQuery} />
            </Suspense>
        </>
    );
}
在服务端中,可以在组件中主动抛出错误,并用一个 Suspense 包裹:
<Suspense fallback={<Loading />}>
    <Chat />
</Suspense>
function Chat() {
    if (typeof window === 'undefined') {
        throw Error('Chat 只能在客户端中渲染。');
    }
    // ……
}