0

我正在学习 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>
  );
}
4

1 回答 1

1

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

所以你抛出一个承诺,当解决时你的组件现在已经准备好了(即计时器承诺 - 或 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方法将其删除,将其作为函数调用而不是提供值。有关文档,请参见此处

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

那么text将不会是依赖项。

我推荐3。


编辑:

最重要的是,您错误地使用了包装器。我查看了您可能使用的教程及其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>
  );
}

这行得通。沙箱在这里

值得注意的是,光环可能只是:

const Hallo = () => {
  const text = data.read()
  return <h2>hello world, {text}</h2>;
};
于 2020-04-16T04:23:11.673 回答