You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React/Redux Toolkit中编辑activeBoard列数组报错求助

问题描述

使用Redux Toolkit开发任务管理器时,从board切片获取activeBoard后,编辑页面中boardName输入框可正常修改,但修改activeBoard.columns数组内的文本时,持续报错:

"Cannot assign to read only property 'board' of object"

已尝试将activeBoard.columns传入useState作为本地状态,但仍报错;若硬编码数组到useState则可正常编辑列输入框。

EditBoardModal.jsx

import React, {useState} from 'react';
import {useDispatch, useSelector} from "react-redux";
import {editBoard} from "../../reducers/board/boardSlice";
import {hideModal} from "../../reducers/modal/modalSlice";
import Button from "../Button/Button";
import "./EditBoardModal.scss";

const EditBoardModal = () => {
  const dispatch = useDispatch();
  const {activeBoard} =  useSelector(store => store.board);
  const [boardName,
    setBoardName] = useState(activeBoard.name);
  const [columns,
    setColumns] = useState([...activeBoard.columns]);

  const nameChangeHandler = (e) => {
    setBoardName(e.target.value)
  }

  const columnsChangeHandler = (i, e) => {
    let columnsValues = [...columns];
    columnsValues[i][e.target.name] = e.target.value;
    console.log(columnsValues[0][e.target.name]);
    setColumns(columnsValues);
  }


  return (
    <div className="edit-board-modal">
      <h3 className="edit-modal-title">Edit Board</h3>
      <div className="edit-board-name-div">
        <label>Board Name</label>
        <input
          value={boardName}
          onChange={nameChangeHandler}
          className="edit-task-title"
          type="text"
          name="edit board name"
          placeholder="e.g. Web Design"/>
      </div>
      <div className="edit-board-columns-div">
        <label>Board Columns</label>
        {columns.map((column, index) => (
          <div className="edit-columns-item-div" key={index}>
            <input
             onChange={e => columnsChangeHandler(index, e)}
              className="edit-column-input"
              type="text"
              name="board"
              value={column.board}
              placeholder="e.g. Web Design"/>
            <svg onClick={() => deleteColumn(index)} key={index} width="15" height="15" xmlns="http://www.w3.org/2000/svg">
              <g fill="#828FA3" fillRule="evenodd"><path d="m12.728 0 2.122 2.122L2.122 14.85 0 12.728z"/><path d="M0 2.122 2.122 0 14.85 12.728l-2.122 2.122z"/></g>
            </svg>
          </div>
        ))}
      </div>

      <Button
        onClick={() => dispatch(hideModal())}
        text="+ Add New Column"
        className="add-column-subtask"/>
      <Button
        onClick={() => {
          dispatch(hideModal())
          dispatch(editBoard(boardName));
        }}
        text="Save Changes"
        className="create-save-changes"/>

    </div>
  )
}

export default EditBoardModal

boardSlice.js

import {createSlice} from "@reduxjs/toolkit";

const boardSlice = createSlice({
  name: "board",
  initialState: {
    boards: [],
    activeBoard: null
  },
  reducers: {
    setActiveBoard: (state, {payload}) => {
      const getBoardByID = state.boards.find(el => el.id === payload);
      state.activeBoard = getBoardByID;
    },
    addBoard: (state, {payload}) => {
      const newBoard = {
        id: payload.id,
        name: payload.name,
        columns: payload.columns
      }
      if (state.boards.length === 0) {
        state.activeBoard = newBoard;
      }
      state.boards.push(newBoard);
    },
    editBoard: (state, {payload}) => {
      state.activeBoard.name = payload;
    },
    deleteCurrentBoard: (state, {payload}) => {
      const deletedBoard = state.boards.filter(item => item.id !== state.activeBoard.id);

      state.boards = deletedBoard;

      state.activeBoard = state.boards[0];
    },
    addColumn: (state, {payload}) => {
      state.activeBoard.columns.push(payload)
    }

  }
});

export const {
  addBoard,
  setActiveBoard,
  deleteCurrentBoard,
  addColumn,
  editBoard
} = boardSlice.actions;

export default boardSlice.reducer;
问题原因
  1. 浅拷贝导致的引用绑定:初始化columns状态时用[...activeBoard.columns]只是对数组做浅拷贝,数组内的每个column对象仍和Redux store中的对象共享引用。Redux Toolkit通过Immer冻结了store中的对象,外部直接修改这些对象的属性会触发只读限制。
  2. 硬编码数组无引用关联:硬编码的数组是全新的本地对象,没有和store对象绑定,因此可以正常修改。
解决方案

1. 深拷贝columns数组,断开store引用

初始化本地columns状态时,对每个column对象单独拷贝,彻底断开和store的引用:

const [columns, setColumns] = useState(
  activeBoard.columns.map(column => ({...column}))
);

2. 优化columnsChangeHandler,保证不可变更新

修改列属性时,拷贝目标column对象后再修改,保持不可变更新的规范:

const columnsChangeHandler = (i, e) => {
  setColumns(prevColumns => {
    const updatedColumns = [...prevColumns];
    updatedColumns[i] = {
      ...updatedColumns[i],
      [e.target.name]: e.target.value
    };
    return updatedColumns;
  });
};

3. 扩展editBoard reducer,支持columns更新

当前editBoard仅处理boardName,需要扩展为接收完整的board数据:
首先修改组件中的dispatch逻辑:

<Button
  onClick={() => {
    dispatch(hideModal())
    dispatch(editBoard({name: boardName, columns: columns}));
  }}
  text="Save Changes"
  className="create-save-changes"/>

然后更新boardSlice中的reducer:

editBoard: (state, {payload}) => {
  const boardIndex = state.boards.findIndex(board => board.id === state.activeBoard.id);
  if (boardIndex !== -1) {
    // 更新boards数组中的目标board
    state.boards[boardIndex] = {
      ...state.boards[boardIndex],
      name: payload.name,
      columns: payload.columns
    };
    // 同步更新activeBoard
    state.activeBoard = {...state.activeBoard, ...payload};
  }
},

4. 补充删除列函数(可选)

组件中deleteColumn未定义,补充实现:

const deleteColumn = (index) => {
  setColumns(prevColumns => prevColumns.filter((_, i) => i !== index));
};

内容的提问来源于stack exchange,提问作者envincebal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 15:45:28