You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何在React中更新嵌套JSON数组对象的state(映射输入场景)

解决React嵌套分类字段的state更新问题

这个问题我之前也碰到过,嵌套state的更新确实容易踩坑——核心难点在于准确定位到要修改的嵌套字段,同时必须遵循React的不可变更新原则(不能直接修改state里的原对象/数组,必须返回新的副本)。下面给你两种可行的解决方案:

方法一:通过分类名称+字段名称定位(直观易读)

这种方式依赖你的catNamefield.name是唯一的,适合数据命名规范的场景。

步骤1:传递分类名称到Item组件

先修改Category.js,把当前分类的名称传递给子组件Item

// Category.js
import React from "react";
import Item from "./Item";

const Category = ({ name, list, handleChange }) => {
  return (
    <div className="section">
      <h3>{name}</h3>
      {list.fields.map(item => (
        <Item 
          id={item.name} 
          name={item.name} 
          key={item.name} 
          list={item} 
          catName={name} {/* 新增:传递当前分类名称 */}
          handleChange={handleChange} 
        />
      ))}
    </div>
  );
};

export default Category;

步骤2:修改Item的onChange,传递定位参数

Item.js里,让onChange事件触发时,把分类名称、字段名称和输入值一起传给父组件:

// Item.js
import React from "react";

const Item = ({ list, catName, handleChange }) => {
  return (
    <div className="item">
      <label className="label">{list.name}</label>
      <input 
        name={list.name} 
        id={list.name} 
        className="input" 
        type="text" 
        onChange={(e) => handleChange(e, catName, list.name)} {/* 新增:传递分类和字段名称 */}
        value={list.amount} 
      />
    </div>
  );
};

export default Item;

步骤3:实现handleChange的嵌套更新逻辑

最后在Main.js里,通过prevState获取当前状态,用map生成新的数组和对象,精准更新目标字段:

// Main.js
import React, { Component } from "react";
import Category from "./Category";
import sampleData from "./sampleData";

class Main extends Component {
  constructor(props) {
    super(props);
    this.state = { list: sampleData };
  }

  handleChange = (e, catName, fieldName) => {
    const newAmount = e.target.value;
    // 如果需要处理数字,可以转成Number:const newAmount = Number(e.target.value);
    this.setState(prevState => ({
      list: prevState.list.map(category => {
        // 找到目标分类
        if (category.catName === catName) {
          return {
            ...category, // 复制原分类的所有属性
            fields: category.fields.map(field => {
              // 找到目标字段,更新amount
              if (field.name === fieldName) {
                return { ...field, amount: newAmount };
              }
              return field; // 其他字段保持不变
            })
          };
        }
        return category; // 其他分类保持不变
      })
    }));
  };

  render() {
    return (
      <div>
        {this.state.list.map(item => (
          <Category id={item.catName} name={item.catName} key={item.catName} list={item} handleChange={this.handleChange} />
        ))}
      </div>
    );
  }
}

export default Main;

方法二:通过索引定位(性能更优,无命名依赖)

如果你的分类或字段可能存在同名情况,用索引定位更可靠,而且比较索引的性能比比较字符串更好。

步骤1:传递分类和字段的索引

修改Main.js的render,传递分类的索引:

// Main.js 中的render部分
{this.state.list.map((item, categoryIndex) => (
  <Category 
    id={item.catName} 
    name={item.catName} 
    key={item.catName} 
    list={item} 
    categoryIndex={categoryIndex} {/* 新增:分类索引 */}
    handleChange={this.handleChange} 
  />
))}

修改Category.js的render,传递字段的索引:

// Category.js 中的render部分
{list.fields.map((item, fieldIndex) => (
  <Item 
    id={item.name} 
    name={item.name} 
    key={item.name} 
    list={item} 
    categoryIndex={categoryIndex} {/* 新增:分类索引 */}
    fieldIndex={fieldIndex} {/* 新增:字段索引 */}
    handleChange={handleChange} 
  />
))}

步骤2:修改Item的onChange传递索引

// Item.js 中的input部分
<input 
  name={list.name} 
  id={list.name} 
  className="input" 
  type="text" 
  onChange={(e) => handleChange(e, categoryIndex, fieldIndex)} {/* 传递索引 */}
  value={list.amount} 
/>

步骤3:实现基于索引的更新逻辑

// Main.js 中的handleChange
handleChange = (e, categoryIndex, fieldIndex) => {
  const newAmount = e.target.value;
  this.setState(prevState => {
    // 逐层创建副本,保证不可变性
    const newList = [...prevState.list];
    const updatedCategory = { ...newList[categoryIndex] };
    const updatedFields = [...updatedCategory.fields];
    
    // 更新目标字段的amount
    updatedFields[fieldIndex] = { ...updatedFields[fieldIndex], amount: newAmount };
    
    // 把更新后的层级放回原结构
    updatedCategory.fields = updatedFields;
    newList[categoryIndex] = updatedCategory;
    
    return { list: newList };
  });
};

关键注意点

  • 永远不要直接修改state里的原对象/数组,比如prevState.list[0].fields[0].amount = newAmount这种写法是错误的,React无法检测到这种修改,不会触发重新渲染。
  • 如果你的数据层级更深,或者更新逻辑更复杂,可以考虑使用Immer库来简化不可变更新的代码,它允许你用“可变”的写法生成不可变的结果。

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

火山引擎 最新活动