Ошибка: производитель погружения вернул новое значение * и * изменил его черновик. Либо вернуть новое значение *, либо * изменить черновик

Итак, я новичок в наборе инструментов 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

  1. Если вы вернете что-либо, кроме черновика, из функции рецепта, это возвращаемое значение заменит состояние
  2. В основном вы хотите изменить черновик, чтобы изменить состояние, ** поэтому возвращать черновик после его изменения необязательно **, immer все равно вернет доработанный черновик
  3. если вы вернете что-нибудь еще из recipe функция, то согласно пункту 1 новое возвращаемое значение заменит состояние
  4. Пункт 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
  },
});

Вы должны удалить только оператор return и .test

Вы получаете эту ошибку, потому что вы изменяете состояние, чего не должно быть. Обходной путь - создать новое состояние без ссылки на старое, что-то вроде: 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;
Другие вопросы по тегам