Ошибка: производитель погружения вернул новое значение * и * изменил его черновик. Либо вернуть новое значение *, либо * изменить черновик
Итак, я новичок в наборе инструментов redux.
У меня есть редуктор
const userAuthSlice = createSlice({
name: "userAuth",
initialState: {
token: '',
},
reducers: {
setToken: (state, action) => state.token = action.payload.test,
},
});
И у меня есть диспетчерская команда
<button
value={text.sign_in.submit}
onClick={() => dispatch(userAuthSlice.actions.setToken({test:"test"}))}
/>
и когда я нажимаю кнопку, я получаю эту ошибку:
введите описание изображения здесь
Я изолировал все, чтобы быть уверенным, что проблема именно в этом, а не больше.
Почему появляется эта ошибка?
8 ответов
Проблема заключается в использовании функции стрелки без фигурных скобок в качестве редуктора, поскольку она действует как неявный оператор возврата. Итак, вы оба мутируетеstate.token
, и возвращает результат присвоения.
Согласно документации Immer по возврату данных, есть несколько способов исправить это:
- Добавление
void
оператор перед назначением - Заключение присвоения в фигурные скобки, чтобы сделать его телом функции
так setToken
редуктор может быть обновлен с помощью void
как
setToken: (state, action) => void(state.token = action.payload.test)
Пункты, о которых стоит узнать IMMER
- Если вы вернете что-либо, кроме черновика, из функции рецепта, это возвращаемое значение заменит состояние
- В основном вы хотите изменить черновик, чтобы изменить состояние, ** поэтому возвращать черновик после его изменения необязательно **,
immer
все равно вернет доработанный черновик - если вы вернете что-нибудь еще из
recipe
функция, то согласно пункту 1 новое возвращаемое значение заменит состояние - Пункт 3 подходит, только если вы не изменяете черновик в своей функции рецепта. т.е. вы можете выбрать один из двух:
a. изменить черновик -> вернуть черновик или не определено, все в порядке
b. хотите вернуть какое-то новое значение, тогда вообще не трогайте черновик
Теперь отвечу на вопрос. это ваша функция редуктора
(state, action) => state.token = action.payload.test
Эта функция делает две вещи:
1. модифицирует состояние
2. возвращает action.payload.test,
поэтому он нарушает пункт 4 и, следовательно, ошибку из библиотеки Immer.
в этом конкретном случае намерение состоит только в том, чтобы изменить состояние, поэтому мы должны отменить существующий возврат с помощью void
setToken: (state, action) => void(state.token = action.payload.test)
Однако библиотека Immer рекомендует использовать блок кода для обеспечения согласованности в большой базе кода, этот блок кода неявно возвращает undefined.
setToken: (state, action) => { state.token = action.payload.test }
Правильный ответ
Использовать вместо
const userAuthSlice = createSlice({
name: "userAuth",
initialState: {
token: '',
},
reducers: {
setToken: (state, action) => state.token = action.payload.test,
},
});
Используйте это {я только что добавил скобки}
const userAuthSlice = createSlice({
name: "userAuth",
initialState: {
token: '',
},
reducers: {
setToken: (state, action) => { state.token = action.payload.test}, // <<= Change Here
},
});
Для тех, кто работает с PullState, моя проблема была с синтаксисом:
Я имел:
const onUpdate = (value: any) => {
someStore.update((s) => (s.value = value));
};
но должно быть:
const onUpdate = (value: any) => {
someStore.update((s) => {
s.value = value;
});
};
Почему-то не нравится стрелочная функция, объявленная при первом подходе.
Вместо того
state.token = action.payload.token
использовать
const token = action.payload.token
state.token = token
Константы неизменны, и присвоение содержимого action.payload константе решило проблему для меня (обратите внимание, что если полезная нагрузка является объектом, это не обязательно
Рабочий пример createSlice:
import { createSlice } from '@reduxjs/toolkit';
export const slice = createSlice({
name: 'login',
initialState: {
username: "Foo",
password: "AAA",
authorized : false
},
reducers: {
setUsername: (state, action) => {
const value = action.payload
state.username = value // action.payload.username
},
setPassword: (state, action) => {
const value = action.payload
state.password = value // action.payload.password
},
logon: (state, action) => {
state.username = action.payload.username
state.password = action.payload.password
state.authorized = true
},
logoff: state => state.authorized = false
},
});
Вы получаете эту ошибку, потому что вы изменяете состояние, чего не должно быть. Обходной путь - создать новое состояние без ссылки на старое, что-то вроде:
var newState = JSON.parse(JSON.stringify(state));
тогда
newState.token = action.payload.test return newState
Вы можете забыть о перерыве; заявление в вашем файле reducer.js;
case LOGIN_USER_ERROR_RESET:
draft.errormsg = '';
draft.loading = false;
draft.error = false;
break;