temporary_user_name
temporary_user_name

Reputation: 37068

Is there a neater way to connect an input field to a state property in React than onChange?

So look at this code below for our example, a simple 2-way data-binding on an input field connecting the field to a property inputValue.

But say you have a more complex page with 30 or more inputs. Are you supposed to write 30+ onChange handlers in the class, all with different names corresponding to the inputs like onNameChange, onEmailChange, onPhoneChange, and so on? Is there no neater, more implicit way to bind inputs than what I have below here?

React.createClass({
  getInitialState() {
    inputValue: ''
  },
  render() {
    return (
      <input
        type='text'
        value={this.state.inputValue}
        onChange={this.onChange} />
    );
  },
  onChange(e) {
    this.setState({ inputValue: e.target.value });
  }
});

Edit: I suppose I could do this and avoid writing handlers on the class:

<input onChange={ e => this.setState({firstName: e.target.value}) } />

Is that kosher?

Upvotes: 4

Views: 3800

Answers (2)

radix
radix

Reputation: 770

React docs has your solution:

https://facebook.github.io/react/docs/forms.html#handling-multiple-inputs

    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: ''};

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }

   handleInputChange(event) {
    const target = event.target;    
    this.setState({
      [target.name]: target.value
    });
  }

      handleSubmit(event) {
        alert('A name was submitted: ' + this.state.value);
        event.preventDefault();
      }

      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              Name:
              <input name="name" type="text" value={this.state.name} onChange={this.handleInputChange} />
            </label>
            <label>
              Email:
              <input name="email" type="text" value={this.state.email} onChange={this.handleInputChange} />
            </label>
                  <label>
              Pet:
              <input name="country" type="text" value={this.state.country} onChange={this.handleInputChange} />
            </label>
            <input type="submit" value="Submit" />
          </form>
        );
      }
    }

Upvotes: 3

Shubham Khatri
Shubham Khatri

Reputation: 281726

The neater way would be to have a single onChange handler and pass on the id to it, and store the value with that id. Your solution will look like

React.createClass({
  getInitialState() {

  },
  onChange(e, type) {
     this.setState({[type]: e.target.value})

  },
  render() {
    return (
      <input
        type='text'
        value={(this.state.inputValue)? this.state.inputValue: ''}
        onChange={this.onChange.bind(this, 'inputValue')} />
      <input
        type='text'
        value={(this.state.emailValue)? this.state.emailValue: ''}
        onChange={this.onChange.bind(this, 'emailValue')} />
    );
  },
  onChange(e) {
    this.setState({ inputValue: e.target.value });
  }
});

The value is given with a ternary operator expression because initially the state is not defined and hence we will get a warning that input is trying to change the uncontrolled input to controlled.

The other way is to have the handler inline like

onChange={ e => this.setState({inputValue: e.target.value}) }

but say you have 30 inputs you need to define their initial state and let it set its value like above, only difference being the value being assigned to it should be with the expression as shown in the first example

The other way to assign value to the input as @MayankShukla suggessted will be

value = {this.state.inputValue || ''}

with getInitialState looking like

getInitialState() {
    return {}
},

Upvotes: 1

Related Questions