为什么这个 `<Suspense>` 子组件在挂起的 Promise 解决后会丢失其状态而不是渲染?

问题描述 投票:0回答:1

为了理解 React 的

<Suspense>
组件,我尝试利用
setTimeout
编写一个简单的钩子,以在渲染之前触发一段时间的“挂起”状态。

在我的测试中,我使用以下设置:

import { Suspense, useMemo, useState } from "react";

function useSuspend(ms: number) {
  const [isSuspended, setSuspended] = useState(true);
  const promise = useMemo(
    () =>
      new Promise<void>((resolve) => {
        setTimeout(() => {
          setSuspended(false);
          resolve();
        }, ms);
      }),
    [ms]
  );

  console.table({ isSuspended, ms });
  if (isSuspended) {
    throw promise;
  }
}

function Suspending() {
  const [id] = useState(Math.random().toFixed(20));
  console.log(id);

  useSuspend(2500);

  return "Done";
}

export default function Main() {
  return (
    <Suspense fallback={"Loading..."}>
      <Suspending />
    </Suspense>
  );
}

但是,这会产生一些(对我而言)相当意外的日志打印:

0.91830134558829579206
┌─────────────┬────────┐
│   (index)   │ Values │
├─────────────┼────────┤
│ isSuspended │  true  │
│     ms      │  2500  │
└─────────────┴────────┘

(2.5s pause)

0.33716150767738661820
┌─────────────┬────────┐
│   (index)   │ Values │
├─────────────┼────────┤
│ isSuspended │  true  │
│     ms      │  2500  │
└─────────────┴────────┘

(continues infinitely every 2.5s forever)

“完成”文本也永远不会呈现。

这些日志似乎表明

<Suspending />
组件在
useSuspend
钩子完成后不会保留其状态,提示组件呈现“就像新的一样”,这对我来说是违反直觉的。有人可以解释一下这种行为吗?

reactjs react-hooks react-suspense
1个回答
0
投票

这些日志似乎表明组件在 useSuspend 钩子完成后不会保留>其状态,提示组件呈现“就像新的一样”,这对我来说是违反直觉的。

是的。 Suspense 的设计是为了当它捕获到一个 Promise 时,它将卸载子树并渲染后备(如果有)。后来,诺言得到了解决,悬念将再次出现在孩子们身上。当孩子们安装时,它们是全新的组件。结果

isSuspended
被设置为其初始值
true
,并且
useMemo
运行并创建一个新的 Promise。然后这个承诺被抛出,并且这个过程重复

为了悬念而做出承诺实际上有点棘手。您通常需要拥有组件外部存在的一些值,您可以同步检查该值以查看异步工作是否已完成。如果没有,则抛出一个可以设置外部值并自行解决的承诺。例如:

let loaded = false;
let promise = null;

function useSuspend(ms: number) {
  if (!loaded) {
    if (!promise) {
      promise = new Promise(resolve => {
        setTimeout(resolve, ms);
      });
    }
    throw promise;
  }
}

请注意,由于加载的值是全局变量,因此应用程序中的每个组件都在共享它。无论哪个组件首先调用

useSuspend
都会启动超时,所有其他组件都会效仿。如果您希望不同的组件具有不同的超时,则需要创建一些更复杂的值存储来满足您的需求。

© www.soinside.com 2019 - 2024. All rights reserved.