Что такое состояние — и почему кнопка не «становится» серой

Смотри, тут прячется мысль, после которой интерфейсы перестают быть магией.
Ты нажал «Отправить». Кнопка стала серой и на ней закрутился спиннер. Кажется очевидным: код взял кнопку и покрасил её.
А на самом деле кнопку никто не трогал. Изменилась одна строчка где-то рядом — «отправка идёт: да». И кнопка нарисовалась заново, уже серой, потому что она всегда рисуется из этой строчки.
Вот эта строчка и есть состояние. И почти вся боль в интерфейсах — про неё.
Что такое состояние простыми словами
Состояние — это всё, что твоё приложение помнит прямо сейчас.
Проверка простая: если закрыть глаза и открыть снова, что приложение должно знать, чтобы нарисовать тот же экран? Вот это и есть состояние.
Например, на экране списка задач:
- список задач, который загрузился с сервера;
- какие из них отмечены галочкой;
- идёт ли прямо сейчас загрузка;
- текст, который ты набрал в поле поиска;
- залогинен ты или нет.
Обрати внимание: «синяя кнопка» и «шрифт 16px» в этот список не попали. Это не состояние, это оформление — оно всегда одинаковое. Состояние — только то, что меняется и что нужно помнить.
Полезно разделять две кучки:
- Состояние данных — что пришло с сервера. Список, профиль, баланс.
- Состояние интерфейса — что происходит на экране. Открыто ли меню, какая вкладка активна, что в поле ввода.
Первое обычно живёт где-то ещё и к тебе приезжает. Второе рождается и умирает вместе с экраном.
Почему ты меняешь не кнопку, а состояние
До того как появились современные интерфейсные библиотеки, писали именно так, как кажется интуитивным: найти кнопку в DOM, выключить её, поменять текст, потом не забыть включить обратно.
Проблема вылезала на третьем условии. Кнопка выключена, если идёт отправка. И ещё если форма пустая. И ещё если нет интернета. Теперь на каждое изменение любого из трёх надо вручную вспомнить все остальные. Забыл одно сочетание — кнопка навсегда серая, и никто не понимает почему.
Поэтому подход перевернули. Ты не описываешь переходы («стало так — сделай вот это»). Ты описываешь картинку: как экран выглядит при данном состоянии. А библиотека — например, React — сама сверяет, что изменилось, и перерисовывает нужное.
Интерфейс становится функцией от состояния. Одно и то же состояние — всегда одна и та же картинка.
Отсюда и правильный вопрос при баге. Не «почему кнопка серая», а «какое состояние заставляет её быть серой и кто его так поставил».
Где интерфейс ломается: две правды вместо одной
А теперь главное. Самый частый и самый мучительный класс багов в интерфейсах — это когда одна и та же правда хранится в двух местах.
Выглядит это безобидно. У тебя есть галочка «уведомления включены». Ты хранишь её в состоянии экрана, чтобы галочка мгновенно переключалась. И ещё она есть в профиле, который приехал с сервера.
Две копии одного факта. Дальше — вопрос времени:
- Запрос на сервер упал, а галочка на экране уже переключилась. Пользователь видит «включено», сервер считает «выключено».
- Пользователь ушёл на другой экран и вернулся. Один экран перечитал профиль, другой показывает старую копию.
- Ты открыл приложение на телефоне и на ноутбуке. Они расходятся.
Код при этом весь правильный. Просто правд стало две, и они неизбежно разъехались.
Второй частый вариант того же — состояние, которое можно вычислить. Хранишь список задач и отдельно число «сколько осталось». Одно обновилось, другое забыли. Показания разошлись.
Кстати, модели такое любят: попросишь агента добавить счётчик — он с большой вероятностью заведёт новую переменную рядом, потому что так короче. Это тот случай, когда стоит посмотреть на диф внимательно.
Один источник правды
Правило, которое закрывает почти всё выше: у каждого факта ровно одно место жительства.
Три практических следствия:
- Не копируй — ссылайся. Если галочка уже есть в профиле, экран должен читать её из профиля, а не заводить свою копию.
- Не храни то, что можно посчитать. «Сколько осталось» — не состояние, а вычисление из списка. Посчитай в момент отрисовки. Расходиться станет нечему.
- Подними состояние туда, откуда его видят все, кому оно нужно. Если галочку меняют на двух экранах — она не принадлежит ни одному из них. Её место выше.
И отладочный приём, который экономит часы. Когда интерфейс ведёт себя странно, не лезь сразу в код отрисовки. Выведи всё состояние экрана целиком — одним объектом, в консоль. В девяти случаях из десяти сразу видно: либо поле не то, что ты думал, либо таких полей два. Дальше можно позвать модель с этим объектом — с полным состоянием на руках она угадывает несравнимо лучше, чем по описанию «кнопка не работает».
Чем состояние отличается от переменной?
Любое состояние хранится в переменных, но не любая переменная — состояние. Состояние — то, от чего зависит картинка на экране: изменилось — нужно перерисовать. Временная переменная внутри функции этим свойством не обладает.
Где вообще хранить состояние?
Начни с самого узкого места: прямо в том экране, где оно нужно. Поднимай выше только тогда, когда его потребовал второй экран. Тянуть глобальное хранилище с первого дня — верный способ усложнить проект раньше времени.
Почему приложение забывает всё при перезагрузке?
Потому что состояние живёт в памяти, а память очищается. Чтобы пережить перезагрузку, его надо куда-то записать — в хранилище браузера, в файл или на сервер. Это уже отдельное решение, и принимать его стоит осознанно: не всё заслуживает того, чтобы помниться вечно.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





