Компонент с React Suspense вызывает вечную петлю - PullRequest
0 голосов
/ 16 апреля 2020

Я изучаю React и пытаюсь использовать Suspense.

Сначала я пытаюсь использовать объект обернутого обещания для реквизита.
Обещание броска обертки, когда оно не решено. Это l oop навсегда.
Затем я пытаюсь сделать это с помощью useEffect, но у него та же проблема.

Вот мой код и песочница. Пожалуйста, расскажите мне, как решить эту проблему.

textandbox EternalL oop
* Комментарий блога 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>
  );
}

1 Ответ

1 голос
/ 16 апреля 2020

Правка 3: Что меня смущает, так как неизвестность, которую я не вижу в документации, - это то, как она работает. IE, как работают дети. Мне кажется, что вы бросаете обещание, глядя на код демонстрации в документации. Но я нигде не вижу этого документированного.

Таким образом, вы бросаете обещание, что после разрешения ваш компонент теперь готов (ie. Обещание таймера - или обещание клиента Apollo - получите обещание и т. Д. c.). Когда это обещание разрешится, ваш компонент сможет подключиться. Мне это нравится, я sh это было четко задокументировано, если я правильно понял, как это работает.


Эта проблема не имеет ничего общего с неизвестностью. Ваш код:

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 меняется. Следовательно, бесконечное l oop.

У вас есть 3 способа исправить это

1) Сделайте какое-нибудь утверждение if, чтобы сделать его не бесконечным l oop (ie). Вы знаете, какой текст не должен быть, или проверьте, совпадает ли он).

2) Удалите text из списка зависимостей (плохо!)

3) удалите его, используя альтернативный метод setText, вызывающий его как функцию вместо предоставления значения. См. здесь для документации.

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

, тогда text не будет зависимостью.

Я рекомендую 3.


Редактировать :

Кроме того, вы использовали оболочку неправильно. Я посмотрел учебник 1038 *, который вы, вероятно, использовали, и его 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>;
};
...