React.js: Identifying different inputs with one onChange handler
JavascriptReactjsJsxJavascript Problem Overview
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.
Javascript Solutions
Solution 1 - Javascript
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'));
Solution 2 - Javascript
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.)
Solution 3 - Javascript
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.
Solution 4 - Javascript
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
Solution 5 - Javascript
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>
)
}
Solution 6 - Javascript
If anyone looking for Functional Component Example,
import React, { useState } from "react";
export default function Exapmle() {
const [userState, setUserState] = useState({
firstName: "",
lastName: ""
})
const handleChange = (e)=>{
const value = e.target.value;
setUserState({
...userState,
[e.target.name]: value
});
}
return (
<form>
<label>
First name
<input type="text" name="firstName" value={userState.firstName}
onChange={handleChange}
/>
</label>
<label>
Last name
<input type="text" name="lastName" value={userState.lastName}
onChange={handleChange}
/>
</label>
</form>
);
}
Solution 7 - Javascript
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>
...
}
Solution 8 - Javascript
@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} />;
}
[...]
Solution 9 - Javascript
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.
Solution 10 - Javascript
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 :)
Solution 11 - Javascript
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
});
Solution 12 - Javascript
state object
const [values, setValues] = useState({
email: "",
password: "",
});
function that change state
const handleChange = name => event => {
setValues({ ...values, [name]: event.target.value });
};
call above function on onchange of input with "name"
<input
onChange={handleChange("email")}
value={email}
className="form-control"
type="email"
/>
Solution 13 - Javascript
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'));