如何设置映射输入的嵌套 JSON 数组对象的状态

Posted

技术标签:

【中文标题】如何设置映射输入的嵌套 JSON 数组对象的状态【英文标题】:How to setState of nested JSON array object of mapped input 【发布时间】:2019-10-24 03:36:40 【问题描述】:

我有一个JSON 文件,其中包含多个类别,每个类别都有一个名称,其中包含一组具有自己名称和值的输入字段。

如何使用setState 更新每个onChange 的值字段?类别和字段使用map() 呈现。

我可以让它在没有嵌套字段的情况下工作,但不能。感谢任何帮助。

JSON 文件

[
    "catName": "Category 1",
    "fields": [
      
        "name": "field 1",
        "amount": "0"
      ,
      
        "name": "field 2",
        "amount": "0"
      
    ]
  ,
  
    "catName": "Category 2",
    "fields": [
      
        "name": "field 1",
        "amount": "0"
      ,
      
        "name": "field 2",
        "amount": "0"
      
]

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 => 
    this.setState( ???  );
  ;

  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;

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
          handleChange=handleChange
        />
      ))
    </div>
  );
;

export default Category;

Item.js

import React from "react";

const Item = ( list, handleChange ) => 
  return (
    <div className="item">
      <label className="label">list.name</label>
      <input
        name=list.name
        id=list.name
        className="input"
        type="text"
        onChange=handleChange
        value=list.amount
      />
    </div>
  );
;

export default Item;

【问题讨论】:

你遇到了什么错误? 好的,***.com/help/someone-answers 【参考方案1】:

将类别和项目索引传递给您的handleChange 函数。使用这些索引来更新数组中的正确项目。通过不做来避免状态突变

// state mutation
this.state.list[categoryIndex].fields[fieldIndex].amount = e.target.value

handleChange 函数

handleChange = (e, categoryIndex, itemIndex) => 

  const  list  = this.state;

  const fields = [...list[categoryIndex].fields.slice(0, itemIndex),
  Object.assign(, list[categoryIndex].fields[itemIndex],  amount: e.target.value ),
  ...list[categoryIndex].fields.slice(itemIndex + 1)
  ]


  this.setState(
    list: [...list.slice(0, categoryIndex),
    Object.assign(, list[categoryIndex],  fields ),
    ...list.slice(categoryIndex + 1)
    ]
  )

Item 组件,添加类别和归档索引作为道具。

import React from "react";

const Item = ( list, handleChange, categoryIndex, itemIndex, value ) => 
  return (
    <div className="item">
      <label className="label">list.name</label>
      <input
        name=list.name
        id=list.name
        className="input"
        type="text"
        value=value
        onChange=(e) => handleChange(e, categoryIndex, itemIndex)
      />
    </div>
  );
;

export default Item;

分类组件

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

const Category = ( name, list, handleChange, categoryIndex ) => 
  return (
    <div className="section">
      <h3>name</h3>
      list.fields.map((item, index) => (
        <Item
          id=item.name
          name=item.name
          key=item.name
          list=item
          categoryIndex=categoryIndex
          itemIndex=index
          value=item.amount
          handleChange=handleChange
        />
      ))
    </div>
  );
;

export default Category;

演示

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>

<script type="text/babel">

const Item = ( list, handleChange, categoryIndex, itemIndex, value ) => 
  return (
    <div className="item">
      <label className="label">list.name</label>
      <input
        name=list.name
        id=list.name
        className="input"
        type="text"
        value=value
        onChange=(e) => handleChange(e, categoryIndex, itemIndex)
      />
    </div>
  );
;

const Category = ( name, list, handleChange, categoryIndex ) => 
  return (
    <div className="section">
      <h3>name</h3>
      list.fields.map((item, index) => (
        <Item
          id=item.name
          name=item.name
          key=item.name
          list=item
          categoryIndex=categoryIndex
          itemIndex=index
          value=item.amount
          handleChange=handleChange
        />
      ))
    </div>
  );
;

class App extends React.Component 
  constructor() 
    super();
    this.state = 
      name: 'React',
      show: false,
      list: [
        
          "catName": "Category 1",
          "fields": [
            
              "name": "field 1",
              "amount": "0"
            ,
            
              "name": "field 2",
              "amount": "0"
            
          ]
        ,
        
          "catName": "Category 2",
          "fields": [
            
              "name": "field 1",
              "amount": "0"
            ,
            
              "name": "field 2",
              "amount": "0"
            
          ]
        
      ]
    ;
  

  handleChange = (e, categoryIndex, itemIndex) => 

    const  list  = this.state;

    const fields = [...list[categoryIndex].fields.slice(0, itemIndex),
    Object.assign(, list[categoryIndex].fields[itemIndex],  amount: e.target.value ),
    ...list[categoryIndex].fields.slice(itemIndex + 1)
    ]


    this.setState(
      list: [...list.slice(0, categoryIndex),
      Object.assign(, list[categoryIndex],  fields ),
      ...list.slice(categoryIndex + 1)
      ]
    )
  
  
  show = () => 
    this.setState(
      show: true
    )
  

  render() 
    return (
      <div>
        this.state.list.map((item, index) => (
          <Category
            id=item.catName
            name=item.catName
            key=item.catName
            categoryIndex=index
            list=item
            handleChange=this.handleChange
          />
        ))
        <br />
        <button onClick=this.show>Show changes</button>
        this.state.show &&
          <pre>
          JSON.stringify(this.state.list, null, 4)
          </pre>
        
      </div>
    );
  


ReactDOM.render(
    <App />,
    document.getElementById('root')
);
</script>

【讨论】:

感谢您的努力和快速响应,非常适合我。非常感激。您是否有机会解释或提供有关 handleChange 中到底发生了什么的背景?再次感谢 我正在使用切片和对象分配来创建一个新数组,而不是直接更改数组。 Object.assign, .slice()【参考方案2】:

您的 JSON 无效。您还忘记检查list 是否已包含任何数据。

试试这个:

在您的 handleChange 方法中确保使用正确的 JSON 标记。你忘了结束]

 this.setState( list: [
      "catName": "Category 1",
      "fields": [
        
          "name": "field 1",
          "amount": "0"
        ,
        
          "name": "field 2",
          "amount": "0"
        
      ]
    ,
    
      "catName": "Category 2",
      "fields": [
        
          "name": "field 1",
          "amount": "0"
        ,
        
          "name": "field 2",
          "amount": "0"
        
    ]
  ])

Main 类的 render 方法中检查列表是否为数组以及其长度是否大于 0。这将防止任何渲染错误,以防设置了非数组类型的值。

   Array.isArray(this.state.list) && this.state.list.length < 0 && this.state.list.map(item => (
      <Category
        id=item.catName
        name=item.catName
        key=item.catName
        list=item
        handleChange=this.handleChange
      />
    ))

还要确保在主类的构造函数中设置一个空数组:

constructor(props) 
    super(props);
    this.state = 
      list: []
    ;
 

【讨论】:

【参考方案3】:

如下更新你的代码

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, fieldName, catName) => 
    //get list from state
    const  list  = this.state

    //this returns the related item's index, I assume that all cats have a unique name, otherwise you should use unique values such as IDs
    const targetCatIndex = list.findIndex(item => item.catName === catName) 

    //find related field index
    const targetFieldIndex = list[targetCatIndex].fields.findIndex(item => item.name === fieldName)

    //update the field and assign to state
    list[targetCatIndex].fields[targetFieldIndex].amount = e.target.value

    this.setState( list: list  );
  ;

  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;



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
          // pass field and cat referance with input event
          handleChange=(e, fieldName) => handleChange(e, fieldName, name)  
        />
      ))
    </div>
  );
;

export default Category;


import React from "react";

const Item = ( list, handleChange ) => 
  return (
    <div className="item">
      <label className="label">list.name</label>
      <input
        name=list.name
        id=list.name
        className="input"
        type="text"
        //pass related field referance here
        onChange=(e) => handleChange(e, list.name)
        value=list.amount
      />
    </div>
  );
;

export default Item;

这里是working demo

【讨论】:

【参考方案4】:

让我们从下往上开始

    您需要通过以下方式向 Item.js 提供其父类别的 ID 将 id 更改为id=$name,$item.name。也可以添加 onClick 事件来清理以前的数据

    类别组件需要提供给item组件的id

    然后在主组件中获得对 json 的正确访问权限后,您可以使用 createNewData 方法创建新对象

这是结果:

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
    ;
  

  createNewData = (mainAccess, property, value) => 
    let newData = sampleData;
    newData.forEach(category => 
      if (category["catName"] === mainAccess) 
        debugger;
        category["fields"].forEach(item => 
          if (item["name"] === property) 
            console.log(item["amount"]);
            item["amount"] = value;
          
        );
      
    );
    return newData
  ;

  handleChange = e => 
    const propertyAccess = e.target.id.split(",");
    const newData = this.createNewData(propertyAccess[0],propertyAccess[1],e.target.value)
    this.setState(list:newData)
  ;

  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;

Item.js

import React from "react";


const Item = ( list, handleChange ,id) => 

    return (
    <div className="item">
      <label className="label">list.name</label>
      <input
        name=list.name
        id=id
        className="input"
        type="text"
        onChange=handleChange
        onClick=e=>e.target.value=""
        value=list.amount

      />
    </div>
  );
;

export default 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=`$name,$item.name`
          name=item.name
          key=item.name
          list=item
          handleChange=handleChange
        />
      ))
    </div>
  );
;

export default Category;

【讨论】:

以上是关于如何设置映射输入的嵌套 JSON 数组对象的状态的主要内容,如果未能解决你的问题,请参考以下文章

如何将具有嵌套对象的复杂 json 文件映射到 java 对象?

如何从 JSON 映射嵌套数组?

带有数组映射的restkit嵌套对象

Flutter:如何映射嵌套的 json 对象

Restkit 0.20 嵌套对象数组映射问题

RestKit:将嵌套数组映射到对象