Когда срабатывает обратный вызов в useEffect?

Я читал, что useEffect является асинхронным, поэтому React сначала рендерит и монтирует страницу и только после обратного вызова в useEffect срабатывает, поэтому иногда мы видим очень быстрое изменение значения в некоторых компонентах.

Но когда я попытался использовать самодельный «сон на 3 секунды» в обратном вызове useEffect, React ждет, пока не пройдет 3 секунды, и только после этого рендерит и монтирует страницу, поэтому я вижу пустую страницу в течение 3 секунд. Также мы можем заметить быстро меняющееся значение после задержки в 3 секунды. Я не понимаю, почему это происходит, так как обратный вызов useEffect должен запускаться после рендеринга и монтирования страницы.

Не могли бы вы объяснить, почему это происходит именно так?

Песочница кода:Связь

Код:

import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [state, setState] = useState(1200000000);
  useEffect(() => {
    let start = new Date().getTime();
    let end = start;
    while (end < start + 3000) {
      end = new Date().getTime();
    }
    setState(3);
  }, []);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <div>Value is : {state}</div>
    </div>
  );
}

ну, ваше «занятое ожидание» полностью блокирует браузер от каких-либо действий в течение 3 секунд, включая обновление DOM. Хотя useEffect запускается после рендеринга React, я думаю, ваш пример доказывает, что фактический DOM еще не обновлен. Не уверен, что это имеет значение, потому что (надеюсь!) вы явно никогда не поместите такой синхронный блокирующий код в реальное приложение!

Robin Zigmond 10.04.2022 18:07
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
1
1
41
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

I've read, that useEffect is asynchronous, so React firstly renders and mounts page and only after callback in useEffect fires, so sometimes we see value changing really fast in some components.

Верный. Это задокументировано здесь:

The function passed to useEffect will run after the render is committed to the screen.

и здесь

Timing of effects

Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event.

(их ударение)

Продолжая свой вопрос:

But when I tried to use self-created "sleep-for-3-seconds" in useEffect callback, React waits until 3 seconds pass, and only after that renders and mounts page, so I see blank page for 3 seconds.

Странно, я не вижу такого поведения:

const { useState, useEffect} = React;

function App() {
    const [state, setState] = useState(1200000000);
    useEffect(() => {
        let start = new Date().getTime();
        let end = start;
        while (end < start + 3000) {
            end = new Date().getTime();
        }
        setState(3);
    }, []);
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <div>Value is : {state}</div>
      </div>
    );
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Когда я запускаю это, я вижу страницу, отображаемую с начальным значением состояния 1200000000, а через три секунды я вижу, что она обновляется с измененным значением состояния.

Я также не вижу этого в этом более простом примере:

const {useEffect} = React;

const Example = () => {
    useEffect(() => {
        const stop = Date.now() + 3000;
        while (Date.now() < stop) {
            // Wait (NEVER DO THIS IN REAL CODE)
        }
        console.log("Done waiting");
    }, []);
    return <div>x</div>;
};

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Когда я запускаю это, я вижу x, отображаемый компонентом, а через три секунды я вижу console.log, что ожидание завершено.

Но, если вы не видите то, что описываете, это может быть просто потому, что у браузера не было возможности фактически отрисовать страницу до вызова useEffect, несмотря на то, что код React пытается убедиться, что это так. И поскольку ваш код занят ожиданием, основной поток связан с этим циклом, и браузер не может обновить отображение страницы.

Но опять же, я не вижу этого с useEffect (вы бы видели с useLayoutEffect).

Спасибо за обстоятельный ответ! Я вижу, что в вашем фрагменте кода useEffect работает прилично, но в песочнице (Ссылка на песочницу) он ждет 3 секунды и только после этого отрисовывает страницу. После некоторого обновления страницы (не локальной страницы, отображаемой с кодом, а всей страницы песочницы) она работает редко, как ожидалось, но в большинстве случаев useEffect работает так, как я написал в вопросе :(

MichaelLearner 10.04.2022 18:33

@MichaelLearner - Вероятно, это проблема CodeSandbox, там много движущихся частей, браузер может быть не в состоянии полностью обновить пользовательский интерфейс. :-)

T.J. Crowder 10.04.2022 19:11

То, что вы видите пустой экран, соответствует коду.

Поскольку фрагмент кода, который вы используете, является сценарием ожидания занятости и делает другие ваши коды javascript остановленными, а пользовательский интерфейс замороженным.

А это нехорошо с точки зрения пользовательского опыта и производительности.

    let start = new Date().getTime();
    let end = start;
    while (end < start + 3000) {
      end = new Date().getTime();
    }

Таким образом, вы можете сделать это следующим образом:

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

useEffect(() => {
    delay(3000).then(res => {
      setState(3);
    });
  }, []);

На самом деле, согласно документация, React по крайней мере приложил усилия, чтобы позволить браузеру отображать компонент перед вызовом обратного вызова useEffect.

T.J. Crowder 10.04.2022 18:19

Да, React визуализирует ваше тело, а затем запускает ваш useEffect хук после того, как DOM готов, потому что ваш хук имеет зависимость от пустого массива []. Но в этом хуке выполняется сценарий занятости-ожидания (блокировки). Во время этого цикла все другие пользовательские интерфейсы, которые должны отображаться после этого компонента, будут остановлены.

Liki Crus 10.04.2022 18:28

Я не уверен, что вы имеете в виду. С кодом OP они должны видеть нарисованную страницу, потому что React предположительно фиксирует и ждет отрисовки перед вызовом useEffect (и, следовательно, перед ожиданием занятости). Я не могу воспроизвести то, что описывает OP, для меня он всегда отображает страницу (затем застревает в режиме ожидания). Хотя, по сути, в любом случае проблема заключается в напряженном ожидании.

T.J. Crowder 10.04.2022 18:30

Другие вопросы по теме