为了理解react的<Suspense>组件,我try 使用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>
  );
}

然而,这产生了一些(对我来说)相当意外的日志(log)打印:

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)

文本"完成"也从未呈现.

这些日志(log)似乎表明<Suspending />组件在useSuspend钩子完成后并不保留其状态,提示组件呈现为"as if new",这对我来说是违反直觉的.

推荐答案

这些日志(log)似乎表明<Suspending />组件在useSuspend钩子完成后不会保留其状态,提示组件呈现为"as if new",这对我来说是违反直觉的.

没错Suspense的设计是为了当它捕获到一个promise时,它将卸载子树并呈现回退(如果有的话).后来,promise 解决了,悬念将再次增加子元素们.对于新安装的组件来说,它们的状态将被指定为初始值(即isSuspendedtrue),useMemo运行并创建一个新的promise.然后抛出这个promise,然后这个过程重复

抛下悬念的promise 是有点棘手的.您通常需要在组件外部存在一些值,您可以同步判断该值,以查看异步工作是否已经完成.如果没有,你就抛出一个promise,它可以设置外部值并解决它自己.例如:

let loaded = false;
let promise = null;

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

请注意,由于loaded是一个全局变量,所以应用中的每个组件都共享它.哪个组件首先调用useSuspend将启动超时,所有其他组件将重复使用相同的promise.如果您希望不同的组件具有不同的超时时间,您需要创建一些更复杂的值存储,以满足您的需求.

Reactjs相关问答推荐

使用useEffect挂钩获取数据

从另一个组件更改组件中const的状态

ReactJs;Reaction-Hook-Form:仅当 Select 了特定 Select 列表选项时才显示文本输入

如何在单击行中的图标时避免选中ionic 复选框?

获取点击的国家/地区名称react 映射

可以使用mode.css/mode.scss引用常规的类名吗?

FireBase Reaction Native-在呈现视图之前等待响应

如何获取用户在 GooglePlacesAutocomplete 中输入的文本?

当从子状态更改复制时,react 父状态更改.我该如何防止这种情况?

无法使用react-hook-form和react-places-autocomplete在字段中输入值

如何在 React Hook Form 中使用嵌套对象处理错误验证消息

当我在 React Router Dom 中使用两个参数并直接在页面上导航时,数据获取没有发生

如何让 useEffect 在 React.JS 中只运行一次?

Stripe Checkout:是否可以创建不自动续订的订阅?

如何到达类组件中的状态?

虽然通过了身份认证,但并没有导航到请求的页面

当每个元素都可拖动时,移动设备上的滚动列表出现问题

如何在 React map 函数循环中使用
标签

错误:无效挂钩调用.钩子只能在函数组件的主体内部调用.使用 next/router 时

如何从 extrareducer redux 更改对象中元素的值