Вы нарушаете правило для редукторов.Правило, которое говорит, не должно изменять свой аргумент входного состояния.Что вы делаете здесь:
const productsReducer = (state = initialState, action) => {
let updatedNum = state.num;
let updatedQty = state.qty;
let index;
и даже дальше по строкам кода для этого productsReducer
.Теперь правило немного вводит в заблуждение и несколько неверно, и я могу понять, что я имею в виду, но, учитывая сказанное, имейте в виду, что мы не должны изменять этот входной аргумент state
.
Так, например, всеэто плохо:
export default (state, action) => {
state[0] = ‘Dan’
state.pop()
state.push()
state.name = ‘Dan’
state.age = 30;
};
Если ваша функция редуктора имеет state
и =
где-то внутри нее.Вы нарушаете это правило редуктора и, как следствие, у вас возникают проблемы с вашим приложением.
Поэтому я думаю, что для того, чтобы устранить вашу ошибку здесь, нам нужно понять это правило:
Почему мой компонент не выполняет повторный рендеринг или у меня не запущен mapStateToProps? Случайное изменение или изменение вашего состояния напрямую является самой распространенной причиной, по которой компоненты не выполняют повторный рендеринг после отправки действия
Правда в том, что вы можете изменять свой аргумент state
весь день и не видеть никаких сообщений об ошибках.Вы можете добавлять свойства к объектам, вы можете изменять свойства объектов, все по этому аргументу state
, и Redux никогда не выдаст вам сообщений об ошибках.
Причина, по которой документы Redux говорят не изменятьаргумент состояния заключается в том, что новичку легче сказать, что он не может изменить это состояние, чем сказать им, когда они могут и не могут изменить это состояние.
Чтобы было ясно, мы все равно должны следовать официальным документам Redux.не мутировать state
аргумент никогда.Мы, безусловно, можем, и вы, конечно, сделали, но вот почему вы должны следовать тому, что говорит Redux Docs.
Я хочу помочь вам понять закулисные вещи, которые происходят с Redux, чтобы вы моглипонять ошибку в вашем приложении и исправить ее.
Давайте рассмотрим исходный код самой библиотеки Redux, в частности фрагмент, который начинается в строке 162 или около того.Вы можете проверить это по адресу: https://raw.githubusercontent.com/reduxjs/redux/master/src/combineReducers.js
Это строка, которая начинается с let hasChanged = false
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
Изучите этот блок кода, чтобы действительно понять, что это говорит:
случайные мутации предотвращают повторную визуализацию после отправки действия
Приведенный выше фрагмент кода является частью Redux, которая выполняет действие и в любое время отправляется и отправляет действие вокруг.для всех различных редукторов внутри вашего приложения.
Поэтому, когда бы мы ни отправляли действие, приведенный выше код Redux будет выполняться.
Я проведу вас через этот код шаг за шагом иполучить лучшее представление о том, что происходит, когда вы отправляете действие.
Первое, что происходит, мы устанавливаем переменную hasChanged
и устанавливаем ее равной false
.
Затеммы вводим цикл for
, который будет перебирать все различные редукторы внутри вашего приложения.
Внутри тела цикла for
есть переменная с именем previousStateForKey
Thпеременной будет присвоено последнее значение состояния, которое вернул ваш редуктор.
Каждый раз, когда редуктор вызывается, первый аргумент будет state
, который он возвратил при последнем запуске.
Таким образом, previousStateForKey
является ссылкой на предыдущее значение state
, которое вернул конкретный редуктор.Следующая строка - это место, где вызывается редуктор.
Первый аргумент - state
, который ваш редуктор возвратил в последний раз, previousStateForKey
, а затем второй аргумент - объект action
.
Ваш редуктор будет работать, а затем в конечном итоге повернет новое значение state
, которое будет присвоено nextStateForKey
.Таким образом, у нас есть hasChanged
, previousStateForKey
и nextStateForKey
, что является нашим новым значением state
.
Сразу после того, как ваш редуктор запустится и назначит это новое значение state
для nextStateForKey
, Redux будетпроверьте, вернул ли ваш редуктор значение undefined, что произойдет, когда наши редукторы будут впервые инициализированы, и Redux проверит это с помощью оператора if
здесь:
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
Это одно из главных правил использования редукторов: редуктор никогда не может вернуть значение undefined.Итак, с этим оператором if
Redux спрашивает, они вернули undefined?Если это так, выведите это сообщение об ошибке.
Если вы пройдете эту проверку, все станет еще интереснее в этой строке кода:
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
hasChanged
собирается взятьзначение прямого сравнения между nextStateForKey
и вашим previousStateForKey
.
Таким образом, правило не изменять ваш аргумент state
сводится к приведенной выше строке кода.
Что это сравнениеОн проверяет, являются ли nextStateForKey
и previousStateForKey
точно такими же массивами или объектами в памяти.
Так что, если вы только что вернули какой-то массив и его точно такой же массив в памяти из последнего разатогда редуктор побежит, тогда hasChanged
будет иметь значение false, иначе, если ваши редукторы вернули совершенно новый массив, созданный в вашем редукторе, и этот массив полностью отличается от массива в последний раз, когда ваш редуктор работал, тогда hasChanged
будет равенв true.
Значит, эта переменная hasChanged
сообщает, изменилось ли какое-либо состояние, возвращаемое этим редуктором?
Таким образом, цикл for
будет повторяться для всех ваших редукторов.и hasChanged
будет либо true
, либо false
, учитывая все те различные редукторы, которые вы ему передали.
Заметьте, как существует только одна переменная hasChanged
?Существует не один для каждого редуктора.Если какой-либо редуктор изменил значение или вернул другое значение, массив или объект или другое значение для целого числа, числа или строки, hasChanged
будет равно true
.
После for
петля, все еще становится интереснее.Мы смотрим на значение hasChanged
, и если оно было изменено на true, результатом всей функции будет возвращение нового объекта state
.Весь новый объект state
собран всеми вашими различными редукторами, в противном случае, если hasChanged
равен false
, мы вместо этого возвращаем state
, а state
является ссылкой на все state
, которые вернули ваши редукторыпоследний раз, когда они запускались.
Итак, этот фрагмент кода из Redux проверяет, возвращал ли кто-нибудь из ваших редукторов после запуска ваших редукторов совершенно новый массив, объект, число или строку, если они это сделали.тогда Redux вернет совершенно новый результат от всех ваших редукторов, в противном случае, если ваши редукторы не вернут нового значения, он вернет старое состояние из ваших редукторов.
Какое значение здесь?
Причина, по которой это относится к вашей проблеме, заключается в том, что если Redux возвращает старое значение состояния, Redux не будет уведомлять остальную часть вашего приложения об изменении данных.
Если у вас есть новый state
, если у вас естьмутировав state
из одного из ваших редукторов, и вы вернете nextState
, Redux посмотрит на этот объект и скажет: о, у нас есть какая-то новая форма состояния, все этиразличные редукторы, и он будет уведомлять остальную часть вашего приложения, включая ваше приложение React, о том, что у вас есть новое доступное состояние, и это заставит ваше приложение React повторно выполнить рендеринг.
Таким образом, причина, по которой вы не должны изменятьваш аргумент состояния в редукторе не потому, что вы не можете его мутировать, причина в том, что если вы случайно вернете то же значение, которое накачано в ваш редуктор, если вы скажете return state;
в вашем редукторе, и он все тот же объект илиМассив, измененный или нет, Redux не скажет никакой разницы, это тот же объект или массив в памяти, ничего не изменилось, и поэтому мы не обновляли данные внутри приложения, и приложению React не нужно отображать себя иВот почему вы не увидите обновленного счетчика на экране.Вот что происходит здесь.
Вот почему документы Redux говорят вам не изменять аргумент state
в редукторе.Если вы вернете аргумент state
, внося в него изменения или нет, если вы вернете то же значение в форме return state;
, то в ваше приложение не будет внесено никаких изменений.
Это то, чем занимается Redux docsпытается передать с этим правилом, не мутировать state
аргумент.
Гораздо проще сказать вам не изменять аргумент state
, как это происходит в документах Redux, чем пройти через огромный ответ, который я только что дал вам.