React не будет группировать вызовы setState внутри обработчика событий, если есть вызов до или между ними - PullRequest
2 голосов
/ 13 июня 2019

Я обнаружил это интересное поведение React, о котором мне хотелось бы узнать больше.

Обычно React отправляет несколько вызовов setState() внутри обработчика событий, верно?

НоЯ проверял, что React не будет пакетировать вызовы, если:

  • Функция обработчика событий - это функция async с вызовом await.
  • И это awaitвызов выполняет до или между вызовами setState().
    • Если await выполняется после вызовов setState(), они группируются как обычно.

ВОПРОС:

Знаете ли вы, в чем причина этого?


CodeSandbox: https://codesandbox.io/s/eventhandlerawaitso-jsdxs

Это mockAPI звонок

function mockAPI() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("I come from API using an AWAIT call");
      },500);
    });
  }

Это обработчики для проверки:

function handleClickNormal() {
    console.clear();

    console.log("Calling 1st setState()");
    updateBooleanState(false);

    console.log("Calling 2nd setState()");
    updateBooleanState(true);

    console.log("After 2nd setState()");
  }

  async function handleClickAwaitBefore() {
    console.clear();

    // AWAIT CALL RUNS BEFORE THE setState CALLS
    const fromAPI = await mockAPI();
    console.log(fromAPI);

    console.log("Calling 1st setState()");
    updateBooleanState(false);

    console.log("Calling 2nd setState()");
    updateBooleanState(true);

    console.log("After 2nd setState()");
  }

  async function handleClickAwaitAfter() {
    console.clear();

    console.log("Calling 1st setState()");
    updateBooleanState(false);

    console.log("Calling 2nd setState()");
    updateBooleanState(true);

    console.log("After 2nd setState()");

    // AWAIT CALL RUNS AFTER THE setState CALLS
    const fromAPI = await mockAPI();
    console.log(fromAPI);
  }

  async function handleClickAwaitBetween() {
    console.clear();

    console.log("Calling 1st setState()");
    updateBooleanState(false);

    // AWAIT CALL RUNS BETWEEN THE setState CALLS
    const fromAPI = await mockAPI();
    console.log(fromAPI);

    console.log("Calling 2nd setState()");
    updateBooleanState(true);

    console.log("After 2nd setState()");
  }

Вот результат:

enter image description here

Комментарии

Мы можем видеть, что setState() вызовы группируются, если есть нет await вызовов (Нажмите Обычный) и если await вызов приходит после setState() (Нажмите Ожидание после).

И что setState() вызовы НЕ пакетируются, если естьawait звоните до (нажмите "Ожидание до") или между setState() звоните (нажмите "Ожидание между").

Ответы [ 2 ]

1 голос
/ 13 июня 2019

Асинхронное программирование очень сильно связано со стеками вызовов и циклами событий.
Вы можете найти много об этом в этом видео: https://www.youtube.com/watch?v=8aGhZQkoFbQ

Таким образом, если между вами await с, setState с попадет в разные стеки.
Я полагаю, что это главная причина, почему реакция не объединяет их.

0 голосов
/ 13 июня 2019

Во-первых, давайте посмотрим, что Дан Абрамов говорит о этом вопросе :

В текущем выпуске они будут объединены, если вы находитесь внутри обработчика событий React. React упаковывает все setStates, выполненные во время обработчика событий React, и применяет их непосредственно перед выходом из собственного обработчика событий браузера.

В текущей версии несколько setStates вне обработчиков событий (например, в сетевых ответах) не будут пакетироваться. Таким образом, вы получите два повторных рендера в этом случае.

Хммм, так что мы можем сделать setState из обработчика событий React и вне его. Обработчик события React - это функция, которую мы передаем в качестве опоры компоненту.

Если мы возьмем handleClickAwaitBefore и перепишем его без асинхронного ожидания, мы получим что-то вроде этого:

 function handleClickAwaitBefore() {
    console.clear();

    // Here we are in event handler and setState is batched
    mockAPI().then(function notEventHandlerAnyMore(){

       // Here we are in totally different function and setState is not batched any more
       console.log(fromAPI);

       console.log("Calling 1st setState()");
       updateBooleanState(false);

       console.log("Calling 2nd setState()");
       updateBooleanState(true);

       console.log("After 2nd setState()"); 
    })

  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...