State Management

Зачем нужен state management

Корзина интернет-магазина показывает 3 товара. Иконка в шапке показывает 2. Оба числа берутся из одного списка, но список скопирован в два разных места, и одно обновили, а другое забыли. Пользователь видит противоречие и теряет доверие к чекауту. Это не редкая экзотика, а самый частый класс багов в интерфейсах: одни и те же данные живут в нескольких местах и расходятся. State management это набор приёмов и инструментов, которые отвечают на вопрос где и как хранить данные, чтобы такого расхождения не случалось.

  • Корзина и иконка количества товаров в шапке, которые должны всегда показывать одно и то же число
  • Аватар и имя пользователя в десятке мест интерфейса: меню, профиль, комментарии, шапка
  • Открытая модалка, выбранная вкладка, развёрнутый сайдбар - состояние UI, общее для несвязанных компонентов
  • Фильтры каталога в URL, чтобы ссылку можно было отправить коллеге и увидеть тот же результат
  • Онбординг-визард на пять шагов, где данные шага 1 нужны на шаге 4

Предварительные знания

  • Понимание что такое компонентный интерфейс (дерево компонентов, как в React, Vue, Angular)
  • Идея локального состояния компонента (например useState) на уровне знакомства
  • Базовое представление о том, что UI это функция от данных: меняются данные - перерисовывается экран

Как из боли двусторонней привязки родились Flux и Redux

2014 год, Facebook. Команда столкнулась с багом счётчика непрочитанных сообщений: иконка показывала уведомления, которых уже не было, потому что данные обновлялись из нескольких мест и расходились. Двусторонняя привязка данных (модель меняет вид, вид меняет модель) превращала поток данных в запутанный граф, где причину рассинхрона было не отследить. На конференции F8 Джинг Чен представила Flux - архитектуру с однонаправленным потоком: action идёт в dispatcher, тот в store, store обновляет view, и больше никак. В 2015 году Дэн Абрамов, готовя доклад на React Europe, написал Redux - компактную реализацию идей Flux с одним store и чистыми редьюсерами. Redux дал предсказуемость и time-travel отладку, но позже выяснилось, что для многих задач он избыточен, и это породило целое поколение более лёгких инструментов.

Что такое состояние приложения

Состояние приложения это все данные, которые меняются во время работы и влияют на то, что отображается. Введённый в поле текст, список товаров в корзине, признак загрузки, открыта ли модалка, выбранная тема - всё это состояние. Современный интерфейс строится по принципу UI это функция от состояния: при изменении данных фреймворк перерисовывает соответствующие части экрана. Поэтому вопрос где живёт состояние определяет, насколько предсказуемо ведёт себя приложение.

  • Локальное состояние компонента: открыт ли выпадающий список, текст в одном поле
  • Общее состояние нескольких компонентов: выбранная тема, корзина, текущий пользователь
  • Серверное состояние: список заказов, профиль - данные, которые на самом деле живут в базе на сервере
  • Состояние навигации: какая страница открыта, какие фильтры в адресной строке

Ключевая мысль: не всё, что хранится в памяти, одинаково. Текст в одном поле и список заказов с сервера это разные сущности с разным жизненным циклом. Смешивать их в одном хранилище - источник большинства проблем, которые курс разбирает дальше.

Что из перечисленного точнее всего описывает состояние приложения?

Синхронизация, prop-drilling и рассинхрон

Хранить одно значение в одном компоненте просто. Боль начинается, когда одни и те же данные нужны в разных, далёких друг от друга частях дерева. Возникают три типичные проблемы: проброс данных через промежуточные компоненты (prop-drilling), синхронизация копий и рассинхрон интерфейса, когда копии разошлись.

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

  • Копирование данных (источник рассинхрона) — Список товаров скопирован в стейт корзины и отдельно в стейт иконки в шапке. Чтобы числа совпадали, оба надо обновлять синхронно. Один забытый апдейт - и UI противоречит сам себе.
  • Один источник, много читателей — Список лежит в одном месте, а корзина и иконка читают из него. Изменение видно сразу везде, потому что копий нет и синхронизировать нечего.

Рассинхрон почти всегда возникает из дублирования. Как только одно и то же значение хранится в двух местах и обновляется независимо, вопрос не в том, разойдутся ли они, а когда. Поэтому борьба идёт не с симптомом (разными числами), а с причиной - наличием копий.

Какова корневая причина рассинхрона интерфейса, когда корзина показывает 3 товара, а иконка в шапке - 2?

Где настоящая боль и зачем инструменты

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

ПроблемаБез инструментаС инструментом state management
Общие данныеКопии в разных компонентах, ручная синхронизацияОдно хранилище, все читают из него
Проброс данныхProp-drilling через всю цепочкуКомпонент берёт нужное напрямую из стора
Изменение из разных местНепредсказуемый граф обновленийЯвные правила (например однонаправленный поток)
ОтладкаНепонятно, кто и когда изменил данныеПрослеживаемая история изменений

Именно из этой боли выросли Flux и Redux: они навели порядок в потоке данных, сделав его однонаправленным и прослеживаемым. Со временем стало ясно, что для разных видов состояния нужны разные инструменты, и единый тяжёлый стор не всегда оправдан. Но исходная задача неизменна: согласованность общих данных при изменениях из многих мест.

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

В чём состоит настоящая задача, которую решают инструменты state management?

Связь с другими темами

Этот урок объясняет саму проблему. Дальше курс раскладывает её на части:

  • Таксономия состояния — Следующий шаг: понять, что состояние бывает разным (локальное, серверное, URL) и каждому нужен свой инструмент
  • Единый источник истины — Прямой ответ на боль из этого урока: одно каноническое место для каждого куска данных
  • Zustand — Современный лёгкий стор, выросший как ответ на избыточность Redux в типовых задачах

Итог

  • Состояние приложения это данные, которые меняются во времени и определяют, что видит пользователь на экране
  • Главная боль не в хранении одного значения, а в синхронизации одних и тех же данных между многими частями интерфейса
  • Prop-drilling (проброс данных через цепочку промежуточных компонентов) делает код хрупким и связывает несвязанные компоненты
  • Рассинхрон UI возникает, когда одни данные скопированы в несколько мест и обновляются независимо
  • Flux (Facebook, 2014) предложил однонаправленный поток данных как лекарство от запутанной двусторонней привязки
  • Redux (Дэн Абрамов, 2015) сделал идеи Flux компактными и предсказуемыми, но для многих задач оказался избыточен

Связанные уроки

  • sm-02-state-taxonomy — Поняв зачем нужен state management, следующий шаг - разобрать виды состояния и подобрать инструмент под каждый
  • rc-38-zustand-state — Zustand это один из современных ответов на проблемы, которые в этом уроке разбираются на уровне идеи
Зачем нужен state management

0

1

Войти