Почему Reducer должен быть чистой функцией в Redux ?

Возможно, вы слышали, что Redux зависит от чистых функций. Но что именно это значит? Давайте разберёмся!
На рисунке ниже изображено простое TODO приложение. В данный момент оно показывает как завершенные, так и не завершенные задачи.
С правой стороны находится state. Это простой JavaScript объект, в котором хранится текущее состояние приложения.

Что происходит при нажатии на одну из задач ?
- reducer принимает старое состояние (state)
- создаёт новый объект путём копирования старых свойств (как id и text) и переопределением измененных (completed)
- возвращает новое состояние

Чистая функция
На фундаментальном уровне, любая функция, которая не изменяет входные данные, не зависит от внешнего состояния (базы данных, DOM или глобальной переменной) и возвращает один и тот же результат для одинаковых входных данных является чистой функцией.
Например функция ниже не изменяет значения “a” или “b”, не зависит от внешнего состояния и всегда возвращает один и тот же результат для одинаковых входных данных.
const add = (a, b) => a + b // чистая функция
Теперь, если вы посмотрите на наш reducer, то заметите, что это тоже чистая функция.

Но почему Reducer должен быть чистой функцией?
Давайте посмотрим, что произойдет, если мы cделаем наш reducer “не чистым”. Закомментируем раздел где он возвращает новый объект и позволим ему мутировать текущее состояние (state).
case 'TOGGLE_TODO':
if (state.id !== action.id) {
return state;
} // return {
// ...state,
// completed: !state.completed
// } state.completed = !state.completed; // изменяем первоначальный state
return state;default: ...
Теперь, если мы попытаемся нажать на одну из задач, ничего не произойдёт!
Хм. Залазим в исходный код Redux.

Без паники! Тут всё просто!
Redux принимает переданное в него состояние (state) и отдаёт его каждому reducer в цикле. Если есть какие-либо изменения он ожидает получить новый объект из reducer функции (также он рассчитывает получить старый объект обратно, если изменений не было).
Дальше Redux проверяет, изменилось ли старое состояние путём обычного сравнения, а так как мы мутировали свойство старого объекта вместо создания нового, внутри нашего reducer, то старое и новое состояние будут ссылаться на один и тот же объект.
Поэтому Redux думает, что ничего не изменилось!
Пример:
const initState = { a: 1 };const reducer = (state) => {
state.a = 3; // мутируем исходный объект initState
return state; // возвращаем обратно initState
}const newState = reducer(initState);console.log(newState, initState); // {a: 3} {a: 3}newState === initState // true
Почему Redux работает таким образом ?
Почему он не может сделать копию старого состояние, а затем где-нибудь сравнить ?
Ответ: есть только один способ узнать равны ли два объекта в JavaScript. Для этого используется deep-compare.
К сожалению, это становится чрезвычайно дорогим удовольствием в реальных приложениях, из-за, как правило, больших объектов.
Таким образом
Новый объект = Новое состояние
Старый объект = Не изменённое состояние
Вот поэтому редюсеры должны быть “чистыми” функциями в Redux!
Оригинал тут.