React 17 что нового
Что нового в React v17 и путь к v18
Несколько недель назад команда React выпустила последнюю версию библиотеки React v17.0 RC. В этом посте мы рассмотрим новые изменения и обновления, с которыми поставляется этот новый выпуск.
UX для обновления версий и путь к v18
Эта новая версия уникальна тем, что не содержит видимых новых функций. Вместо этого он полностью сосредоточен на обновлении React в целом.
С момента появления React всегда существовал процесс установки новой версии, в котором вы должны выбрать, обновлять ли свою версию для использования новых функций или продолжать использовать старые версии без каких-либо промежуточных действий. Например, Context API устарел, и хотя большинство приложений React не используют его, React по-прежнему поддерживает его.
Продолжаются дискуссии о продолжении поддержки или отказе от приложений, использующих старые версии. Таким образом, в дальнейшем эта стратегия обновления была переосмыслена так, чтобы включать более старые версии, и, как таковая, новая версия 17 позволяет постепенное обновление React.
Команда React в основном пыталась сделать так, чтобы в дальнейшем переход с одной версии React на другую был простым и плавным. В версии 17 вы получаете ступеньку, которая гарантирует, что, например, даже безопаснее встроить дерево, которым вы управляете в одной версии, в другое дерево, управляемое совершенно другой версией.
Речь идет просто о предоставлении большего количества вариантов обновления, потому что раньше, если вы обновлялись с одной версии React до более новой, это обновляло все приложение. В некоторых случаях, когда у вас может быть две версии React в одном проекте, это вызывает множество проблем с событиями.
Это может быть не идеальный путь, но он пригодится разработчикам, которые поддерживают большие проекты, и эта новая версия гарантирует, что эти приложения больше не останутся без внимания.
Изменения в делегировании событий
React обрабатывает обработчики событий таким образом, что если вы поместите один встроенный на кнопку, как это:
Эквивалент vanilla JS при компиляции будет выглядеть так:
Затем React прикрепляет один обработчик для каждого типа события непосредственно к узлу документа, а не прикрепляет его к узлам DOM, на которых они были объявлены. Это называется делегированием событий, и это очень полезно для больших приложений и процессов, таких как воспроизведение событий.
Учитывая постепенные обновления, о которых мы только что говорили в предыдущем разделе, вложенные приложения, созданные с использованием разных версий React, также пришлось переосмыслить. В этой новой версии React обработчики событий больше не будут прикрепляться на уровне документа; скорее, они будут прикреплены к контейнеру DOM, в котором было отрисовано дерево.
С этим изменением теперь можно безопасно вкладывать приложения, созданные с использованием разных версий React, хотя это будет начинаться с версии 17 и выше.
Критические изменения
В эту новую версию внесено несколько критических изменений. Команда позаботилась о том, чтобы они были минимальными; некоторые из них просто поведенческие, но их все же важно отметить. Так что такие вещи, как устаревшие методы в предыдущих выпусках, по-прежнему доступны.
Делегирование мероприятия
У вас могут возникнуть проблемы с новыми изменениями в делегировании событий.
В этой новой версии e.stopPropagation() фактически остановит выпуск вашего обработчика документов:
Таким образом, мы видим, что делегирование событий теперь ближе к обычной модели DOM, чем когда-либо прежде.
Выравнивание браузера
В систему событий React было внесено несколько изменений, некоторые из которых включают:
Эти несколько изменений позволяют более точно согласовать React с поведением браузеров и даже улучшить взаимодействие.
Нет пула событий
Начиная с этой новой версии, оптимизация пула событий была удалена из React из-за продолжающейся путаницы и простого факта, который не улучшает производительность современного браузера.
Команда React называет это изменением поведения и называет это нарушением, хотя они не видели, чтобы это что-то ломало в Facebook, поэтому шансы действительно низкие. Кроме того e.persist() по-прежнему доступен для объектов событий, хотя ничего не делает.
Время очистки эффекта
Эта новая версия также делает синхронизацию функции очистки хука useEffect более последовательной.
В React 16 функция очистки эффекта запускается синхронно, в отличие от большинства эффектов, которые не задерживают обновления экрана и которые по умолчанию React запускаются асинхронно. Команда React обнаружила, что этот синхронный процесс не так идеален, как и в случае с componentWillMount для больших приложений, когда пользователь переключает вкладки.
Итак, эта новая версия вносит некоторые изменения. Функция очистки эффекта теперь будет выполняться асинхронно, как и другие, и, если компонент отключается, очистка будет запущена только после того, как обновления отобразятся на экране.
Последовательные ошибки при возврате undefined
Разработчики React знают, что функции, возвращающие undefined, помечаются ошибкой:
В основном из-за того, насколько легко непреднамеренно вернуть undefined:
Обратите внимание, что для случаев, когда вы ничего не визуализируете намеренно, n«u«ll будет возвращено вместо этого.
Удаление частного экспорта
Некоторые внутренние компоненты React подвергались воздействию других несвязанных проектов, особенно React Native для Интернета, который раньше зависел от внутренних компонентов системы событий, которая была очень хрупкой и легко ломалась. Теперь эти частные экспорты удалены, в этой новой версии React, для них было предложено другое решение, которое не позволяет им зависеть от этих внутренних компонентов.
Это изменение просто означает, что более старые версии RN для веб-версий не будут совместимы с React v17, но более новые версии будут.
Как обновить
Рекомендуется использовать эту версию для тестовых проектов, так как в ближайшем будущем появятся более стабильные версии. Вы можете поднять любые проблемы, которые могут возникнуть при использовании или обновлении для использования React v17 RC, на GitHub.
Чтобы установить с помощью npm, запустите:
Чтобы установить с помощью Yarn, запустите:
У команды также есть UMD-сборки React через CDN:
Вы можете прочитать документацию для получения подробных инструкций по установке.
Вывод
На этом мы подошли к завершению нашего обзора. Хотя в React 17 не было никаких новых функций, он создает прецедент для версии 18, напрямую обращаясь к опыту обновления и более точно согласовывая поведение React с современными браузерами. Удачного взлома!
React v17.0
Today, we are releasing React 17! We’ve written at length about the role of the React 17 release and the changes it contains in the React 17 RC blog post. This post is a brief summary of it, so if you’ve already read the RC post, you can skip this one.
The React 17 release is unusual because it doesn’t add any new developer-facing features. Instead, this release is primarily focused on making it easier to upgrade React itself.
In particular, React 17 is a “stepping stone” release that makes it safer to embed a tree managed by one version of React inside a tree managed by a different version of React.
It also makes it easier to embed React into apps built with other technologies.
React 17 enables gradual React upgrades. When you upgrade from React 15 to 16 (or, this time, from React 16 to 17), you would usually upgrade your whole app at once. This works well for many apps. But it can get increasingly challenging if the codebase was written more than a few years ago and isn’t actively maintained. And while it’s possible to use two versions of React on the page, until React 17 this has been fragile and caused problems with events.
We’re fixing many of those problems with React 17. This means that when React 18 and the next future versions come out, you will now have more options. The first option will be to upgrade your whole app at once, like you might have done before. But you will also have an option to upgrade your app piece by piece. For example, you might decide to migrate most of your app to React 18, but keep some lazy-loaded dialog or a subroute on React 17.
This doesn’t mean you have to do gradual upgrades. For most apps, upgrading all at once is still the best solution. Loading two versions of React — even if one of them is loaded lazily on demand — is still not ideal. However, for larger apps that aren’t actively maintained, this option makes sense to consider, and React 17 lets those apps not get left behind.
We’ve prepared an example repository demonstrating how to lazy-load an older version of React if necessary. This demo uses Create React App, but it should be possible to follow a similar approach with any other tool. We welcome demos using other tooling as pull requests.
We’ve postponed other changes until after React 17. The goal of this release is to enable gradual upgrades. If upgrading to React 17 were too difficult, it would defeat its purpose.
Changes to Event Delegation
To enable gradual updates, we’ve needed to make some changes to the React event system. React 17 is a major release because these changes are potentially breaking. You can check out our versioning FAQ to learn more about our commitment to stability.
In React 17, React will no longer attach event handlers at the document level under the hood. Instead, it will attach them to the root DOM container into which your React tree is rendered:
In React 16 and earlier, React would do document.addEventListener() for most events. React 17 will call rootNode.addEventListener() under the hood instead.
We’ve confirmed that numerous problems reported over the years on our issue tracker related to integrating React with non-React code have been fixed by the new behavior.
If you run into issues with this change, here’s a common way to resolve them.
Other Breaking Changes
The React 17 RC blog post describes the rest of the breaking changes in React 17.
We’ve only had to change fewer than twenty components out of 100,000+ in the Facebook product code to work with these changes, so we expect that most apps can upgrade to React 17 without too much trouble. Please tell us if you run into problems.
React 17 supports the new JSX transform. We’ve also backported support for it to React 16.14.0, React 15.7.0, and 0.14.10. Note that it is completely opt-in, and you don’t have to use it. The classic JSX transform will keep working, and there are no plans to stop supporting it.
React Native has a separate release schedule. We landed the support for React 17 in React Native 0.64. As always, you can track the release discussions on the React Native Community releases issue tracker.
To install React 17 with npm, run:
To install React 17 with Yarn, run:
We also provide UMD builds of React via a CDN:
Предварительная версия React 17: обошлись без новой функциональности
Оригинальная статья опубликована в блоге React, повествование в переводе ведётся от имени авторов оригинала Дэна Абрамова (Dan Abramov) и Рэйчел Нэйборс (Rachel Nabors).
10 августа мы выпустили первый релиз-кандидат (предварительную версию) React 17. С момента последнего мажорного обновления React прошло 2.5 года, а это много по нашим стандартам. В этой статье расскажем, какова роль этого обновления, каких изменений от него ожидать и как протестировать предварительную версию React 17.
Обошлись без новой функциональности
Версия React 17 необычная, потому что в ней нет каких-либо новых возможностей для разработчиков. Вместо этого новая версия позволяет удобнее обновлять сам React.
Мы активно работаем над новыми возможностями, но они не войдут в эту версию. React 17 — часть нашей стратегии по внедрению новой функциональности. Нам важно, чтобы она была доступна всем пользователям независимо от версии React, которую они используют.
В частности, React 17 — «релиз-ступень», которая обеспечивает безопасное встраивание дерева, управляемого одной версией React, в дерево, которое управляется другой версией React.
Постепенные обновления
В последние 7 лет обновления версий React работали по принципу «всё или ничего». Вы либо работаете со старой версией, либо обновляете приложение, и оно полностью работает на новой версии. Промежуточных вариантов не было.
Пока это работает, но стратегия «всё или ничего» ограничивает нас. Некоторые изменения API, например, отказ от устаревшего API Context, невозможно выполнить автоматически. Большинство написанных в последнее время приложений не используют устаревшие API, но мы всё равно поддерживаем их. Мы вынуждены выбирать межу поддержкой устаревшей функциональности в React и необходимостью навсегда оставить некоторые приложения на старых версиях React. Оба варианта неудачные.
Поэтому мы сделали новую опцию.
React 17 позволяет обновляться постепенно. Когда вы обновляетесь с версии 15 на 16 (или, в будущем, с версии 16 на 17), обычно вы обновляете всё приложение. Это отлично работает во многих случаях. Но такой подход может стать проблемой, если кодовая база была написана несколько лет назад и не поддерживалась. Использовать разные версии React на одной странице можно было и раньше. Но до React 17 такой подход делал код хрупким и вызывал проблемы с событиями.
Мы решили многие из этих проблем с помощью React 17. То есть после выхода React 18 и других версий в будущем у вас появится больше возможностей. Одна из них — обновлять приложение полностью, как это происходило раньше. Но у вас появится и возможность обновлять приложение по частям. Например, вы сможете обновить приложение до будущей версии React 18, но оставить на React 17 некоторые диалоги или вложенные роуты с отложенной загрузкой.
Это не значит, что вы должны использовать постепенные обновления. Лучшей стратегией для большинства приложений по-прежнему будет одномоментный переход на новую версию. Загрузку двух версий React нельзя назвать хорошим идеальным решением, даже если одна из них загружается лениво по требованию. Но для больших приложений, которые не поддерживаются активно, этот подход имеет смысл. А React 17 позволяет таким приложениям не устаревать безнадёжно.
Чтобы использовать поэтапное обновление, вам нужно внести некоторые изменения в систему обработки событий React. React 17 — мажорный релиз, так как реализованные в нём изменения потенциально критические. На деле нам пришлось изменить менее чем 20 компонентов из более чем 100 тыс. Поэтому мы ожидаем, что большинство приложений смогут мигрировать на новую версию без серьёзных проблем. Сообщите нам, если вы всё-таки столкнулись с проблемами при обновлении.
На Хекслете курс по React входит в профессию «Фронтенд JavaScript». После регистрации базовые курсы в профессии, включая «Введение в программирование», «Основы командной строки», «Настройка окружения», «Системы контроля версий», можно пройти бесплатно.
Пример постепенных обновлений
Мы подготовили демо-репозиторий, в котором можно посмотреть, как при необходимости может быть реализована ленивая загрузка старой версии React. В этом репозитории используется бойлерплейт Create React App, но вы можете использовать любой другой инструмент. Пулреквесты с примерами использования других инструментов приветствуются.
Важно
Другие изменения мы отложили, они появятся после релиза React 17. Цель React 17 — сделать возможными постепенные обновления. Если обновление до React 17 было бы сложным, мы не достигли бы поставленной цели.
Изменения делегирования событий
Создание приложений с использованием разных версий React всегда было технически возможным. Однако такой подход был довольно хрупким из-за внутреннего устройства системы обработки событий.
Обычно вы вешаете обработчики событий на элементы в React-компонентах так:
Эквивалентный код на чистом JavaScript выглядит так:
Однако это приводит к проблемам при постепенном обновлении.
Если вы используете на странице несколько версий React, каждая из них регистрирует обработчики событий на верхнем уровне. Это нарушает e.stopPropagation() : если вложенное дерево остановило всплытие события, внешнее дерево всё ещё получает его. Из-за этого сложно использовать разные версии React одновременно. Это не гипотетический пример — разработчики редактора Atom столкнулись с этой проблемой на практике несколько лет назад.
Именно поэтому мы поменяли внутреннее устройство привязки событий в React 17.
Благодаря этим изменениям теперь стало безопаснее встраивать React-дерево под управлением одной версии библиотеки внутрь дерева, которым управляет другая версия библиотеки. Чтобы это работало, обе части приложения должны использовать React 17 и выше. Поэтому обновление до React 17 играет важную роль. Говоря иначе, новый релиз — необходимая ступень, которая сделает возможными постепенные обновления.
Также описанные выше изменения позволяют проще встраивать React в приложения, созданные с использованием других технологий. Например, если внешняя «оболочка» вашего приложения написана на jQuery, но внутри более новый код написан на React, e.stopPropagation() внутри React-кода не даст событию достигнуть jQuery-кода, как вы и ожидаете. Это работает и в обратном направлении. Если вам больше не нравится React и вы хотите переписать приложение, например, на jQuery, можете переписывать внешнюю «оболочку» с React на jQuery, не опасаясь нарушить всплытие событий.
Мы подтвердили, что многочисленные проблемы, связанные с интеграцией React с другими технологиями, решаются благодаря новому поведению.
Важно
Вы можете спросить, как обновление повлияло на использование порталов за пределами рут-контейнера. React также отслеживает события, привязанные к контейнерам порталов, поэтому проблем здесь нет.
Устранение потенциальных проблем
Как и в других случаях выхода критических обновлений, возможно, вам придётся скорректировать код приложений. В Facebook нам пришлось изменить около 10 модулей, а всего у нас много тысяч модулей.
Вы можете решить проблему, если привяжете прослушиватель к стадии погружения. Чтобы сделать это, надо передать < capture: true >третьим аргументом в document.addEventListener :
Обратите внимание, насколько в целом гибкий этот подход. Например, использование стадии погружения вероятно исправит ошибки в вашем коде, которые происходят при вызове e.stopPropagation() вне обработчика событий. Иными словами, всплытие событий в React 17 больше похоже на всплытие событий в DOM.
Другие критические изменения
Мы минимизировали количество критических обновлений в React 17. Например, в новой версии сохраняются все методы, которые были объявлены устаревшими в предыдущих версиях. Однако в React 17 есть другие критические обновления. Наш опыт показал относительную безопасность таких обновлений. В целом, нам пришлось изменить код менее чем в 20 модулях из более чем 100 000 тысяч.
Согласованность с браузерами
Мы внесли несколько изменений в систему событий:
Эти изменения согласовывают поведение React с поведением браузеров и улучшают их взаимодействие.
Важно
Отказ от использования пулов событий
В React 17 мы отказались от оптимизации с помощью объединения событий в пулы. Она не улучшает производительность в современных браузерах и создаёт проблемы даже для опытных React-разработчиков.
В React 17 этот код работает так, как вы ожидаете. Мы полностью отказались от использования пулов событий, поэтому вы можете обратиться к полям событий в любое время, когда это необходимо.
Это изменение поведения, поэтому мы отметили это обновление как критическое. На практике мы не увидели, чтобы оно вызвало какие-то проблемы в коде Facebook. Возможно, обновление даже исправило какие-то ошибки. Заметьте, что e.persist() всё так же доступен в объекте события React, но сейчас он ничего не делает.
Тайминг сброса эффектов
Мы сделали тайминг функции сброса useEffect более согласованным.
В React 16 функция сброса эффекта, если она существует, выполняется асинхронно. Мы заметили, что это неидеально для больших приложений, так как замедляет переходы на больших экранах, например, при переключении вкладок.
В React 17 функция сброса эффекта тоже запускается асинхронно. Например, если компонент размонтируется, функция сброса выполнится после обновления экрана.
Важно
Значит ли это, что теперь невозможно исправлять предупреждения о setState на размонтированных элементах? Не переживайте, React специально проверяет это и не запускает предупреждения о setState в промежутке между размонтированием и сбросом. Поэтому запросы отмены или интервалы почти всегда остаются одинаковыми.
Также React 17 выполняет функции сброса в таком же порядке, что и эффекты — в соответствии с их расположением в дереве. В более ранних версиях этот порядок мог изменяться.
Устранение потенциальных проблем
Мы заметили, что это обновление сломало только несколько компонентов из тысяч, хотя ещё нужно тщательно протестировать переиспользуемые библиотеки. Один из примеров проблемного кода выглядит так:
Мы не думаем, что эта проблема станет распространённой, так как о ней предупреждает линтер. Убедитесь, что используете правило eslint-plugin-react-hooks.
Согласованные ошибки при возврате undefined
В React 16 и более ранних версиях возврат undefined всегда был ошибкой.
Это частично связано с тем, что undefined легко вернуть непреднамеренно.
В React 17 компоненты forwardRef и memo ведут себя так же, как компоненты на классах и функциональные компоненты. При возврате из них undefined вы получите ошибку.
Нативные стеки компонентов
Когда вы бросаете ошибку в браузере, он показывает вам трассировку стека (stack trace) с названием функций и их расположением. Однако стеков JavaScript часто не хватает для отслеживания проблем, так как здесь играет важную роль иерархия дерева React. Вам необходимо знать не только то, что компонент выбросил ошибку, но также где в дереве React находится этот компонент.
В связи с этим начиная с версии React 16 при возникновении ошибки печатается стек компонентов. Раньше стек компонентов был менее удобным по сравнению с нативным стеком JavaScript. В частности, стек компонентов в консоли не был кликабельным, так как React не знал, где в исходном коде объявлена конкретная функция. Кроме того, стеки компонентов были практически бесполезными при работе с продакшен-кодом. В отличие от обычных минифицированных стеков JavaScript, которые позволяют получить имена функций с помощью маппинга, при использовании стеков компонентов React приходилось выбирать между размером бандла и использованием стеков в продакшене.
В React 17 мы используем другой механизм для создания стеков. Стеки компонентов создаются из нативных стеков JavaScript. Это позволяет получить удобные трассировки стеков React-компонентов в продакшен-окружении.
React обеспечил это с помощью подхода, который нельзя назвать традиционным. В настоящее время браузеры не дают доступ к стековому кадру (стек-фрейм, stack frame) функции (исходному файлу и расположению). Когда React перехватывает ошибку, он будет восстанавливать стек компонентов, выбрасывая и перехватывая временную ошибку внутри каждого из компонентов, которые находятся на более высоком уровне. Это незначительно снижает производительность при сбоях, но такое происходит только один раз на каждый тип компонентов.
Если хотите больше подробностей, посмотрите этот пулреквест. В большинстве случаев этот механизм не должен повлиять на ваш код. Разработчикам будет полезно знать о новой возможности: стеки компонентов теперь стали кликабельными, так как они созданы на основе нативных браузерных стек-фреймов. Это позволяет «расшифровать» их и найти нужную информацию об ошибках так же, как вы делаете это с обычными ошибками JavaScript.
Критичность изменения заключается в том, что React после перехвата ошибки повторно запускает часть функций и конструкторов классов, которые расположены в стеке выше. Поскольку функции рендеринга и конструкторы классов не должны иметь побочных эффектов, что важно для серверного рендеринга, это не должно привести к проблемам на практике.
Удаление приватного экспорта
Последним важным критическим изменением можно назвать удаление некоторых внутренних реализаций React, которые ранее были доступны другим проектам. Например, раньше React Native for Web зависел от внутренних реализаций системы событий. Но эти зависимости были хрупкими и часто ломались.
В React 17 мы удалили такие приватные экспорты. Насколько нам известно, React Native for Web был единственным проектом, который их использует. И этот проект уже использует другие инструменты, которые не зависят от этих приватных экспортов.
Это значит, что старые версии React Native for Web будут несовместимыми с React 17. Но у новых версий проблемы совместимости не будет. На практике это не вызовет затруднений, так как React Native for Web и так должен был обновляться, чтобы не терять совместимость после изменений React.
Установка
Рекомендуем в ближайшее время установить предварительную версию React 17 и сообщать о любых проблемах, с которыми вы столкнётесь во время миграции. Помните, что по сравнению со стабильной версией в предварительной более вероятны ошибки. Поэтому пока не используйте релиз-кандидат в продакшене.
Чтобы установить предварительную версию React 17 из npm, используйте команду:
Для установки из Yarn используйте команду:
Также через CDN доступны UMD-сборки:
Адаптированный перевод статьи React v17.0 Release Candidate: No New Features by Dan Abramov and Rachel Nabors.