【问题标题】:TypeError: Cannot assign to read only property in react JSTypeError:无法分配给反应JS中的只读属性
【发布时间】:2021-01-29 13:10:21
【问题描述】:

下面是渲染任务数组的任务组件。


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 reducer 如下 // 使用 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 | }

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

谢谢。

【问题讨论】:

    标签: javascript arrays reactjs redux react-redux


    【解决方案1】:

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

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

    您可以通过将减速器的代码更改为这个来使其工作

    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

    【讨论】:

    • 这并不能解决我的问题,我不认为为什么会这样。我提到我正在使用 redux 工具包,它使用 immer 来保持状态的不变性。我正在更新它提供的代理状态,而不是反应状态。我认为问题在于items 是一个浅拷贝,所以它给出了错误。但[...tasks] 应该创建一个新数组,而不是副本。
    • 我的错,答案主要是我通过查看代码可以推断出的疯狂猜测。如果不知道在哪里/如何调用 handleOnDragEnd() 函数以及您作为 result 参数传递的内容,或者您​​在哪里定义 dispatch,那么很难完全理解您要做什么,我'我猜是useDispatch() 钩子?我的意思是这有点难以猜测。
    • 尝试制作一个小的代码框示例,可能更容易传达整个场景。如果你想知道是不是深/浅克隆问题,也可以试试JSON.parse(JSON.stringify(nestedArray))。数组解构只深入 1 级。
    • stringify 然后解析项目工作。 [...tasks] 似乎没有正常工作。理想情况下,应该在创建新数组时打破状态绑定。字符串化和解析是一个不错的技巧,但仍在等待正确的答案。谢谢队友
    • 很高兴你解决了它,伙计,这里有一篇关于为什么你不能让它与[...tasks]一起工作的文章。 dev.to/samanthaming/… 本质上原因是数组中有对象,你需要像 [...tasks.map((task) =&gt; ({...task}))] 这样的东西来深入 2 个级别,为每个额外的级别添加一个额外的 .map
    猜你喜欢
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    相关资源
    最近更新 更多