# Forms
# Controlled Components
Before we begin to write code, it’s important to note how React deals with forms and events. Consider this simple input tag below:
<input type="text" placeholder="First Name" />
In order to get the data inside the input tag, you need to be able to fetch the content somehow. We do this by making sure that the view is always in sync with the state. This means that the value for the input element must first be created in the state and then set to that state value in the render() function.
Let's see an example:
import React, { useState } from 'react';
const FormComponent = () => {
const [name, setName] = useState('');
const handleChange = (event) => setName(event.target.value);
return (
<form>
<label>First Name</label>
<input
onChange={handleChange}
placeholder="First Name"
type="text"
value={name}
/>
</form>
);
};
export default InputComponent;
The input element has a value of name which means the value of the input is set to be in sync with the value in state. This essentially translates to the fact that whatever is typed in the input field will be stored in the state. The onChange event will be executed whenever there is a change in input element and the handleChange() function will be triggered. This will save the current value of the input to our local state.
So React knows how to store the values from the input element to the state now, but how do we deal with form submission. Very simple. Take a look at the code block below:
import React, { useState } from 'react';
const FormComponent = () => {
const [name, setName] = useState('');
const handleChange = (event) => setName(event.target.value);
const handleSubmit = (event) => {
event.preventDefault();
console.log(`First Name: ${name}`);
};
return (
<form onSubmit={handleSubmit}>
<label>First Name</label>
<input
onChange={handleChange}
placeholder="First Name"
type="text"
value={name}
/>
<input type="submit" value="Submit" />
</form>
);
};
export default InputComponent;
The form component now has an onSubmit event that triggers the handleSubmit function. On the handleSubmit we prevent the default HTML form behavior of browsing to a new page.
The whole process above of syncing the value of the input element to the state is called Controlled Components. We essentially made the React state the single source of truth. The React component that’s responsible for rendering the form is also responsible for what happens whenever a user adds anything to the form.
# Handling Multiple Inputs
When you need to handle multiple controlled input elements, you can add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name.
import React, { useState } from 'react';
const FormComponent = () => {
const [user, setUser] = useState({
firstName: '',
lastName: '',
});
const handleChange = (event) => {
const { value } = event.target;
setUser({
...user,
[event.target.name]: value,
});
};
const handleSubmit = (event) => {
event.preventDefault();
console.log(`First Name: ${user.firstName}`);
console.log(`Last Name: ${user.lastName}`);
};
return (
<form onSubmit={handleSubmit}>
<label>
First name
<input
name="firstName"
onChange={handleChange}
type="text"
value={user.firstName}
/>
</label>
<label>
Last name
<input
name="lastName"
onChange={handleChange}
type="text"
value={user.lastName}
/>
</label>
</form>
);
};
export default InputComponent;
In addition to getting the value from the event target, we get the name of that target as well. This is the essential point for handling multiple input fields with one handler. We funnel all changes through that one handler but then distinguish which input the change is coming from using the name.
This example is using [event.target.name], with the name in square brackets, to create a dynamic key name in the object. Because the form name props match the state property keys, the firstName input will set the firstName state and the lastName input will separately set the lastName state.
Also note that, because we are using a single state object that contains multiple properties, we're spreading (...user) the existing state back into the new state value, merging it manually, when calling setUser. This is required when using useState hook in the solution.