Важность отказа от мутирования состояния в redux/react

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

«Мутирование состояния в React — это анти-паттерн».

Большинство из нас, использующих React, знают об этом и нехотя соглашаются с этим.

Но есть серьезные последствия того, что ваш код React/Redux не является неизменяемым.

Мое путешествие в кроличью нору

В кодовой базе, над которой я работал, была странная ошибка. Компонент, который я создавал, полагался на объект состояния redux. Но обновление этого состояния путем вызова существующего редуктора для обновления состояния не перерисовывало объект.

В течение следующего часа я вырывал свои волосы, пытаясь разобраться в этом. Я глубоко погрузился в теорию, что это должно быть что-то связанное с внутренним устройством моего компонента.

Но потом, спустя долгое время, я решил сменить направление. Что если проблема в глобальном состоянии? Я проверил Redux DevTools, и состояние явно изменилось. Но повторного рендеринга не произошло.

Я начал подозревать, что проверка объектов React не улавливает изменений — но как это может быть, если состояние изменено? Я провел небольшой эксперимент, используя useEffect.

useEffect(() => {}, 
    console.log('Is this working?')
[myStateValue])
Вход в полноэкранный режим Выйти из полноэкранного режима

Это не сработало, когда состояние было обновлено, и у меня появился дымящийся пистолет.

Я проверил редуктор, обновляющий это состояние. И тут он уставился на меня, существующий редуктор был мутирован 😱.

state.value = newValue
return {
    state
}
Вход в полноэкранный режим Выход из полноэкранного режима

Эту ошибку легко допустить.

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

Но я понял, что это ошибка, которую легко допустить. Сложность в том, что технически код работает — но с огромными оговорками (ошибка с перерисовкой, с которой я столкнулся, — одна из них).

Это может происходить потому, что люди забывают, или во время рефакторинга для упрощения кода. Установка значения напрямую — это то, к чему привыкли многие программисты. К моему ужасу, после просмотра существующего кода в нем было еще больше мутировавших состояний.

Как должен был выглядеть код редуктора

return {
    ...state,
    value: newValue
}
Вход в полноэкранный режим Выход из полноэкранного режима

Есть небольшая разница в коде, но огромная разница в том, как это проверяется React/Redux.

Почему вы никогда не должны мутировать состояние

Во-первых, Redux предостерегает от мутирования состояния. Есть много, много причин, почему. Но главная из них заключается в том, что Redux использует неглубокую проверку равенства, а НЕ глубокую проверку равенства.

Во-вторых, в React вообще не следует изменять состояние объекта. React использует [.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) проверку ссылок. Это означает, что он проверяет, является ли ссылка на объект одинаковой в памяти, а НЕ имеет ли она различные вложенные значения. React Hooks делает очень хорошую работу по предотвращению этого, но с редукторами Redux эта работа остается за вами.

// The problematic code above is like coding:
const [value, setValue] = useState('a')
state.value = 'b'

// instead of coding:
setValue('b')
Вход в полноэкранный режим Выход из полноэкранного режима

В моем случае мне потребовался час, чтобы найти проблему, но меньше минуты, чтобы ее устранить.

Вывод

Никогда не мутируйте состояние React и Redux!

Если вы обнаружили старый код, который мутирует — разработайте план быстрого решения этой проблемы, иначе это может создать кошмар для отладки.

Оцените статью
devanswers.ru
Добавить комментарий