В этом посте описывается, как важно не изменять состояние в реакции.
Чтобы понять мутации состояний в реакции, сначала нужно понять основы мутаций данных в javascript.
Если что-то неизменяемое, значит, это не может быть изменено. В javascript есть 5 примитивов. числа, строки, логические значения, undefined и null, они неизменяемы.
Пример1: -
let str = ‘abc’
str нельзя изменить напрямую, поэтому, если мы хотим внести изменения в строку, мы должны использовать такие функции, как replace, toUpperCase и т. д., которые создают новую строку.
Пример2: -
let str1 = ‘abc’ let str2 = ‘abc’
Теперь str1 === str2 вернет true, потому что str1 и str2 имеют одинаковое значение и относятся к одному и тому же примитиву.
Но для объектов это не работает.
let str1 = new String(‘abc’) let str2 = new String(‘abc’)
str1 === str2 возвращает false, потому что, хотя значения равны, в памяти создаются два разных объекта, поэтому оба они ссылаются на два разных объекта, поэтому это сравнение не удалось при сравнении ссылок.
В javascript, если вы хотите сравнить значения массивов или объектов, прямого способа сделать это нет, необходимо сравнить каждый отдельный элемент в массиве или пары значений ключа в объекте.
Проблемы с мутациями состояний в react
В документации React упоминается, что никогда не изменяйте this.state напрямую, вместо этого всегда используйте this.setState для любых обновлений состояния. Вот две основные причины для этого:
a.) setState работает партиями, что означает, что нельзя ожидать, что setState выполнит обновление состояния немедленно, это асинхронная операция, поэтому изменения состояния могут произойти в более поздний момент времени, что означает, что вручную изменяемое состояние может быть отменено setState.
б.) Производительность. При использовании чистого компонента или shouldComponentUpdate они будут выполнять неглубокое сравнение с использованием оператора ===, но если вы измените состояние, ссылка на объект останется прежней, поэтому сравнение не удастся.
Предотвращение мутаций массива / объекта
а.) Используйте фрагмент
let x = [‘a’, ’b’, ’c’, ’d’, ’e’]
Если мы хотим удалить c из приведенного выше массива и распечатать массив, мы можем сделать следующее.
x.splice(2,1) console.log(x) // prints [‘a’, ’b’, ’d’, ’e’]
но splice напрямую изменил x, поэтому он изменил массив.
Это может быть достигнуто неизменным образом, как показано ниже, с помощью slice и concat, поскольку это неизменяемые операции,
let x = [‘a’, ’b’, ’c’, ’d’, ’e’] let y = x.slice(0,2).concat(x.slice(3)) console.log(x) // prints original array [‘a’, ’b’, ’c’, ’d’, ’e’] console.log(y) // prints [‘a’, ’b’, ’d’, ’e’]
б.) Используйте Object.assign
let x = { ‘a’:’Hello’, ‘b’: ‘Hey’ }
Теперь предположим, хотим ли мы изменить значение «а» с «Привет» на «Ура».
Мутабельный путь: -
x.a = «Ура», это напрямую изменит объект, которого мы хотим избежать в реакции, если x принадлежит состоянию.
Неизменяемый способ: -
let y = Object.assign({}, x }// creates a brand new object
y.a = «Ура», теперь y можно использовать для обновления состояния реакции, поскольку оно полностью неизменяемо.
c.) Используйте оператор Spread в ES6.
Функциональность, описанная выше, может быть достигнута с помощью оператора распространения.
let x = { ‘a’:’Hello’, ‘b’: ‘Hey’ } let y = {…x,’a’:’Hurray’} console.log(x) // prints { a: ‘Hello’, b: ‘Hey’ } console.log(y)// prints { a: ‘Hurray’, b: ‘Hey’ }
г) Вложенные объекты
Допустим, состояние содержит объект пользователя, который выглядит следующим образом:
let user = { profile:{ address:{ city: ‘London’ } } }
Если мы хотим навсегда изменить город из Лондона в Нью-Йорк, нам нужно сделать это, как показано ниже.
{ …state, user:{ …state.user, profile:{…state.user.profile, address:{…state.user.profile.address, city:’Newyork’}} } }
Вот почему рекомендуется сохранять состояние реакции как можно более плоским, также рассмотрите возможность использования Immutable.js (https://facebook.github.io/immutable-js/), который выполняет неизменяемые изменения данных с помощью встроенных функций или Immutability Helper. как предлагается в документации React (https://reactjs.org/docs/update.html).