Почему при кликах на кнопку может происходить рассинхронизация цвета текста и фона в React
Что проверяют
Понимаете ли вы, что React коммитит одно состояние целиком, и значит видимая рассинхронизация всегда означает, что один из двух цветов живёт вне этого состояния. Проверяют умение искать источник истины, а не навешивать эффекты.
Как отвечать
Начать с главного: два вызова сеттеров в одном обработчике попадают в один коммит, React объединяет обновления, включая пришедшие из промисов и таймеров. Внутри одного коммита фон и текст разъехаться не могут, оба стиля применяются к одному узлу в одной фазе. Значит, причина снаружи, и вариантов немного.
Один цвет задан не состоянием: записан руками через ref.current.style или классом, который навесил чужой код. React перерисовывает только своё, и две записи начинают спорить.
У свойств разное время анимации: правило вида transition: background-color 0.3s без упоминания color даёт ровно этот эффект, и React тут вообще ни при чём.
Значения приходят из разных источников: одно из локального состояния, второе из внешнего стора, на который подписались вручную. Внешние хранилища читают через useSyncExternalStore, иначе при прерываемом рендере возможен разрыв.
Обновление считается от устаревшего замыкания, например setBg(!bg) в обработчике, зафиксированном с пустым списком зависимостей. Лечится функциональной формой сеттера.
Правильный вывод стоит произнести вслух: два зависимых значения хранят как одно. Состояние это тема кнопки, а фон и текст выводятся из неё.
Где спотыкаются
Валят всё на асинхронность обновления состояния и предлагают «передать колбэк вторым аргументом». У хука такого аргумента нет, это память о классовом API, и к рассинхронизации двух стилей оно отношения не имеет.
Синхронизируют второй цвет эффектом по первому. Это лишний коммит и промежуточный кадр, где фон уже новый, а текст ещё старый, то есть ровно та проблема, которую пытались убрать.
Ссылаются на конкурентный рендер как на причину. React не показывает наполовину применённый коммит: конкурентность может дать устаревшую, но целостную картинку либо разрыв при чтении внешнего стора мимо useSyncExternalStore.