Redux: Почему избежать мутаций, такой фундаментальной частью его использования?

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

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

Я понимаю, что не вызывающие побочные эффекты помогают нам получить преимущества однонаправленного потока данных.

Я просто не понимаю, что так важно, чтобы не мутировать старое состояние.

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

Вопрос:

Существуют ли другие причины, по которым мы не хотим мутировать старое состояние на каждом шагу?

Ответ 1

Работа с неизменяемыми структурами данных может оказать положительное влияние на производительность, если это будет сделано правильно. В случае React производительность часто заключается в том, чтобы избежать ненужного повторного рендеринга вашего приложения, если данные не изменились.

Для этого вам нужно сравнить следующее состояние вашего приложения с текущим состоянием. Если состояния различаются: повторная визуализация. В противном случае нет.

Чтобы сравнить состояния, вам нужно сравнить объекты в состоянии для равенства. В простых старых JavaScript-объектах вам нужно будет провести глубокое сравнение, чтобы увидеть, изменилось ли какое-либо свойство внутри объектов.

С неизменяемыми объектами вам это не нужно.

immutableObject1 === immutableObject2

в основном делает трюк. Или если вы используете lib как Immutable.js Immutable.is(obj1, obj2).

В терминах реакции вы можете использовать его для метода shouldComponentUpdate, как это делает популярный PureRenderMixin.

shouldComponentUpdate(nextProps, nextState) {
    return nextState !== this.state;
}

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

Надеюсь, это способствует обоснованию неизменных объектов.

Ответ 2

Я тоже довольно новичок в Redux (и React.js), но это то, что я понимаю из изучения этого материала.

Существует несколько причин, по которым неизменяемое состояние выбирается над изменчивым. Прежде всего, отслеживание мутаций довольно сложно. Например, если вы используете переменную в нескольких частях кода, и переменная может быть изменена в каждом из этих мест, вам необходимо обрабатывать каждое изменение и синхронизировать результаты мутации. Этот подход во многих случаях приводит к двунаправленным потокам данных. Части данных текут вверх и вниз по функциям, переменным и так далее. Код начинает загрязняться конструкциями if-else, которые ответственны за обработку изменений состояния. Когда вы добавляете некоторые асинхронные вызовы, ваши изменения состояния могут быть еще труднее отслеживать. Конечно, мы можем подписаться на события данных (например, Object.observe), но это может привести к ситуации, когда какая-то часть приложения, которое пропустила изменение, не синхронизируется с другой частью вашей программы.

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

Вы можете найти дополнительную информацию о плюсах и минусах изменчивости (и о том, почему он был выбран в качестве основного подхода Redux):

Ответ 3

Redux проверяет, совпадает ли старый объект с новым объектом, сравнивая расположения памяти двух объектов. Если вы измените свойство старых объектов внутри редуктора, "новое состояние" и "старое состояние" будут указывать на один и тот же объект, а Redux будет делать вывод, что ничего не изменилось.

Ответ 4

Нет причин. Нет никаких основополагающих причин, по которым shouldComponentUpdate оптимизация "чистого рендера" не может работать с изменяемыми контейнерами состояний. Эта библиотека делает это, например.

https://github.com/Volicon/NestedReact

С неизменяемыми данными ссылка на структуру данных сама по себе может использоваться как токен версии. Таким образом, сравнивая ссылки, вы сравниваете версии.

С изменчивыми данными вам нужно будет ввести (и сравнить) отдельные токены версии, которые трудно сделать вручную, но их можно легко достичь с помощью умных "наблюдаемых" объектов.

Ответ 5

Ключ мантры "без мутаций" состоит в том, что если вы не можете мутировать объект, вы должны создать новый (со свойствами исходного объекта и новых). Чтобы обновить компоненты при отправке действия, Redux проверяет, отличается ли объект, а не изменения свойств, поэтому:

  • Если вы создаете новый объект, Redux увидит, что объект отличается от него, поэтому он будет запускать обновления компонентов.
  • Если вы перепутаете объект, который уже находится в хранилище (например, добавление или изменение свойства) Redux не увидит изменения, поэтому он не будет обновить компоненты.