React 18 启用并发模式 (Concurrent Mode)【机翻】

原文:https://17.reactjs.org/docs/concurrent-mode-reference.html

题外话

截止到目前版本 React 18 对应的 @types/react (17.0.43)@types/react-dom (17.0.14) 尚未更新

  • ReactDOM.createRoot 需要改为引用 react-dom/client ,如 import { createRoot } from 'react-dom/client'
  • React 18 的新特性,有更新在 @types/react/next.d.ts ,但默认 @types/react/index.d.ts 没有引用,可以手动添加引用,或者在项目中自行引用。相信很快 @types/react 和 @types/react-dom 就会同步 18 的版本。

这篇大部分是 Google 翻译的 ,算是一个走读性的文章,对 React 18 的并发模式有所了解即可。

createRoot

ReactDOM.createRoot(rootNode).render(<App />);

替换 ReactDOM.render(<App />, rootNode) 以启用并发模式(Concurrent Mode)。

Suspense API

Suspense

<Suspense fallback={<h1>Loading...</h1>}>
  <ProfilePhoto />
  <ProfileDetails />
</Suspense>

Suspense 让你的组件在渲染之前“等待”某些东西,在等待时显示回退。

在此示例中,ProfileDetails 正在等待异步 API 调用以获取一些数据。 当我们等待 ProfileDetailsProfilePhoto 时,我们将改为显示 Loading... 后备。 重要的是要注意,直到 <Suspense> 中的所有子项都已加载,我们将继续显示回退。

Suspense 有两个属性:

  • fallback 需要一个加载指示器。 在 Suspense 组件的所有子组件完成渲染之前,会显示回退。
  • unstable_avoidThisFallback 需要一个布尔值。 它告诉 React 是否在初始加载期间“跳过”显示此边界。 此 API 可能会在未来的版本中删除。

<SuspenseList>

<SuspenseList revealOrder="forwards">
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={1} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={2} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={3} />
  </Suspense>
  ...
</SuspenseList>

SuspenseList 通过协调这些组件向用户显示的顺序来帮助协调许多可以暂停的组件。

当多个组件需要获取数据时,这些数据可能会以不可预知的顺序到达。 然而,如果你将这些项目包装在一个 SuspenseList 中,React 将不会在列表中显示一个项目,直到之前的项目已经显示(这个行为是可调整的)。

SuspenseList 有两个属性:

  • revealOrder (forwards, backwards, together) 定义应该显示“SuspenseList”子项的顺序。
    • together 会在它们准备好时显示所有,而不是一一显示。
  • tail (collapsed, hidden) 指示如何显示 SuspenseList 中未加载的项目。
    • 默认情况下,SuspenseList 将显示列表中的所有后备。
    • collapsed 仅显示列表中的下一个后备。
    • hidden 不显示任何已卸载的项目。

请注意,SuspenseList 仅在其下方最近的 SuspenseSuspenseList 组件上运行。 它不会搜索比一层更深的边界。 但是,可以将多个“SuspenseList”组件相互嵌套以构建网格。

useTransition

const SUSPENSE_CONFIG = { timeoutMs: 2000 };

const [startTransition, isPending] = useTransition(SUSPENSE_CONFIG);

useTransition allows components to avoid undesirable loading states by waiting for content to load before transitioning to the next screen. It also allows components to defer slower, data fetching updates until subsequent renders so that more crucial updates can be rendered immediately.

useTransition 允许组件通过在转换到下一个屏幕之前等待内容加载来避免不希望的加载状态。 它还允许组件将较慢的数据获取更新推迟到后续渲染,以便可以立即渲染更重要的更新。

useTransition 钩子返回一个数组中的两个值。

  • startTransition 是一个接受回调的函数。 我们可以使用它来告诉 React 我们想要推迟哪个状态。
  • isPending 是一个布尔值。 这是 React 通知我们是否正在等待过渡完成的方式。

**如果某些状态更新导致组件挂起,则该状态更新应包含在转换中。 **

const SUSPENSE_CONFIG = { timeoutMs: 2000 };

function App() {
  const [resource, setResource] = useState(initialResource);
  const [startTransition, isPending] = useTransition(SUSPENSE_CONFIG);
  return (
    <>
      <button
        disabled={isPending}
        onClick={() => {
          startTransition(() => {
            const nextUserId = getNextId(resource.userId);
            setResource(fetchProfileData(nextUserId));
          });
        }}
      >
        Next
      </button>
      {isPending ? " Loading..." : null}
      <Suspense fallback={<Spinner />}>
        <ProfilePage resource={resource} />
      </Suspense>
    </>
  );
}

在这段代码中,我们使用 startTransition 包装了我们的数据获取。 这使我们可以立即开始获取配置文件数据,同时将下一个配置文件页面及其关联的Spinner的呈现延迟 2 秒(时间显示在timeoutMs中)。

isPending 布尔值让 React 知道我们的组件正在转换,因此我们可以通过在前一个配置文件页面上显示一些加载文本来让用户知道这一点。

要深入了解事务(个人认为 Transition 理解为数据库处理的事务,更贴切,很早就认为 React 应该实现这种机制,每个项目都得手写),您可以阅读 Concurrent UI Patterns.

useTransition Config

const SUSPENSE_CONFIG = { timeoutMs: 2000 };

useTransition 接受带有 timeoutMs可选的 Suspense Config。 这个超时时间(以毫秒为单位)告诉 React 在显示下一个状态(上例中的新配置文件页面)之前要等待多长时间。

注意:我们建议您在不同模块之间共享 Suspense Config。

从实际项目角度出发,应该是:

  • 全局有一个默认的 Suspense 配置
  • 组件再根据实际的环境和情况,override 这个配置

useDeferredValue

const deferredValue = useDeferredValue(value, { timeoutMs: 2000 });

返回值的延迟版本,该版本最多可能“lag behind” 它 timeoutMs

当您有基于用户输入立即呈现的内容以及需要等待数据获取的内容时,这通常用于保持界面响应。

一个很好的例子是文本输入。

function App() {
  const [text, setText] = useState("hello");
  const deferredText = useDeferredValue(text, { timeoutMs: 2000 }); 

  return (
    <div className="App">
      {/* Keep passing the current text to the input */}
      <input value={text} onChange={handleChange} />
      ...
      {/* But the list is allowed to "lag behind" when necessary */}
      <MySlowList text={deferredText} />
    </div>
  );
 }

这使我们可以立即开始显示input的新文本,从而使网页感觉响应(立即响应)。 同时,根据 timeoutMsMySlowList 在更新前“滞后”最多 2 秒,使其能够在后台使用当前文本进行渲染。

useDeferredValue Config

const SUSPENSE_CONFIG = { timeoutMs: 2000 };

useDeferredValue 接受带有 timeoutMs可选 Suspense Config。 这个超时时间(以毫秒为单位)告诉 React 延迟值允许滞后多长时间。

当网络和设备允许时,React 总是会尝试使用更短的延迟。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章