REACT: объединить объекты с состоянием в массив

Я пытаюсь создать неизменяемый массив состояний, который динамически заполняется входными данными. Однако я не могу создать новое состояние и добавить его в свой массив people. Как правильно это сделать? Ниже приведена ссылка на демонстрацию проблемы с помощью jsfiddle.

ССЫЛКА НА ДЕМО: http://jsfiddle.net/560fyjnp/1536/

Отображение и возврат добавленного массива:

appendPerson() {
    let newPerson = {
      name: '',
      age: '',
      school: ''
    };

    this.setState((prevState) => ({
      people: prevState.people.concat([newPerson])
    }));
  }

Обработка изменений из входов:

  handleChangeEvent(e) {
    this.setState({[e.target.name]: e.target.value})
  }

Пример ввода:

Name: <input name = "name" value = {person.name} onChange = {this.props.handleChangeEvent} />

Сопоставление состояния и отображение строк с отслеживанием состояния:

    {this.state.people.map((person, index) =>
    <div key = {index}>
     Name: {person.name}<br />
     Age: {person.age}<br />
     School: {person.school}
     <hr/>
    </div>
    )}

Почему вы хотите добавить при изменении ввода? Я считаю, что должна быть кнопка или что-то в этом роде.

Robo Robok 27.03.2018 22:01

есть, см. демо @RoboRobok

alt-rock 27.03.2018 22:02

Ваш handleChangeEvent не будет работать, потому что вы не укажете индекс в массиве people для обновления. Все, что вы передаете, - это {name: 'name'}, но как ваш код узнает, кого он должен обновить?

Chase DeAnda 27.03.2018 22:03

@RoboRobok входы должны управлять своим собственным состоянием для каждого объекта в массиве

alt-rock 27.03.2018 22:05

@ChaseDeAnda, как я могу это исправить, чтобы управлять каждым человеком?

alt-rock 27.03.2018 22:07

@ alt-rock Проверьте мой ответ

Chase DeAnda 27.03.2018 22:13
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
6
67
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы должны передать индекс в handleChangeEvent. Рабочий пример: http://jsfiddle.net/560fyjnp/1551/

class Inputs extends React.Component {
  constructor(props) {
    super(props);
    this.state = {}
  }

  render() {
    return (
      <div>
        {this.props.people.map((person, index) =>
            <div key = {index}>
          Name: <input name = "name" data-index = {index} value = {person.name} onChange = {this.props.handleChangeEvent} /><br/>
          Age: <input name = "age" data-index = {index} value = {person.age} onChange = {this.props.handleChangeEvent} /><br/>
          School: <input name = "school" data-index = {index} value = {person.school} onChange = {this.props.handleChangeEvent} />
            <hr />
          </div>
         )}
         <button onClick = { this.props.appendPerson }>Add Another Person</button>
      </div>
    );
  }
}

class People extends React.Component {
    constructor(props) {
    super(props);

    this.state = {
      people: [{
        name: 'Jerry',
        age: '20',
        school: 'Fun University'
      }]
    }

      this.handleChangeEvent = this.handleChangeEvent.bind(this);
      this.appendPerson = this.appendPerson.bind(this);
  }


  handleChangeEvent(e) {
    this.setState((prevState)=>{
        prevState.people[parseInt(e.target.getAttribute('data-index'))][e.target.name]= e.target.value;
      return prevState;
    })
  }

  appendPerson() {
    let newPerson = {
      name: '',
      age: '',
      school: ''
    };

    //this.setState({ people: this.state.people.concat([newPerson])});
    this.setState((prevState) => ({
      people: prevState.people.concat([newPerson])
    }));
  }

  render() {
    return (
      <div>
        <h1>Add Person:</h1>
        <Inputs
          people = {this.state.people}
          appendPerson = {this.appendPerson}
          handleChangeEvent = {this.handleChangeEvent}
        />

        <h1>People:</h1>
        {this.state.people.map((person, index) =>
        <div key = {index}>
         Name: {person.name}<br />
         Age: {person.age}<br />
         School: {person.school}
         <hr/>
        </div>
        )}
      </div>
    );
  }
}

ReactDOM.render(
  <People/>,
  document.getElementById('container')
);
Ответ принят как подходящий

Рабочий JSFiddle здесь

Вам необходимо обновить функцию handleChangeEvent, чтобы она приняла индекс, который будет передан в качестве параметра. Вы должны bind обработчик изменений в Inputs передать индекс:

  handleChangeEvent(index, e) {
    // Make a shallow clone of your state array
    const people = [...this.state.people];
    // Update the object at the passed in index with the property name and value
    people[index] = {...people[index], [e.target.name]: e.target.value};
    this.setState({
        people
    });
  }

Полный пример:

class Inputs extends React.Component {
  constructor(props) {
    super(props);
    this.state = {}
  }

  render() {
    return (
      <div>
        {this.props.people.map((person, index) =>
            <div key = {index}>
          Name: <input name = "name" value = {person.name} onChange = {this.props.handleChangeEvent.bind(this, index)} /><br/>
          Age: <input name = "age" value = {person.age} onChange = {this.props.handleChangeEvent.bind(this, index)} /><br/>
          School: <input name = "school" value = {person.school} onChange = {this.props.handleChangeEvent.bind(this, index)} />
            <hr />
          </div>
         )}
         <button onClick = { this.props.appendPerson }>Add Another Person</button>
      </div>
    );
  }
}

class People extends React.Component {
    constructor(props) {
    super(props);

    this.state = {
      people: [{
        name: 'Jerry',
        age: '20',
        school: 'Fun University'
      }]
    }

      this.handleChangeEvent = this.handleChangeEvent.bind(this);
      this.appendPerson = this.appendPerson.bind(this);
  }


  handleChangeEvent(index, e) {
    const people = [...this.state.people];
    people[index] = {...people[index], [e.target.name]: e.target.value};
    this.setState({
        people
    });
  }

  appendPerson() {
    let newPerson = {
      name: '',
      age: '',
      school: ''
    };

    //this.setState({ people: this.state.people.concat([newPerson])});
    this.setState((prevState) => ({
      people: prevState.people.concat([newPerson])
    }));
  }

  render() {
    return (
      <div>
        <h1>Add Person:</h1>
        <Inputs
          people = {this.state.people}
          appendPerson = {this.appendPerson}
          handleChangeEvent = {this.handleChangeEvent}
        />

        <h1>People:</h1>
        {this.state.people.map((person, index) =>
        <div key = {index}>
         Name: {person.name}<br />
         Age: {person.age}<br />
         School: {person.school}
         <hr/>
        </div>
        )}
      </div>
    );
  }
}

ReactDOM.render(
  <People/>,
  document.getElementById('container')
);

Хороший ответ, но использование индекса в качестве ключа - это антипаттерн, поэтому я считаю, что этот ответ неполный. Определенно следует использовать уникальный идентификатор человека, который изменит способ реализации handleChangeEvent.

Omar 27.03.2018 23:29

@Omar справа, но id не был установлен в данных примера, а все остальные поля могут быть изменены, так что это было то, что осталось.

Chase DeAnda 27.03.2018 23:31

@Omar также, использование индекса в качестве ключа доставит вам неприятности только в том случае, если порядок списка будет часто меняться. В этом примере нет удаления, поэтому порядок всегда будет одинаковым.

Chase DeAnda 27.03.2018 23:32

Я знаю, что все, что я говорю, это лучше сделать все возможное и реализовать это для спрашивающего и для будущих зрителей. Ваш ответ хороший, очень легко понять, и спрашивающий, вероятно, теперь может сделать то, что я говорю, самостоятельно. Да, нет удаления, но я предполагаю, что с этим он столкнется когда-нибудь в будущем.

Omar 27.03.2018 23:32

@Omar __ отметил.

Chase DeAnda 27.03.2018 23:34

Вам нужно немного изменить свой код, чтобы это работало.

Ознакомьтесь с моим решением. Демо JSFiddle

Вот как должна работать функция onChange в элементах input.

<input name = "name" value = {person.name} onChange = {({ target }) => this.props.handleChangeEvent(index, target.name, target.value)} />

А еще есть функция handleChangeEvent.

handleChangeEvent(peopleIndex, name, value) {
    this.setState(prevState => {
      const editedPeople = prevState.people[peopleIndex];
      editedPeople[name] = value;
  
      return {
        people: [
          ...prevState.people.slice(0, peopleIndex),
          editedPeople,
          ...prevState.people.slice(peopleIndex + 1)
        ]
      };
    });
  }

Вот и все. Независимые структуры данных, связанные с входами без атрибута data-*.

Надеюсь это поможет.

Другие вопросы по теме