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

React Redux中修改嵌套数组对象内座位status值的方法

React-Redux 电影选座功能座位状态更新实现

现有代码结构

开发电影选座功能时,初始定义的座位数据源结构如下:

data = [{
    class: "A",
    seat: [
      { id: "A1", price: 7.5, status: false },
      { id: "A2", price: 7.5, status: false },
      { id: "A3", price: 7.5, status: false },
      { id: "A4", price: 7.5, status: false },
    ],
  },
  {
    class: "B",
    seat: [
      { id: "B1", price: 7.5, status: false },
      { id: "B2", price: 7.5, status: false },
      { id: "B3", price: 7.5, status: false },
      { id: "B4", price: 7.5, status: false },
    ],
  }
]

项目中初始编写的movieReducer代码如下:

const stateMovie = {
  cart: [],
  data: data,
};
export const movieReducer = (state = stateMovie, action) => {
    case "ADD_TO_CART": {
let cloneCart = [...state.cart];
let index1 = cloneCart.findIndex((sp) => {
  return sp.id == action.seatItem.id;
});
let cloneData = [...state.data];
if (index1 == -1) {
  cloneCart.push(action.seatItem);
  action.seatItem.status = "selecting";
} 
state.data = cloneData;
state.cart = cloneCart;
return { ...state };
}
case "REMOVE_FROM_CART": {
let cloneCart = [...state.cart];
cloneCart.splice(action.index, 1);
let cloneData = [...state.data];
state.data = cloneData;
state.cart = cloneCart;
return { ...state };
}
case "CONFIRM_BOOKING": {
let cloneData = [...state.data]
state.data = cloneData;
return { ...state };
}

触发ADD_TO_CART动作选中座位后,state.data中对应座位的status字段会更新为'selecting',此时数据结构示例:

data = [{
    class: "A",
    seat: [
      { id: "A1", price: 7.5, status: false },
      { id: "A2", price: 7.5, status: false },
      { id: "A3", price: 7.5, status: false },
      { id: "A4", price: 7.5, status: false },
    ],
  },
  {
    class: "B",
    seat: [
      { id: "B1", price: 7.5, status: false },
      { id: "B2", price: 7.5, status: 'selecting' },
      { id: "B3", price: 7.5, status: false },
      { id: "B4", price: 7.5, status: false },
    ],
  }
]

需求说明

在CONFIRM_BOOKING动作的处理逻辑中,需要将已加入购物车的座位在state.data内对应的status值从'selecting'修改为true,实现必须符合Redux不可变数据更新规范。

符合规范的实现方案

Redux不可变更新核心规则:禁止直接修改原state上的任何属性,所有发生值变化的嵌套层级都要创建新的对象/数组副本,未修改的节点保留原引用,兼顾不可变要求和渲染性能。

首先修正原有reducer中浅拷贝不彻底、直接修改原对象引用的问题,再补全CONFIRM_BOOKING逻辑,完整代码如下:

const stateMovie = {
  cart: [],
  data: data,
};

export const movieReducer = (state = stateMovie, action) => {
  switch (action.type) {
    case "ADD_TO_CART": {
      const existSeat = state.cart.find(sp => sp.id === action.seatItem.id);
      if (existSeat) return state;
      // 分层创建副本更新座位状态
      const newData = state.data.map(row => {
        if (!row.seat.some(seat => seat.id === action.seatItem.id)) return row;
        return {
          ...row,
          seat: row.seat.map(seat => {
            if (seat.id === action.seatItem.id) {
              return { ...seat, status: "selecting" };
            }
            return seat;
          })
        }
      });
      return {
        ...state,
        cart: [...state.cart, { ...action.seatItem, status: "selecting" }],
        data: newData
      };
    }

    case "REMOVE_FROM_CART": {
      const removedSeat = state.cart[action.index];
      const newData = state.data.map(row => {
        if (!row.seat.some(seat => seat.id === removedSeat.id)) return row;
        return {
          ...row,
          seat: row.seat.map(seat => {
            if (seat.id === removedSeat.id) {
              return { ...seat, status: false };
            }
            return seat;
          })
        }
      });
      return {
        ...state,
        cart: state.cart.filter((_, idx) => idx !== action.index),
        data: newData
      };
    }

    case "CONFIRM_BOOKING": {
      // 用Set存储选中座位ID,把匹配时间复杂度从O(n)降到O(1)
      const selectedSeatIds = new Set(state.cart.map(seat => seat.id));
      const newData = state.data.map(row => {
        // 提前判断当前行是否有需要更新的座位,无更新直接返回原行减少性能消耗
        const hasUpdateSeat = row.seat.some(seat => selectedSeatIds.has(seat.id) && seat.status === "selecting");
        if (!hasUpdateSeat) return row;
        // 需要更新的行创建新对象副本,座位数组同步生成新副本
        return {
          ...row,
          seat: row.seat.map(seat => {
            if (selectedSeatIds.has(seat.id) && seat.status === "selecting") {
              return { ...seat, status: true };
            }
            return seat;
          })
        }
      });
      // 确认购票后清空购物车,返回全新state对象
      return {
        ...state,
        cart: [],
        data: newData
      };
    }

    default:
      return state;
  }
}

实现要点

  • 全程没有直接修改原state或原嵌套对象的属性,所有更新都通过展开运算符生成新副本
  • 遍历过程中跳过无修改的节点,保留原引用,避免不必要的组件重渲染
  • 修正了原有逻辑中直接修改action.seatItem、浅拷贝数组后仍操作嵌套引用的问题,避免状态污染
  • 确认购票后自动清空购物车,符合常规选座购票流程逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 03:51:26