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

Во-первых, я пытаюсь использовать обернутый объект обещания для реквизита.
Обертка бросает обещание, когда оно не решено. Он повторяется вечно.
Затем я пробую использовать useEffect, но с той же проблемой.

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

Textandbox 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>
  );
}
0
MIsoku 16 Апр 2020 в 06:34

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>;
};
1
Diesel 19 Апр 2020 в 04:35