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

如何用React和Redux修改数组中的元素?

在Redux中修改数组指定元素的正确姿势

当然可行!在Redux里更新数组中的某个元素是非常常见的需求,但一定要严格遵守Redux的不可变更新原则——你绝对不能直接修改原state,必须返回一个全新的状态对象,这也是你想使用Object.assign()的原因,这个思路完全正确。

先说说你现有代码里的问题

  1. Reducer的EDIT_COMPONENT逻辑错误:你现在的写法是直接试图修改state.co,但你的状态结构里co是components数组中每个元素的属性,而且还写错了Object.assign(写成了Object.assing),完全没处理数组的更新。
  2. 容器组件的事件参数缺失:你的编辑按钮点击时没有把当前组件的内容和id传递给onEditComponent,导致reducer拿不到要修改的目标元素和新值。
  3. 小细节问题:容器组件里的this.props.compons虽然是自定义别名,但编辑按钮没关联输入框的值,没法获取用户要修改的内容。

修正后的Reducer代码

在EDIT_COMPONENT的case里,我们需要遍历数组,找到对应id的元素,返回一个新数组——新数组里除了目标元素被替换成更新后的版本,其他元素都保持原样。这里用map方法实现不可变更新最直观:

import * as actionTypes from '../actions';

const iniState = { components: [] };

const rootReducer = (state = iniState, action) => {
  switch (action.type) {
    case actionTypes.ADD_COMPONENT:
      const newComponent = { 
        id: Math.random(), 
        co: action.data.compToReducer 
      };
      return { 
        ...state, 
        components: state.components.concat(newComponent) 
      };
    case actionTypes.DELETE_COMPONENT:
      return { 
        ...state, 
        components: state.components.filter(component => component.id !== action.index) 
      };
    case actionTypes.EDIT_COMPONENT:
      // 生成新数组,只更新目标元素
      const updatedComponents = state.components.map(component => {
        if (component.id === action.data.index) {
          // 返回新的元素对象,不修改原数据
          return { ...component, co: action.data.componentToReducer };
        }
        // 其他元素直接返回原对象
        return component;
      });
      // 返回全新的state对象
      return { ...state, components: updatedComponents };
    default:
      return state;
  }
};

export default rootReducer;

修正后的容器组件代码

主要是给编辑按钮绑定正确的事件逻辑,获取输入框的值并传递给Redux的action:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import FoodComponent from '../../components/FoodComponent/FoodComponent';
import AddComponent from '../../components/Buttons/AddComponent';
import * as actionTypes from '../../store/actions';
import classes from './FoodComponents.scss';

class FoodComponents extends Component {
  // 处理编辑逻辑:获取输入值并触发action
  handleEdit = (componentId) => {
    // 获取当前输入框的内容
    const inputEl = event.target.previousElementSibling;
    const newValue = inputEl.value.trim();
    if (newValue) {
      this.props.onEditComponent(newValue, componentId);
      inputEl.value = ''; // 清空输入框
    }
  };

  render() {
    return (
      <div>
        <AddComponent text="Add component" click={this.props.onAddComponent} />
        <ul>
          { this.props.compons.map(component => (
            <li key={component.id} >
              <p className={classes.Component}>{component.co}</p>
              <input type="text" placeholder="输入新内容" />
              {/* 点击时传递当前组件的id */}
              <button onClick={() => this.handleEdit(component.id)}>
                Edit Component
              </button>
              <button onClick={() => this.props.onDeleteComponent(component.id)}>
                Delete component
              </button>
            </li>
          )) }
        </ul>
      </div>
    )
  }
}

const mapStateToProps = state => {
  return { 
    compons: state.components 
  }
};

const mapDispatchToProps = dispatch => {
  return { 
    onAddComponent: (component) => dispatch({
      type: actionTypes.ADD_COMPONENT, 
      data: {compToReducer: component}
    }),
    onDeleteComponent: (id) => dispatch({
      type: actionTypes.DELETE_COMPONENT, 
      index: id 
    }),
    onEditComponent: (component, id) => dispatch({
      type: actionTypes.EDIT_COMPONENT, 
      data:{componentToReducer: component, index: id}
    })
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(FoodComponents);

补充说明

  • 为什么必须返回新状态?Redux的reducer是纯函数,依赖状态的引用变化来触发组件更新。如果直接修改原数组或对象,Redux会认为状态没有变化,相关组件不会重新渲染。
  • 除了map方法,你也可以用Object.assign或者展开运算符创建新元素,核心都是不碰原数据,只生成新副本。
  • 上面用了获取DOM元素的方式拿输入值,你也可以把输入框改成受控组件,用组件自身的state管理输入内容,这会更符合React的最佳实践。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:15:17