Автоматическое дозирование в React 18

Основная команда React недавно выпустила альфа-версию React 18, которая включает готовые улучшения существующих функций, таких как:

  • Автоматическое пакетирование для меньшего количества рендеров
  • Поддержка SSR для Suspense
  • Исправлены причуды неопределенного поведения.

В этом блоге мы поговорим о том, что такое пакетная обработка, как она работала раньше и что изменилось.

Что такое пакетная обработка?

Пакетная обработка – это когда React группирует несколько обновлений состояния в один повторный рендеринг для повышения производительности .

Давайте посмотрим на это на примере:
предположим, у нас есть два обновления состояния внутри обработчика кликов по событию. React объединит эти два обновления и отобразит только один раз.

function App() {
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);

  const clickHandler = () => {
    setCount((count) => count + 1); // Does not re-render here
    setToggle((toggle) => !toggle); // Does not re-render here
    // React will only re-render once at the end(and that's batching)
  };

  console.log("Rendered ", count, toggle);

  return (
    <div>
      <button onClick={clickHandler}>Click Me</button>
      <h1>Count: {count}</h1>
      <h1>Toggle: {toggle.toString()}</h1>
    </div>
  );
}

В приведенном выше примере представление будет отображаться только один раз после нажатия кнопки.

автоматическое дозирование реагировать 17.JPG

React выполняет автоматическое пакетирование для нас в случае обработчиков событий. Достаточно умен, чтобы объединить два обновления в одно, что приведет только к одному рендерингу и, следовательно, к повышению производительности приложения.

💻 Демо: пакетные обновления внутри обработчиков событий в React 17

Проблема с дозированием

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

НО, есть одна проблема. React несовместим с пакетными обновлениями.

Давайте посмотрим на пример:
предположим, нам нужно получить данные или использовать setTimeout, а затем обновить состояние в соответствии с результатом. Что ж, в этом случае React не будет пакетировать обновления и выполнять два независимых обновления состояния, следовательно, запускать два рендера.

function App() {
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);

  const clickHandler = () => {
    setTimeout(() => {
      setCount((count) => count + 1);
      setToggle((toggle) => !toggle);
    }, 100);
  };

  console.log("Rendered ", count, toggle);

  return (
    <div>
      <button onClick={clickHandler}>Click Me</button>
      <h1>Count: {count}</h1>
      <h1>Toggle: {toggle.toString()}</h1>
    </div>
  );
}

В приведенном выше примере приложение будет повторно отрисовывать после, setCountа затем снова после того, как в setToggleрезультате будет выполнено два отрисовки.

проблема с пакетной реакцией 17.JPG

💻 Демо: обновления внешних обработчиков событий НЕ пакетируются в React 17

До React 18 мы пакетировали обновления только во время обработчиков событий React. Обновления внутри обещаний, setTimeout, собственных обработчиков событий или любого другого события по умолчанию не пакетировались в React.

Решение

Основная команда React придумала идею автоматической пакетной обработки для React 18, что означает, что теперь все обновления будут автоматически пакетироваться , независимо от их происхождения. Итак, теперь обновления внутри тайм-аутов или сетевых вызовов будут обрабатываться так же, как и события.

Примечание. Ожидается, что вы установили React 18 Alpha и включили параллельный режим .

Давайте возьмем тот же пример и попробуем его с React 18:

function App() {
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);

  const clickHandler = () => {
    setTimeout(() => {
      setCount((count) => count + 1);
      setToggle((toggle) => !toggle);
    }, 100);
  };

  console.log("Rendered ", count, toggle);

  return (
    <div>
      <button onClick={clickHandler}>Click Me</button>
      <h1>Count: {count}</h1>
      <h1>Toggle: {toggle.toString()}</h1>
    </div>
  );
}

Теперь, с React 18, оба обновления будут автоматически пакетироваться, и приложение будет отображаться только один раз.

автоматическое дозирование реагировать 18.JPG

💻 Демо: пакетные обновления вне обработчиков событий в React 18

Но подождите, а что, если вы не хотите выполнять пакетную обработку?

Бывают случаи, когда вы не захотите автоматически пакетировать обновления.

Что ж, на помощь приходит flushSync .

Мы можем использовать, ReactDOM.flushSync() чтобы отказаться от пакетной обработки.

function App() {
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);

  const clickHandler = () => {
    flushSync(() => {
      setCount((count) => count + 1);
    });
    // React has updated the DOM by now
    flushSync(() => {
      setToggle((toggle) => !toggle);
    });
    // React has updated the DOM by now
  };

  console.log("Rendered ", count, toggle);

  return (
    <div>
      <button onClick={clickHandler}>Click Me</button>
      <h1>Count: {count}</h1>
      <h1>Toggle: {toggle.toString()}</h1>
    </div>
  );
}

В приведенном выше коде мы использовали flushSync()для обновления setCountи setToggle независимо друг от друга получили два рендера.

flushSync.JPG

💻 Демонстрация: использование flushSync для независимого обновления состояний в React 18

TL; DR

До React 18 React пакетировал обновления только внутри обработчиков событий. Никакие другие обновления из обещаний, setTimeout или любого другого события не были пакетированы.

Начиная с React 18, react будет автоматически пакетировать все обновления, независимо от их происхождения.

Если вы хотите отказаться от пакетной обработки в React 18, вы можете добиться этого, используя flushSync().

Спасибо, что прочитали этот блог. Это мой первый блог, поэтому я очень нервничаю по поводу его публикации.

Ответить