Nathan H
Nathan H

Reputation: 49451

React input setState for array of objects

Look at the form to edit phone numbers in Google Contacts.

enter image description here

Let's say my state looks like:

    this.state = {
        phones: [
            {
                country: 'US',
                label: 'Home',
                number: '555'
            }
        ],

    };

My HTML would look like, in a loop and simplified to be all text inputs:

<input type="text" value={this.state.phones[index].country} onChange={this.handleChange} />
<input type="text" value={this.state.phones[index].number} onChange={this.handleChange} />
<input type="text" value={this.state.phones[index].label} onChange={this.handleChange} />

How would you implement the handleChange to call setState while supporting the fact that it is an array, and that each item in the array has multiple properties?

I saw pieces of answers in other questions, but nothing complete.

Upvotes: 7

Views: 8496

Answers (2)

Paul Fitzgerald
Paul Fitzgerald

Reputation: 12129

You can do something like the following and also make a Phone component.

class Phone extends React.Component {

  render() {
    return (
      <div className="App">
        <p>{this.props.label}</p>
        <input
          type="number"
          onChange={this.props.updateNumber}
          value={this.props.number}
        />
      </div>
    );
  }
}

class App extends React.Component {
  state = {
    phones: [
      {
        name: "US",
        number: ""
      },
      {
        name: "UK",
        number: ""
      }
    ]
  };

  updateNumber = (e, index) => {
    const phones = this.state.phones;
    phones[index].number = e.target.value;
    this.setState({
      phones
    });
  };

  render() {
    return (
      <div className="App">
        {this.state.phones.map((phone, i) => {
          return (
            <Phone
              updateNumber={e => {
                this.updateNumber(e, i);
              }}
              number={this.state.phones[i].number}
              label={phone.name}
            />
          );
        })}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'></div>

Upvotes: 4

ryangoree
ryangoree

Reputation: 1934

2 options

  1. Make a Phone component with it's own state as suggested by Paul Fitzgerald.

  2. Add the index to a data-index attribute and check for the value in handleChange.

Example of 2:

<input type="text" value={this.state.phones[index].country} data-index="0" onChange={this.handleChange} />
<input type="text" value={this.state.phones[index].number} data-index="0" onChange={this.handleChange} />
<input type="text" value={this.state.phones[index].label} data-index="0" onChange={this.handleChange} />

then in handleChange:

handleChange(e) {
    ...
    const i = e.target.dataset.index;
    ...
}

Upvotes: 0

Related Questions