Содержание

  • Введение
  • Что такое «состояние»?
  • Преимущества глобального состояния
  • Недостатки глобального состояния
  • Преимущества разделяемого состояния
  • Недостатки разделяемого состояния
  • Как разделять состояние?
  • Шаблоны и библиотеки управления состоянием

Введение

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

Таблицы, социальные субъекты, диаграммы  —  у всего в интернете имеются данные и способ взаимодействия с ними.

А мы, как разработчики, постоянно ищем способы эффективнее управлять взаимодействиями, чтобы не приходилось принимать лекарства от головной боли, вызываемой всеми этими рассуждениями и отладкой.

Появилось много решений, код стал пригоднее для повторного использования, абстрагированнее. От вопроса «Как справиться с этим сценарием?» мы перешли к «В какой библиотеке имеется все для проекта?».

Из-за обилия вариантов главная проблема сейчас  —  выявление лучшей библиотеки, а о самих решаемых проблемах часто забывают.

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

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

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

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

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

Начнем с основ, реализуя решения проблемы разными библиотеками управления состоянием.


Что такое «состояние»?

Состояние  —  это любая информация, запоминаемая для работы приложения/системы.

Состояние бывает локальным, когда находится в компоненте и потребляется им, или разделяемым, когда один экземпляр потребляется несколькими компонентами.

Состояние находится на сервере (приложения типа crud) или в клиенте (веб-редакторы).

В целом, локальное состояние  —  это хорошо. Если информация «живет и умирает», где используется, то легко понять, для чего она, если можно ее очистить или реорганизовать.

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

У обоих решений имеются сильные и слабые стороны.

Преимущества глобального состояния

  • Всегда доступно: остается весь сеанс и применяется в каждом компоненте.
  • Не нужно думать, куда поместить новую информацию для сохранения.
  • Требуется меньше рефакторинга: без перемещения вверх-вниз при внедрении новых функций.
  • Легко сохраняется/инициализируется «с холода».

Недостатки глобального состояния

  • Остается весь сеанс, способно сильно снизить производительность, особенно если неизменяемое.
  • Сбивает с толку: зачем здесь эти данные, применяются ли для новой функции и какие компоненты от этого зависят?
  • Не очень хорошо масштабируется: в приложении загружается больше данных. Если не очистить ненужную часть, это отразится на производительности.
  • Затрудняется присвоение имен: в них должна присутствовать область видимости.

Преимущества разделяемого состояния

  • Проще присвоение имен: с помощью локальности дается много контекста.
  • Безопаснее удаление и рефакторинг: случаи использования очень близки, поэтому за ними легко следить.
  • Удобнее сопровождение: легче понять, для чего оно нужно, если остается близким к случаям использования.
  • Довольно хорошо масштабируется: при размонтировании компонентов состояние очищается.

Недостатки разделяемого состояния

  • Код в компонентах значительно удлиняется: можно сократить, переместив логику в хук.
  • Похожие локальные имена в разных компонентах чреваты путаницей. Решение: поиск и замена на несвязанные варианты.
  • Требуется много рефакторинга: перемещение вверх-вниз  —  это мучение.
  • Пробрасывание свойств.

Как разделять состояние?

Не разделять состояние, которое в этом не нуждается,  —  подход вовсе не плохой. А как насчет того, чтобы поделиться информацией?

Подъем состояния

  • По возможности сохраняйте состояние локальным.
  • Прямое отслеживание.
  • Пробрасывание свойств.

Контекст

  • Пробрасывание свойств исключается.
  • Осторожнее с провайдерами.
  • Может быть глобальным.
  • Эффективные повторные отрисовки: при изменении состояния компонентов отрисовываются только те, которыми потребляется контекст.
  • Сложно отслеживать/сопровождать: у кого при изменении чего-либо вызывается этот цикл отрисовки?

Внешние библиотеки

  • Скрывают сложность.
  • Могут быть платформенно-независимыми.
  • Или предназначенными для React: использовать соответствующие API для интеграции и/или производительности.

Внешние сущности (ручная реализация с API-интерфейсами, отличными от React, например localstorage и url)

  • Синхронизация вкладок и окон.
  • Разделяйте состояние отправлением ссылки, например отфильтрованные поиски.

Шаблоны и библиотеки управления состоянием

Возможно, список неполный: постоянно появляются новые.

Для состояния клиента

Redux

  • Предсказуемая.
  • Легко отслеживать, почему, когда и как обновлено состояние.
  • Централизованная.
  • Шаблонный код.
  • Платформенно-независимая.

MobX

  • Минималистичная: без шаблонного кода.
  • Реактивная.
  • Асинхронность без усилий.
  • С прокси-сервером.
  • Платформенно-независимая.

Recoil

  • Простые API.
  • Встроенная асинхронность.
  • Минималистичная: без шаблонного кода.
  • С поддержкой API-интерфейсов React.

Jotai

  • Упрощенная версия Recoil: в четыре раза меньше.
  • Несколько ограничена: нет React Fast Refresh и снимков.
  • Стабильные API-интерфейсы постоянно хранимого состояния.

Zustand

  • Встроенная асинхронность.
  • Централизованная.

Valtio

  • С прокси-сервером.
  • Состояние изменяется напрямую.
  • Интегрированный ESLint-плагин.
  • Платформенно-независимая.

RxJS

  • Шаблонный код: привязки React.
  • Платформенно-независимая.
  • Потоки как состояние.
  • Много теории для освоения.

Rematch

  • Реализация Redux.
  • Шаблонный код сведен к минимуму.
  • Очень маленькая.
  • Сложная типизация: нетривиальным структурам требуется приведение типов.

Hookstate

  • Геттеры и сеттеры, напоминает Java.
  • Имена полей в состояниях конфликтуют с API, например к myState.value нужно обращаться как myState.nested.value.get().
  • Структура состояния очень легко отслеживается, быстро пишется.
  • Прокси-магия, но более явная.

A̵k̵i̵t̵a̵ Elf

  • Используется RxJS.
  • Хранилища на основе сущностей.
  • С интерфейсом командной строки.
  • Шаблонный код: определение репозиториев.
  • База данных CRM: как стандартные операции.
  • Платформенно-независимая.

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


Как выбрать правильную, лучшую библиотеку?

Такой не существует. Сформулируем так: лучшую для чего? Даже на этот вопрос может не быть универсального, научного ответа.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Fabio Brunori: State Management in React — Overview

Предыдущая статьяКак улучшить навыки работы с Python в 2023 году
Следующая статьяРаскройте потенциал VS Code для программирования на Ruby