Я пытаюсь создать неизменяемый массив состояний, который динамически заполняется входными данными. Однако я не могу создать новое состояние и добавить его в свой массив 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>
)}
есть, см. демо @RoboRobok
Ваш handleChangeEvent не будет работать, потому что вы не укажете индекс в массиве people для обновления. Все, что вы передаете, - это {name: 'name'}, но как ваш код узнает, кого он должен обновить?
@RoboRobok входы должны управлять своим собственным состоянием для каждого объекта в массиве
@ChaseDeAnda, как я могу это исправить, чтобы управлять каждым человеком?
@ alt-rock Проверьте мой ответ



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы должны передать индекс в 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')
);
Вам необходимо обновить функцию 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 справа, но id не был установлен в данных примера, а все остальные поля могут быть изменены, так что это было то, что осталось.
@Omar также, использование индекса в качестве ключа доставит вам неприятности только в том случае, если порядок списка будет часто меняться. В этом примере нет удаления, поэтому порядок всегда будет одинаковым.
Я знаю, что все, что я говорю, это лучше сделать все возможное и реализовать это для спрашивающего и для будущих зрителей. Ваш ответ хороший, очень легко понять, и спрашивающий, вероятно, теперь может сделать то, что я говорю, самостоятельно. Да, нет удаления, но я предполагаю, что с этим он столкнется когда-нибудь в будущем.
@Omar __ отметил.
Вам нужно немного изменить свой код, чтобы это работало.
Ознакомьтесь с моим решением. Демо 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-*.
Надеюсь это поможет.
Почему вы хотите добавить при изменении ввода? Я считаю, что должна быть кнопка или что-то в этом роде.