Home » Reactjs » React.js: Identifying different inputs with one onChange handler

React.js: Identifying different inputs with one onChange handler

Posted by: admin November 29, 2017 Leave a comment

Questions:

Curious what the right way to approach this is:

var Hello = React.createClass({
getInitialState: function() {
    return {total: 0, input1:0, input2:0};
},
render: function() {
    return (
        <div>{this.state.total}<br/>
            <input type="text" value={this.state.input1} onChange={this.handleChange} />
            <input type="text" value={this.state.input2} onChange={this.handleChange} />
        </div>
    );
},
handleChange: function(e){
    this.setState({ ??? : e.target.value});
    t = this.state.input1 + this.state.input2;
    this.setState({total: t});
}
});

React.renderComponent(<Hello />, document.getElementById('content'));

Obviously you could create separate handleChange functions to handle each different input, but that’s not very nice. Similarly you could create a component just for an individual input, but I wanted to see if there’s a way to do it like this.

Answers:

I suggest sticking to standard HTML attributes like name on input Elements to identify your inputs. Also, you don’t need to keep “total” as a separate value in state because it is composable by adding other values in your state:

var Hello = React.createClass({
    getInitialState: function() {
        return {input1: 0, input2: 0};
    },
    render: function() {
        const total = this.state.input1 + this.state.input2;

        return (
            <div>{total}<br/>
                <input type="text" value={this.state.input1} name="input1" onChange={this.handleChange} />
                <input type="text" value={this.state.input2} name="input2" onChange={this.handleChange} />
            </div>
        );
    },
    handleChange: function(e) {
        this.setState({[e.target.name]: e.target.value});
    }
});

React.renderComponent(<Hello />, document.getElementById('content'));

Questions:
Answers:

You can use the .bind method to pre-build the parameters to the handleChange method.
It would be something like:

  var Hello = React.createClass({
    getInitialState: function() {
        return {input1:0, 
                input2:0};
    },
    render: function() {
      var total = this.state.input1 + this.state.input2;
      return (
        <div>{total}<br/>
          <input type="text" value={this.state.input1} 
                             onChange={this.handleChange.bind(this, 'input1')} />
          <input type="text" value={this.state.input2} 
                             onChange={this.handleChange.bind(this, 'input2')} />
        </div>
      );
    },
    handleChange: function (name, e) {
      var change = {};
      change[name] = e.target.value;
      this.setState(change);
    }
  });

  React.renderComponent(<Hello />, document.getElementById('content'));

(I also made total be computed at render time, as it is the recommended thing to do.)

Questions:
Answers:

The onChange event bubbles… So you can do something like this:

// A sample form
render () {
  <form onChange={setField}>
    <input name="input1" />
    <input name="input2" />
  </form>
}

And your setField method might look like this (assuming you’re using ES2015 or later:

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

I use something similar to this in several apps, and it’s pretty handy.

Questions:
Answers:

Deprecated solution

valueLink/checkedLink are deprecated from core React, because it is confusing some users. This answer won’t work if you use a recent version of React. But if you like it, you can easily emulate it by creating your own Input component

Old answer content:

What you want to achieve can be much more easily achieved using the 2-way data binding helpers of React.

var Hello = React.createClass({
    mixins: [React.addons.LinkedStateMixin],
    getInitialState: function() {
        return {input1: 0, input2: 0};
    },

    render: function() {
        var total = this.state.input1 + this.state.input2;
        return (
            <div>{total}<br/>
                <input type="text" valueLink={this.linkState('input1')} />;
                <input type="text" valueLink={this.linkState('input2')} />;
            </div>
        );
    }

});

React.renderComponent(<Hello />, document.getElementById('content'));

Easy right?

http://facebook.github.io/react/docs/two-way-binding-helpers.html

You can even implement your own mixin

Questions:
Answers:

You can use a special React attribute called ref and then match the real DOM nodes in the onChange event using React‘s getDOMNode() function:

handleClick: function(event) {
  if (event.target === this.refs.prev.getDOMNode()) {
    ...
  }
}

render: function() {
  ...
  <button ref="prev" onClick={this.handleClick}>Previous question</button>
  <button ref="next" onClick={this.handleClick}>Next question</button>
  ...
}

Questions:
Answers:

You can also do it like this:

...
handleChange(input, value) {
    this.setState({
        input: value
    })
}

render() {
    return (
        <div>
            <input type="text" onChange={e => this.handleChange('input1', e.target.value)} />
            <input type="text" onChange={e => this.handleChange('input2', e.target.value)} />
        </div>
    )
}

Questions:
Answers:

@Vigril Disgr4ce

When it comes to multi field forms, it makes sense to use React’s key feature: components.

In my projects, I create TextField components, that take a value prop at minimum, and it takes care of handling common behaviors of an input text field. This way you don’t have to worry about keeping track of field names when updating the value state.

[...]

handleChange: function(event) {
  this.setState({value: event.target.value});
},
render: function() {
  var value = this.state.value;
  return <input type="text" value={value} onChange={this.handleChange} />;
}

[...]

Questions:
Answers:

You can track the value of each child input by creating a separate InputField component that manages the value of a single input. For example the InputField could be:

var InputField = React.createClass({
  getInitialState: function () {
    return({text: this.props.text})
  },
  onChangeHandler: function (event) {
     this.setState({text: event.target.value})
  }, 
  render: function () {
    return (<input onChange={this.onChangeHandler} value={this.state.text} />)
  }
})

Now the value of each input can be tracked within a separate instance of this InputField component without creating separate values in the parent’s state to monitor each child component.

Questions:
Answers:

Hi have improved ssorallen answer. You don’t need to bind function because you can access to the input without it.

var Hello = React.createClass({
    render: function() {
        var total = this.state.input1 + this.state.input2;
        return (
             <div>{total}<br/>
                  <input type="text" 
                    value={this.state.input1}
                    id="input1"  
                    onChange={this.handleChange} />
                 <input type="text" 
                    value={this.state.input2}
                    id="input2" 
                    onChange={this.handleChange} />
            </div>
       );
   },
   handleChange: function (name, value) {
       var change = {};
       change[name] = value;
       this.setState(change);
   }
});

React.renderComponent(<Hello />, document.getElementById('content'));

Leave a Reply

Your email address will not be published. Required fields are marked *