具有 React Suspense 的组件会导致永恒循环

Component with React Suspense cause Eternal Loop

我正在学习 React,并尝试使用 Suspense。

首先,我尝试使用包装的 promise 对象作为道具。
包装器在未解决时抛出承诺。它永远循环。
然后我用useEffect试试,还是一样的问题

这是我的代码和沙箱。 请教我如何解决这个问题。

textsandbox EternalLoop
* useEffect 块注释在沙箱中被淘汰,因为它会永远循环。

import React, { Suspense, useEffect, useState } from "react";

const lazyTimer = () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(" from 10sec past");
    }, 1000);
  });
  return promise;
};

const wrapPromise = promise => {
  let status = "pending";
  let result;
  console.log("looping....");

  const suspender = promise.then(
    r => {
      status = "fulfilled";
      result = r;
    },
    e => {
      status = "rejected";
      result = e;
    }
  );
  const read = () => {
    if (status === "pending") {
      throw suspender;
    } else if (status === "rejected") {
      throw result;
    } else {
      return result;
    }
  };
  return { read };
};

const Hallo = () => {
  const [text, setText] = useState("your on time");
  // useEffect(
  //   setText(
  //     wrapPromise(lazyTimer()).read()
  //   ), [text],
  // );
  return <h2>hello world, {text}</h2>;
};

export default function App() {
  return (
    <div className="App">
      <Suspense fallback={<p>Loading...</p>}>
        <Hallo />
      </Suspense>
    </div>
  );
}

编辑 3:让我感到困惑的是我没有看到记录的悬念是它是如何工作的。 IE,children 是如何工作的。在我看来,您通过查看文档中的演示代码做出了承诺。但是我没有在任何地方看到这个记录。

所以你抛出一个承诺,当解析时你的组件现在已经准备好了(即定时器承诺 - 或者 Apollo 客户端承诺 - 获取承诺等)。当该承诺解决时,您的组件现在可以挂载。我喜欢它,如果我对它的工作原理是正确的,我希望它被清楚地记录下来。


本题与悬念无关。您的代码:

const Hallo = () => {
  const [text, setText] = useState("your on time");
  useEffect(
  //   setText(
  //     wrapPromise(lazyTimer()).read()
  //   ), [text],
  // );
  return <h2>hello world, {text}</h2>;
};

有这个问题。它运行 setText,然后它具有 text 作为依赖项。所以你改变 text,然后它再次运行,因为 text 改变了。因此无限循环。

你有 3 种方法来解决这个问题

1) 做一些 if 语句使其不是无限循环(即你知道什么文本不应该是或检查它是否相同)。

2) 从依赖列表中删除 text(不好!)

3) 使用替代方法 setText 将其删除,将其作为函数调用而不是提供值。有关文档,请参阅 here

   useEffect(
       setText(text => wrapPromise(lazyTimer()).read())
     ), [],
   );

那么text就不是依赖关系了。

我推荐3.


编辑:

最重要的是,您错误地使用了包装器。我看了看tutorial you probably used and its Github。他们创建包装器。然后在数据获取部分(您的计时器)将他们的承诺包装在承诺包装器中。

我尝试使用 useEffect:

使您的代码尽可能相似
import React, { Suspense, useEffect, useState } from "react";
import "./styles.css";

const wrapPromise = promise => {
  let status = "pending";
  let result;
  console.log(`looping.... + ${new Date().toString()}`);

  const suspender = promise.then(
    r => {
      status = "fulfilled";
      result = r;
    },
    e => {
      status = "rejected";
      result = e;
    }
  );
  const read = () => {
    if (status === "pending") {
      throw suspender;
    } else if (status === "rejected") {
      throw result;
    } else {
      return result;
    }
  };
  return { read };
};

const lazyTimer = () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(" from 10sec past");
    }, 10000);
  });
  return wrapPromise(promise);
};

const data = lazyTimer();

const Hallo = () => {
  const [text, setText] = useState(data.read());
  useEffect(() => {
    console.log("Im running");
    setText(data.read());
  }, []);

  return <h2>hello world, {text}</h2>;
};

export default function App() {
  return (
    <div className="App">
      <Suspense fallback={<p>Loading...</p>}>
        <Hallo />
      </Suspense>
    </div>
  );
}

这行得通。沙盒 here.

请注意,光环可能只是:

const Hallo = () => {
  const text = data.read()
  return <h2>hello world, {text}</h2>;
};