19 сентября

React 17: Ничего нового?

JavaScriptReactJS
Перевод
Автор оригинала: Dan Abramov and Rachel Nabors

Disclamer

Это вольный перевод оригинальной статьи из официального блога. Почему вольный? Скорее потому, что в оригинале слишком много воды и отсылок к причинам тех или иных принятых в прошлом решений.

Ничего нового?

Семнадцатый релиз React необычен отсутствием новых фич и/или функционала. Этот релиз сфокусирован на добавлении возможности постепенного обновления проектов на следующие мажорные версии библиотеки в будущем, что актуально для проектов с большой кодовой базой.

Многообещающий Concurrent Mode не будет представлен в 17 версии, как и другие нововведения, над которыми активно работает команда. Грядущий релиз является частью стратегии постепенных (частичных) обновлений.

Постепенные обновления

В течение последних 7 лет обновления React были в духе "все или ничего". Либо обновляемся до новой версии, либо остаемся на старой. Порой, необходимо было менять что-то в кодовой базе, как, например будет с устаревшим Context API, который не получится перенести автоматически.

React 17 привносит стратегию постепенных обновлений. Команда чинит большое количество имеющихся проблем в React 17. Это позволяет в следующей версии библиотеки иметь больше опций при обновлении.

Первый из вариантов будет таким же как и прежде: обновляем все приложение разом. Но теперь будет опция обновить приложение по частям. Например, большую часть приложения легко перетащить на новую версию (React 18, или React 19), тогда как какое-нибудь загруженное через lazy-подход диалоговое окно или часть приложения под определенном роутом можно будет оставить на React 17.

Команда приготовила репозиторий, который демонстрирует как загружать асинхронно (lazy-load) более старые версии React. В примере используется приложение на React 17.0.0-rc.0, которое подгружает компоненты, написанные с помощью устаревших приемов, работающие на React 16.8

Список изменений в React 17

Изменение в делегировании событий

Реакт с самой его первой версии меняет способ привязки событий, например onClick, к DOM-элементам. Реакт автоматически использует прием делегирования событий и привязывает все события к объекту document. Таким образом, достигается повышение производительности.

Однако, в случае, если несколько версий React используется на странице, у вас микрофронтенды или все еще часть функционала работает на jQuery, возникают проблемы. Такое поведение ломает event.stopPropagation(): если вложенное дерево остановило распространение (propagation) события, внешнее дерево все равно получит его. Это делает сложным работу в случае вложенных нескольких версий React. Команда популярного редактора Atom столкнулась с такой проблемой.

Теперь все обработчики крепятся к корневому элементу, а не объекту document:

const rootNode = document.getElementById('root'); // <-- вот сюда
ReactDOM.render(<App />, rootNode);

Убран костыль с Синтетическим Событием (SyntheticEvent Even Pooling)

В 17 Реакте убрана оптимизация событий, которая более не актуальна в современных браузерах.

function handleChange(event) {
  // это работает в 16 React только если добавить event.persist()
  setData(data => ({
    ...data,
    // This crashes in React 16 and earlier:
    text: event.target.value
  }));
}

Теперь такой код не будет валиться с ошибкой, и нет нужды писать event.persist()

Для обратной совместимости эта функция оставлена в качестве заглушки. Разработчики попробовали это на существующем коде в Facebook и увидели отсутствие регрессий. Возможно, это обновление еще и исправило какое-то количество багов!

Ближе к браузерам

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

  • Событие onScroll больше не всплывает, чтобы избежать текущей путаницы;

  • События onFocus и onBlur изменены "под капотом" на нативные focusin и focusout;

  • onClickCapture и другие Capture-события теперь используют браузерные обработчики событий.

useEffect() теперь полностью асинхронный

useEffect(() => {
  // This is the effect itself.
  return () => {
    // This is its cleanup.
  };
});

Если раньше только функция эффекта запускалась асинхронно, тогда как возвращаемая функция для очистки подписок запускалась синхронно, так же как и метод componentWillUnmount(), то теперь и эта функция работает асинхронно, что улучшает производительность в случае перерисовки большого количества элементов, например, когда на сайте присутствуют вкладки, или при переходе со страницы на страницу.

Для синхронной работы, можно по-прежнему использовать useLayoutEffect(), который остался незатронутым.

Ошибки при возвращении undefined

Это изменение довольно минорно, но повышает консистентность ошибок.

Если раньше нельзя было возвращать undefined только в обычных компонентах, то теперь такая ошибка будет выбрасываться еще и в React.forwardRef и React.memo.

let Button = forwardRef(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
});

let Button = memo(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
});

Улучшенный стек вызовов при ошибках

Когда выбрасывается ошибка, браузер показывает stack trace с названиями функций и их местоположения. К сожалению, довольно трудно по этому стеку вызовов понять, в каком месте и в каком компоненте была вызвана ошибка на самом деле.

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

В 17 React используется новых механизм по генерации стека вызовов, что позволяет увидеть дерево React-компонентов, которое привело к ошибке, даже в production-среде.

Удаление приватных экспортов

Последнее изменение — это удаление некоторых внутренних экспортов, которые ранее были открыты наружу. Например, React Native for Web ранее зависела на некоторых внутренностях системы событий, но эта зависимость не была надежной.

В 17 React эти приватные экспорты были удалены. По большей части из-за того, что только вышеупомянутый проект их использовал, а миграция на более надежные методы уже была произведена.

Также был удалены ReactTestUtils.SimulateNative методы. Они не были документированы, но теперь их не будет вовсе.

Changelog

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

В новой версии React также включено 5 изменений в React, 37 изменений в React Dom, пару изменений в React DOM Server, одно изменение в React Test Rerender.

А что с Concurrent Mode?

По-прежнему, этот режим имеет статус экспериментального. В 17 React было исправлено множество багов, удалены одни unstable_ методы, и добавлены новые. Пока для продакшена использовать его рано, но потыкать определенно можно и нужно. Например, есть библиотека для работы с Firebase, reactfire, разработчики которой сделали основную версию зависимой от Concurrent Mode. К сожалению, репозиторий кажется заброшенным последние несколько месяцев. Надеюсь, это исправится.

Теги:ReactJavascriptreactjsReact 16React 17
Хабы: JavaScript ReactJS
+23
10,1k 26
Комментарии 13
Лучшие публикации за сутки

Минуточку внимания

Разместить