Тема конечных автоматов занимает центральную роль во фронтенд-разработке. Интерактивные элементы всегда вовлечены в процессы, связанные с изменением состояний. Модальные окна бывают открытые и скрытые, кнопка нажата, отжата или заблокирована (например, во время AJAX-запроса). Примеров бесконечное множество. Нередко эти автоматы зависят друг от друга, что порождает иерархию автоматов. Например, возможность взаимодействовать с элементом на экране может появляться только после нажатия кнопки «редактировать».
В React работа с автоматами проста до безобразия и в большинстве случаев не требует использования специальных библиотек. Возьмём, к примеру, кнопку, которая отвечает за показ куска текста. Её состояния можно описать так:
- По умолчанию текст скрыт (состояние hidden)
- Клик по кнопке отображает текст (состояние shown)
- Повторный клик прячет текст (hidden)
В данном случае у кнопки два состояния, поэтому можно упростить задачу и использовать флаг как индикатор состояния. Назовём его как isShown
.
Заметка для тех, кто уже знаком с состоянием в бэкенде. Использовать флаги с булевыми значениями очень не рекомендуется в бэкенде, когда состояние хранится в базе. Цена изменения автомата слишком высока (изменение типа колонки с boolean на string), поэтому даже в случае бинарной логики лучше делать полноценный автомат с именованными состояниями. Другими словами, для хранения состояния используйте не булево поле (с true/false), а текстовое поле, в котором будет содержаться полное название состояния. Например, если статья может находиться в двух состояниях («опубликована» или «не опубликована»), то нужно делать не поле 'published: bool' со значениями true и false, а поле 'publishing_state' со значениями 'published' и 'unpublished'
See the Pen js_react_fsm by Hexlet (@hexlet) on CodePen.
Большая часть кода в React (как и во всем фронтенде) выглядит именно так, как в примере выше. События порождают изменения состояния в данных, на основе которых, в свою очередь, меняется представление. Количество конечных автоматов во фронтенд-приложениях растёт с астрономической скоростью, главное их видеть и выделять явно.
Структура состояния
Данные, с которыми работает React, как правило, приходят из бэкенда. И эти данные тоже участвуют в разных процессах и находятся в разных состояниях. Например, статья может быть опубликована, а может быть и нет. И в зависимости от того, в каком она состоянии, рисуется UI. И здесь начинается самое интересное. Конкретно состояние опубликованности статьи не является частью UI, но UI использует это состояние, а при изменениях оно синхронизируется на фронтенде и бэкенде. Но в UI часто появляются состояния, которые отвечают исключительно за внешний вид, но не являются частью данных.
Если предположить, что данные, пришедшие с бэкенда, внутри нашего объекта-состояния хранятся как список под ключом items
, то возникает вопрос: куда записывать данные, отвечающие за состояние UI? То есть те самые состояния, которые появляются только при взаимодействии с пользователем и не используются на серверной стороне?
Пример: с бэкенда приходит статья такой структуры: { id: 3, name: 'How to program', state: 'published' }
. Она отправляется в items
. А в UI есть возможность зайти в её редактирование и для этого используется флаг (состояние) isEditing
, который существует только на экране. Вопрос: где хранить эту переменную?
Самый простой вариант: изменить саму статью внутри items
так, чтобы она имела такой вид: { id: 3, name: 'How to program', state: 'published', isEditing: true }
. Хотя на первый взгляд он и кажется разумным, проблем он привносит больше, чем пользы. В основном эти проблемы связаны с задачами синхронизации. Иногда бывает нужно отправить всю статью на сервер (после изменений), а иногда перечитать её заново с бэкенда. В такой ситуации нужно будет либо извлекать только нужные данные, либо постоянно делать «мердж» (слияние), чтобы не потерять состояние UI. Практика показала, что гораздо проще добавлять отдельный список исключительно под задачи хранения состояния UI. То есть в стейте добавить отдельный список, например, под названием itemUIStates
, и для статьи в него добавится элемент { articleId: 3, isEditing: true }
.
Дополнительные материалы
- Почему сложно программировать UI
- Курс "Автоматное программирование"
- Урок "Процессы и автоматы, их описывающие"
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.