In this article, we’ll look at how to handle forms with React.
Form Handling
The standard way to handle form input value changes is to handle them with React. This is a technique called controlled components.
We can create controlled components with input
, textarea
, and select
elements. They maintain their own state, and updates are based on user input.
Like with anything else, the component state is updated with setState()
. This means that we have to call it with the value of the input elements to update the state with the inputted values.
To update the state with the inputted value, we can write the following:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { name: "" };
}
handleChange(event) {
this.setState({ name: event.target.value });
}
handleSubmit(event) {
alert(`Name submiited: ${this.state.name}`);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit.bind(this)}>
<label>
Name:
<input
type="text"
value={this.state.name}
onChange={this.handleChange.bind(this)}
/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
In the code above, we have the input
element that has an onChange
handler which is set to this.handleChange
. The handleChange
method has the code to update this.state.name
with the latest inputted value. event.target.value
has the latest value.
Then, we attached an onSubmit
handler to the form, which is set to this.handleSubmit
.
We call event.preventDefault
to prevent the default submit action, so we can run JavaScript code in the handler to display the alert box with the latest inputted value.
The handler methods make it easy to handle input validation. We can also manipulate it there. For example, we can change our input value before updating the state as follows:
handleChange(event) {
this.setState({name: event.target.value.toLowerCase()});
}
We don’t have to create an extra function to change our input to lowercase before setting it to this.state.name
.
We have the value
prop to set the value to the latest state so that we can see our inputted value.
The textarea Tag
Like with handling input
elements, we can handle value changes with textarea
elements the same way.
We can write the following to set the state to the latest inputted value:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { essay: "" };
}
handleChange(event) {
this.setState({ essay: event.target.value });
}
handleSubmit(event) {
alert(`Essay submiited: ${this.state.essay}`);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit.bind(this)}>
<label>Essay:</label>
<br />
<textarea
type="text"
value={this.state.essay}
onChange={this.handleChange.bind(this)}
/>
<br />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
It’s pretty much the same as the code for handling inputted values from the input
element, but we have a textarea
element instead.
The select Tag
The select
element creates a drop-down where we can select more values.
We can write the following code to get the selection from the drop-down and set it to the state:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { fruit: "apple" };
}
handleChange(event) {
this.setState({ fruit: event.target.value });
}
handleSubmit(event) {
alert(`Favorite fruit: ${this.state.fruit}`);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit.bind(this)}>
<label>Favorite Fruit:</label>
<br />
<select
onChange={this.handleChange.bind(this)}
value={this.state.fruit}
>
<option value="apple">Apple</option>
<option value="orange">Orange</option>
<option value="mango">Mango</option>
</select>
<br />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
As we can see, it’s almost the same as the other examples, except that we have a select
element with option
elements inside instead.
select
elements also work for getting multiple selections. We can update the state with all the selections by getting the options available, getting the ones that are selected, and putting them in an array as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { fruits: [] };
}
handleChange(event) {
const options = event.target.options;
const fruits = [];
for (const option of options) {
if (option.selected) {
fruits.push(option.value);
}
}
this.setState({ fruits });
}
handleSubmit(event) {
alert(`Favorite fruit: ${this.state.fruits.join(",")}`);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit.bind(this)}>
<label>Favorite Fruit:</label>
<br />
<select
multiple
onChange={this.handleChange.bind(this)}
value={this.state.fruits}
>
<option value="apple">Apple</option>
<option value="orange">Orange</option>
<option value="mango">Mango</option>
</select>
<br />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
In the code, we have:
handleChange(event) {
const options = event.target.options;
const fruits = [];
for (const option of options) {
if (option.selected) {
fruits.push(option.value);
}
}
this.setState({ fruits });
}
In the first line of the function, we have:
const options = event.target.options;
Which has all the options from the select
element.
Then, we can loop through the options
and check the selected
property of each and then push them to the array if selected
is true
. We then call setState
with fruits
to update the state with the latest selected values.
In handleSubmit
, we can this.state.fruits
and call join
on it with a comma to convert it to a comma-separated string.
Also, the value
prop of the select
element takes an array instead of a string. React is smart enough to parse the array and render the selected values.
Handling Multiple Inputs
When we need to handle multiple inputs, we don’t want to make a new onChange
handler function for each input. Therefore, we want to make a function that can set all values.
We can do that as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { firstName: "", lastName: "" };
}
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
alert(`Name ${this.state.firstName} ${this.state.lastName}`);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit.bind(this)}>
<label>First Name:</label>
<br />
<input
name="firstName"
onChange={this.handleChange.bind(this)}
value={this.state.firstName}
/>
<br />
<label>Last Name:</label>
<br />
<input
name="lastName"
onChange={this.handleChange.bind(this)}
value={this.state.lastName}
/>
<br />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
In the code above, we have the following handleChange
method:
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
We get the event.target
object and set it to target
. The target
has the name
and value
properties that have the input’s name and value attribute values respectively.
Therefore, we can take advantage of ES6’s dynamic object property feature and call setState
with the name
as the property name and value
as the value to update the state.
Conclusion
We should handle input changes by writing controlled components. To do this, we attach an event handler function to the onChange
event.
To handle form submit, we attach an onSubmit
event handler to the form, and then get the event
object from the parameter and call event.preventDefault
inside so we can run JavaScript code in the handler.
To handle multiple input changes with one onChange
handler, we get the name
and value
properties from event.target
from the handler’s parameter and update it with the dynamic property name feature available since ES6.