Как установить состояние вложенного массива JSON объекта сопоставленного ввода - PullRequest
2 голосов
/ 09 июня 2019

У меня есть 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;

Ответы [ 4 ]

0 голосов
/ 09 июня 2019

Передайте категорию и индекс предмета вашей функции 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;

DEMO




const Item = ({ list, handleChange, categoryIndex, itemIndex, value }) => { return (
handleChange(e, categoryIndex, itemIndex)} />
); }; const Category = ({ name, list, handleChange, categoryIndex }) => { return (

{name}

{list.fields.map((item, index) => ( ))}
); }; 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 (
{this.state.list.map((item, index) => ( ))}
{this.state.show &&
          {JSON.stringify(this.state.list, null, 4)}
          
}
); } } ReactDOM.render ( , document.getElementById ( 'корень') );
0 голосов
/ 09 июня 2019

давайте начнем снизу вверх

  1. , вам необходимо указать идентификатор Item.js его родительской категории, изменив идентификатор на id={${name},${item.name}}.также было бы неплохо добавить событие onClick для очистки предыдущих данных

  2. компонент категории, который необходимо предоставить, это 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;
0 голосов
/ 09 июня 2019

Обновите ваш код следующим образом

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;

А вот и рабочая демоверсия

0 голосов
/ 09 июня 2019

Ваш 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 класса проверьте, является ли список массивом и его длина больше 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: []
    };
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...