【问题标题】:How to setState of nested JSON array object of mapped input如何设置映射输入的嵌套 JSON 数组对象的状态
【发布时间】: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;

【问题讨论】:

标签: javascript arrays json reactjs object


【解决方案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】:

      让我们从下往上开始

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

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

      3. 然后在主组件中获得对 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;
      

      【讨论】:

        猜你喜欢
        • 2018-01-10
        • 2021-07-26
        • 1970-01-01
        • 1970-01-01
        • 2021-09-30
        • 2021-06-20
        • 2022-07-07
        • 2021-02-08
        • 2023-01-23
        相关资源
        最近更新 更多