[javascript] React.js: Identifying different inputs with one onChange handler

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.

This question is related to javascript reactjs jsx

The answer is


The key of your state should be the same as the name of your input field. Then you can do this in the handleEvent method;

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

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.


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>
  ...
}

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.)


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'));

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.


You can also do it like this:

...
constructor() {
    super();
    this.state = { input1: 0, input2: 0 };
    this.handleChange = this.handleChange.bind(this);
}

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

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

I will provide really simple solution to the problem. Suppose we have two inputs username and password,but we want our handle to be easy and generic ,so we can reuse it and don't write boilerplate code.

I.Our form:

                <form>
                    <input type="text" name = "username" onChange={this.onChange} value={this.state.username}/>
                    <input type="text" name = "password" onChange={this.onChange} value={this.state.password}/>
                    <br></br>
                    <button type="submit">Submit</button>
                </form>

II.Our constructor ,which we want to save our username and password ,so we can access them easily:

constructor(props) {
    super(props);
    this.state = {
        username: '',
        password: ''
    };

    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
}

III.The interesting and "generic" handle with only one onChange event is based on this:

onChange(event) {
    let inputName = event.target.name;
    let value = event.target.value;

    this.setState({[inputName]:value});


    event.preventDefault();
}

Let me explain:

1.When a change is detected the onChange(event) is called

2.Then we get the name parameter of the field and its value:

let inputName = event.target.name; ex: username

let value = event.target.value; ex: itsgosho

3.Based on the name parameter we get our value from the state in the constructor and update it with the value:

this.state['username'] = 'itsgosho'

4.The key to note here is that the name of the field must match with our parameter in the state

Hope I helped someone somehow :)


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


@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} />;
}

[...]

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to reactjs

Error: Node Sass version 5.0.0 is incompatible with ^4.0.0 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app Template not provided using create-react-app How to resolve the error on 'react-native start' Element implicitly has an 'any' type because expression of type 'string' can't be used to index Invalid hook call. Hooks can only be called inside of the body of a function component How to style components using makeStyles and still have lifecycle methods in Material UI? React Hook "useState" is called in function "app" which is neither a React function component or a custom React Hook function How to fix missing dependency warning when using useEffect React Hook? Unable to load script.Make sure you are either running a Metro server or that your bundle 'index.android.bundle' is packaged correctly for release

Examples related to jsx

TypeScript and React - children type? How to use componentWillMount() in React Hooks? expected assignment or function call: no-unused-expressions ReactJS You should not use <Link> outside a <Router> ReactJS - .JS vs .JSX React: Expected an assignment or function call and instead saw an expression React-Native Button style not work ReactJs: What should the PropTypes be for this.props.children? ReactJS map through Object Console logging for react?