HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Изменение состояния фронтенд-приложения не всегда означает изменение данных, с которыми работает приложение. У данных может быть состояние, которое влияет только на внешний вид. Такое состояние называется UI-состоянием, то есть состоянием интерфейса пользователя. Его особенность в том, что оно существует только на клиенте во время взаимодействия с интерфейсом.</p>
1 <p>Изменение состояния фронтенд-приложения не всегда означает изменение данных, с которыми работает приложение. У данных может быть состояние, которое влияет только на внешний вид. Такое состояние называется UI-состоянием, то есть состоянием интерфейса пользователя. Его особенность в том, что оно существует только на клиенте во время взаимодействия с интерфейсом.</p>
2 <p>Например, в интернет-магазине у карточки товара может быть состояние "в фокусе" при наведении курсора. Это влияет только на отображение (например, карточка увеличивается или меняет цвет), но не меняет данные о товаре. Другой пример - индикатор загрузки. Когда пользователь отправляет форму, интерфейс может отображать спиннер или затемнять кнопку отправки. Это состояние актуально только во время запроса и не сохраняется в базе данных. Еще один пример - раскрытие или сворачивание списка комментариев. Сам факт того, что пользователь нажал кнопку "Показать больше", изменяет только локальное состояние отображения, но не сами комментарии и их содержание в базе данных.</p>
2 <p>Например, в интернет-магазине у карточки товара может быть состояние "в фокусе" при наведении курсора. Это влияет только на отображение (например, карточка увеличивается или меняет цвет), но не меняет данные о товаре. Другой пример - индикатор загрузки. Когда пользователь отправляет форму, интерфейс может отображать спиннер или затемнять кнопку отправки. Это состояние актуально только во время запроса и не сохраняется в базе данных. Еще один пример - раскрытие или сворачивание списка комментариев. Сам факт того, что пользователь нажал кнопку "Показать больше", изменяет только локальное состояние отображения, но не сами комментарии и их содержание в базе данных.</p>
3 <p>Представьте себе обычный<a>аккордеон</a>. Это способ отображения данных, с помощью которого можно скрыть или раскрыть какой-то из элементов списка. Для работы подобного аккордеона нужно состояние, описывающее отображение каждого элемента: свернут/раскрыт.</p>
3 <p>Представьте себе обычный<a>аккордеон</a>. Это способ отображения данных, с помощью которого можно скрыть или раскрыть какой-то из элементов списка. Для работы подобного аккордеона нужно состояние, описывающее отображение каждого элемента: свернут/раскрыт.</p>
4 <p>Где должно храниться это состояние? Например, его можно поместить внутрь самих данных:</p>
4 <p>Где должно храниться это состояние? Например, его можно поместить внутрь самих данных:</p>
5 <p>Где-то дальше, в слое отображения, происходит вывод этих данных с учетом флага. Технически задача решена, но у данного способа хранения есть существенные недостатки.</p>
5 <p>Где-то дальше, в слое отображения, происходит вывод этих данных с учетом флага. Технически задача решена, но у данного способа хранения есть существенные недостатки.</p>
6 <p>Начнем с главного. Данные на фронтенде не появляются из ниоткуда. Данные приложения хранятся на сервере, приходят с сервера и уходят на сервер. А сервер ничего про внешний вид не знает и знать не должен, это не касается данных. UI-состояние временное и изменяется только на клиенте. И тут возникает серьезная проблема. Если UI-состояние хранится внутри данных, то придется постоянно выполнять две вещи:</p>
6 <p>Начнем с главного. Данные на фронтенде не появляются из ниоткуда. Данные приложения хранятся на сервере, приходят с сервера и уходят на сервер. А сервер ничего про внешний вид не знает и знать не должен, это не касается данных. UI-состояние временное и изменяется только на клиенте. И тут возникает серьезная проблема. Если UI-состояние хранится внутри данных, то придется постоянно выполнять две вещи:</p>
7 <ul><li>Вводить дополнительную обработку для всех приходящих данных с сервера, добавляя туда UI-состояние.</li>
7 <ul><li>Вводить дополнительную обработку для всех приходящих данных с сервера, добавляя туда UI-состояние.</li>
8 <li>Вводить дополнительную обработку для всех данных, уходящих на сервер, удаляя из них все UI-состояние.</li>
8 <li>Вводить дополнительную обработку для всех данных, уходящих на сервер, удаляя из них все UI-состояние.</li>
9 </ul><p>А подобных элементов отображения, как правило, значительно больше, чем один. Сюда можно отнести видимость модальных окон, сортировку, скрытие/раскрытие во всех возможных проявлениях, различные режимы (редактирование), подтверждения и многое другое. Все это придется не просто хранить внутри данных, но и постоянно помнить про необходимость дополнительной обработки.</p>
9 </ul><p>А подобных элементов отображения, как правило, значительно больше, чем один. Сюда можно отнести видимость модальных окон, сортировку, скрытие/раскрытие во всех возможных проявлениях, различные режимы (редактирование), подтверждения и многое другое. Все это придется не просто хранить внутри данных, но и постоянно помнить про необходимость дополнительной обработки.</p>
10 <p>Но это еще не все. Далеко не всегда весь набор данных обрабатывается одинаково. Возможно, что один набор данных выводится на странице в разных местах либо только частично. Это значит, что UI-состояние у разных элементов может быть разное, либо у каких-то элементов его может не быть вообще. Как поступать в таком случае? Игнорировать различия и добавлять всем одинаковый набор данных или усложнять логику и делать заполнение выборочным?</p>
10 <p>Но это еще не все. Далеко не всегда весь набор данных обрабатывается одинаково. Возможно, что один набор данных выводится на странице в разных местах либо только частично. Это значит, что UI-состояние у разных элементов может быть разное, либо у каких-то элементов его может не быть вообще. Как поступать в таком случае? Игнорировать различия и добавлять всем одинаковый набор данных или усложнять логику и делать заполнение выборочным?</p>
11 <p>Из-за перечисленных проблем UI-состояние хранят отдельно от самих данных. Причем для каждой ситуации это будет свой набор данных. Например, для аккордеона состояние может выглядеть так:</p>
11 <p>Из-за перечисленных проблем UI-состояние хранят отдельно от самих данных. Причем для каждой ситуации это будет свой набор данных. Например, для аккордеона состояние может выглядеть так:</p>
12 <p>В какой момент это состояние появляется внутри state? UI-состояние может формироваться как в процессе работы приложения, так и на этапе инициализации при его запуске. Например:</p>
12 <p>В какой момент это состояние появляется внутри state? UI-состояние может формироваться как в процессе работы приложения, так и на этапе инициализации при его запуске. Например:</p>
13 <ul><li>При запуске приложения: состояние видимости элементов аккордеона задаётся по умолчанию (visibility: 'hidden').</li>
13 <ul><li>При запуске приложения: состояние видимости элементов аккордеона задаётся по умолчанию (visibility: 'hidden').</li>
14 <li>Во время работы: состояние модального окна изменяется на visible, когда пользователь кликает по кнопке открытия этого окна.</li>
14 <li>Во время работы: состояние модального окна изменяется на visible, когда пользователь кликает по кнопке открытия этого окна.</li>
15 </ul><h2>Пример: Реализация аккордиона</h2>
15 </ul><h2>Пример: Реализация аккордиона</h2>
16 <p>Соберем все вмести и посмотрим на код аккордиона, который демонстрирует принцип работы с UI-состоянием. В этом примере используется подход инициализации состояния по запросу (кроме первого элемента, который должен быть показан сразу).</p>
16 <p>Соберем все вмести и посмотрим на код аккордиона, который демонстрирует принцип работы с UI-состоянием. В этом примере используется подход инициализации состояния по запросу (кроме первого элемента, который должен быть показан сразу).</p>
17 <p><a>Попрактиковаться</a></p>
17 <p><a>Попрактиковаться</a></p>
18 <h2>Плюсы и минусы разделения</h2>
18 <h2>Плюсы и минусы разделения</h2>
19 <p>Отделяя UI-состояние от основных данных, мы получаем значительные преимущества:</p>
19 <p>Отделяя UI-состояние от основных данных, мы получаем значительные преимущества:</p>
20 <ul><li><p>Упрощение взаимодействия с сервером. Данные, отправляемые и получаемые от сервера, остаются чистыми и понятными. UI-состояние обрабатывается исключительно на клиенте, без дополнительной очистки или обогащения.</p>
20 <ul><li><p>Упрощение взаимодействия с сервером. Данные, отправляемые и получаемые от сервера, остаются чистыми и понятными. UI-состояние обрабатывается исключительно на клиенте, без дополнительной очистки или обогащения.</p>
21 </li>
21 </li>
22 <li><p>Повышение гибкости. Поскольку интерфейс может иметь несколько представлений одних и тех же данных, отдельное хранение UI-состояния позволяет легко настраивать каждое представление независимо друг от друга.</p>
22 <li><p>Повышение гибкости. Поскольку интерфейс может иметь несколько представлений одних и тех же данных, отдельное хранение UI-состояния позволяет легко настраивать каждое представление независимо друг от друга.</p>
23 </li>
23 </li>
24 <li><p>Удобство отладки и тестирования. Разделение данных и UI-состояния облегчает поиск и устранение ошибок, так как можно отдельно проверять корректность данных и отдельно проверять состояние интерфейса.</p>
24 <li><p>Удобство отладки и тестирования. Разделение данных и UI-состояния облегчает поиск и устранение ошибок, так как можно отдельно проверять корректность данных и отдельно проверять состояние интерфейса.</p>
25 </li>
25 </li>
26 </ul><p>Однако при таком подходе появляется новая задача: нужно поддерживать синхронизацию данных и UI-состояния. Изменение данных, например, удаление компании из списка, должно приводить к соответствующим изменениям в UI-состоянии, иначе возможны ошибки и некорректное отображение.</p>
26 </ul><p>Однако при таком подходе появляется новая задача: нужно поддерживать синхронизацию данных и UI-состояния. Изменение данных, например, удаление компании из списка, должно приводить к соответствующим изменениям в UI-состоянии, иначе возможны ошибки и некорректное отображение.</p>
27 <p><a>Полный пример</a></p>
27 <p><a>Полный пример</a></p>
28 <p>Для автоматизации таких задач применяют:</p>
28 <p>Для автоматизации таких задач применяют:</p>
29 <ul><li>Общую функцию-обработчик, которая управляет и данными, и UI-состоянием.</li>
29 <ul><li>Общую функцию-обработчик, которая управляет и данными, и UI-состоянием.</li>
30 <li>Реактивные инструменты управления состоянием (Redux, Zustand, MobX), автоматически синхронизирующие изменения.</li>
30 <li>Реактивные инструменты управления состоянием (Redux, Zustand, MobX), автоматически синхронизирующие изменения.</li>
31 </ul><h2>Итог</h2>
31 </ul><h2>Итог</h2>
32 <p>Разделение UI-состояния и данных позволяет снизить сложность и повысить управляемость приложения, упрощает работу с сервером и облегчает тестирование. Однако требует продуманной синхронизации между состоянием интерфейса и основными данными. Применение подходящих инструментов и практик помогает эффективно решать эти задачи.</p>
32 <p>Разделение UI-состояния и данных позволяет снизить сложность и повысить управляемость приложения, упрощает работу с сервером и облегчает тестирование. Однако требует продуманной синхронизации между состоянием интерфейса и основными данными. Применение подходящих инструментов и практик помогает эффективно решать эти задачи.</p>