开发者问题收集

TypeError:无法分配给 React JS 中的只读属性

2021-01-29
2783

下面是呈现任务数组的 Task 组件。

export function Task() {

    const tasks = useSelector((state) => state.tasks.taskArray);

    function handleOnDragEnd(result) {
        if (!result.destination) return;

        let items = [...tasks]; // also tried with tasks.slice() to obtain a deep copy

        let i = result.source.index;
        let direction = result.destination.index > result.source.index; 
                   // direction true means moving right & swapping

        while (i != result.destination.index) {
            if (direction) {
                let tempGlobalKey = items[i].globalKey;
                items[i].globalKey = items[i + 1].globalKey; // <<< here 
                items[i + 1].globalKey = tempGlobalKey;
                i++;
            } else {
                let tempGlobalKey = items[i].globalKey;
                items[i].globalKey = items[i - 1].globalKey; // <<< here
                items[i - 1].globalKey = tempGlobalKey;
                i--;
            }
        }

        dispatch(updateOrder(items));
    }

.
..
... so on

在 taskSlice 中,updateOrder 减速器如下 // 使用 redux-toolkit 因此直接改变下面的状态

export const tasksSlice = createSlice({
    name: "tasks",
    initialState: {
        taskArray: [],
        meta: {
            globalKey: 0,
            completedTaskStartIndex: -1,
        },
    },
    reducers: {
        updateOrder: (tasks, { payload }) => {
            tasks.taskArray = payload;
        },
    }
    ......so on

当拖动结束并处理拖动结束时,错误出现在代码中标记的行上。错误消息是

TypeError: Cannot assign to read only property 'globalKey' of object '#<Object>'
handleOnDragEnd
src/containers/tasks/index.js:60
  57 |     i++;
  58 | } else {
  59 |     let tempGlobalKey = items[i].globalKey;
> 60 |     items[i].globalKey = items[i - 1].globalKey;
     | ^  61 |     items[i - 1].globalKey = tempGlobalKey;
  62 |     i--;
  63 | }

我知道状态不能直接改变,因此我正在更新数组的深层副本并将其设置为理想情况下应该可以工作的新状态。如果可以指出问题或者有其他方法可以实现这一点,我将不胜感激。

谢谢。

2个回答

当您使用 Redux 时,reducer 的目的不是改变先前的状态,而是返回一个 新的修改后的 状态。

这听起来像是一个微不足道的区别,但事实并非如此,因为先前的状态对象通常是不可变的,具体取决于框架和/或 Flux 实现。

您可以通过将 Reducer 的代码更改为此代码来使其工作

reducers: {
  updateOrder: (previousState, { payload }) => {
    return {
      ...previousState,
      taskArray: payload
    }
  },
}

编辑:

似乎您正在尝试深度克隆嵌套对象。 以下代码

const myNewArray = [...myOldArray];

确实会在内存中使用旧数组中的所有值创建一个新对象,并且改变新对象不会更改旧数组。

有一个小警告,数组中的每个子项都将通过引用而不是值进行克隆。 这意味着执行如下操作:

const myOldArray = [{a: "hello"}]
const myNewArray = [...myOldArray];

将保留对同一 {a: "hello"> 对象的引用。

因此,执行 myNewArray.push("asdf") 不会影响 myOldArray ,但执行 myNewArray[0].a = "goodbye" 也会影响 myOldArray 的第一项。

这里有一篇更好的解释文章 https://dev.to/samanthaming/how-to-deep-clone-an-array-in-javascript-3cig

Franco Roura
2021-01-29

全部合而为一...

const copyDeepObject = obj => {
   return JSON.parse(JSON.stringify(obj));
}

let copy = copyDeepObject({anything you want to copy}); 
copy.readOnlyProp = "new value";
Muhammad Ahmar Khan
2023-02-20