redux 无法指派给对象'# '的只读属性'url'< Object>

webghufk  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(145)

我尝试在对象内部更改键的值,但似乎我一直在改变状态。
问题发生在州调度

代码:

export function nameList(id, rank, sub) {
  const { nameItem: name } = store.getState().nameListSlice; //gets the nameItem state

  if (!name.length || name.length === 0) {
         const newName = [
  {
    name: "Male",
    url: getNameUrl("Male", id, rank, sub),
    icon: "Male",
    toolTip: "Male",
    active: false,
    idName: "male",
  },

  {
    name: "Female",
    url: getNameUrl("Female", id, rank, sub),
    icon: "Female",
    toolTip: "Female",
    active: false,
    idName: "female",
  },
];

store.dispatch(updateDetails(newName)); //The issue happen here
  } else {
    return name.map((n) => {
      n.url = getNameUrl(n.name, id, rank, sub);
      return n;
    });
  }
}

// This function returns a url
function getNameUrl(type, id, rank, sub) {
  switch (type) {
    case "Male": {
      return `/male/${id}/${rank}/${sub}`;
    }
    case "Female": {
      return `/female/${id}/${rank}/${sub}`;
    }
  }
}

减速器/作用:(redux工具包)

export const nameListSlice = createSlice({
  name: 'nameItem',
  initialState,
  reducers: {
    updateDetails: (state,action) => {
      state.nameItem = action.payload
    },
  },
})

export const { updateDetails } = nameListSlice.actions

export default nameListSlice.reducer

我得到的错误:

TypeError: Cannot assign to read only property 'url' of object '#<Object>'

这发生在上面的代码disptach -store.dispatch(updateDetails(newName));注解这段代码,修复了这个问题。如何调度没有这个错误?
同样基于此Uncaught TypeError: Cannot assign to read only property
但还是同样错误

1l5u6lss

1l5u6lss1#

RTK在使用immerjs的基础上,使用store.getState().nameListSlice得到的nameItem状态切片不是一个不可变的immerjs草图,可以使用不可变的更新函数createNextState来执行immutable update patterns
为什么你可以在createSlicestate.nameItem = action.payload;一样创建reducer的情况下改变状态?
createSlice使用createReducer创建大小写还原器。请参见createReducer的注解:

/**
 * A utility function that allows defining a reducer as a mapping from action
 * type to *case reducer* functions that handle these action types. The
 * reducer's initial state is passed as the first argument.
 *
 * @remarks
 * The body of every case reducer is implicitly wrapped with a call to
 * `produce()` from the [immer](https://github.com/mweststrate/immer) library.
 * This means that rather than returning a new state object, you can also
 * mutate the passed-in state object directly; these mutations will then be
 * automatically and efficiently translated into copies, giving you both
 * convenience and immutability.

每个case reducer的主体都隐式地封装了一个对produce()的调用,这就是为什么你可以在case reducer函数中改变状态。
但是如果你在case reducer之外得到状态切片,它就不是一个可变的draft状态,所以你不能像n.url = 'xxx'那样直接改变它。
例如:

import { configureStore, createNextState, createSlice, isDraft } from '@reduxjs/toolkit';

const initialState: { nameItem: any[] } = { nameItem: [{ url: '' }] };
export const nameListSlice = createSlice({
  name: 'nameItem',
  initialState,
  reducers: {
    updateDetails: (state, action) => {
      state.nameItem = action.payload;
    },
  },
});
const store = configureStore({
  reducer: {
    nameListSlice: nameListSlice.reducer,
  },
});

const { nameItem: name } = store.getState().nameListSlice;

console.log('isDraft: ', isDraft(name));

const nextNames = createNextState(name, (draft) => {
  draft.map((n) => {
    n.url = `/male`;
    return n;
  });
});

console.log('nextNames: ', nextNames);

输出量:

isDraft:  false
nextNames:  [ { url: '/male' } ]

相关问题