Explain why we use controlled forms (vs uncontrolled forms)
Implement a controlled form
Use form data to update state in a parent component
In React, rather than looking into the DOM to get the form’s input field values when the form is submitted, we use state to monitor the user’s input as they type, so that our component state is always in sync with the DOM.
<body>
<form id="uncontrolledForm">
<input type="text" id="nameInput" placeholder="Enter your name" />
<button type="submit">Submit</button>
</form>
<script>
document.getElementById('uncontrolledForm').addEventListener('submit', function(event) {
event.preventDefault();
const name = document.getElementById('nameInput').value;
alert(`Hello, ${name}!`);
});
</script>
function ControlledFormFunctional() {
const [name, setName] = useState('');
const handleInputChange = (event) => {
setName(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
alert(`Hello, ${name}!`)};
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={handleInputChange}
/>
<button type="submit">Submit</button>
</form>
)}
To keep track of each input’s value, you need:
State for the input that will manage the input’s value
An onChange listener attached to the input to monitor users behavior and update state as the user interacts with the field
A value attribute on the input that corresponds to a key in state
And for the form itself, you need an onSubmit listener on the form to finally submit data.
const [name, setName] = useState("");
const [about, setAbout] = useState("");
const [phase, setPhase] = useState("");
const [link, setLink] = useState("");
const [image, setImage] = useState("");
❗ Most common approach (and cleanest) is to create a state object with key/value pairs associated with each form field:
const [formData, setFormData] = useState({
name: "",
about: "",
phase: "",
link: "",
image: "",
});
Example:
<input type="text" id="about" onChange={handleOnChange} />
🤯 If using individual pieces of state for form fields, a separate helper function will be created for each corresponding field.
Example:
<input type="text" id="about" onChange={handleAbout} />
<input type="text" id="phase" onChange={handlePhase} />
value attribute of each input field to the corresponding state variable:Example:
<input
type="text"
id="about"
onChange={handleOnChange}
value={formData.about}
/>
❗Note The reason formData.name is being used is because the state variable is an object named formData. To access the value of a key within the object, dot notation is used.
name attribute to the input fields:<input
type="text"
id="about"
onChange={handleOnChange}
value={formData.about}
name="about"
/>
❗ IMPORTANT: The name attribute needs to match with the key created in the state object in order to update the value. If the key in the state object is ‘about’ then the name attribute for the corresponding input field should be about as well
const handleOnChange = (e) => {
// e.target will return an object, the element that triggered the event with properties
// including name and value. Object destructuring is used to extract that values from e.target
// This is the same as doing:
// const name = e.target.name
// const value = e.target.value
const { name, value } = e.target;
// The setter function is then invoked and a new object will be created with the
// contents of the previous formData spread and the new key/value added to avoid overwriting the
// previous form field values
setFormData((formData) => ({ ...formData, [name]: value }));
};
<form> element, add an onSubmit listener with a handleSubmit helper function that will run when the form is submitted:<form className="form" autoComplete="off" onSubmit={handleSubmit}></form>
const handleSubmit = (e) => {
e.preventDefault();
};
The state of projects is defined inside of the parent component App and the behavior occurs in the child component ProjectForm. When the new project is submitted, projects will need to be updated to include it.
Here is where the process of inverse data flow will need to occur:
App component called onAddProject that will update the projects state:const onAddProject = (newProject) => {
setProjects([...projects, newProject]);
};
projects is an array so to update the state, a new array will be created with the elements of the original projects array spread and the new project passed to onAddProject added as a new element
Pass onAddProject as a prop to ProjectFrom from within App component:
<ProjectForm onAddProject={onAddProject} />
Inside the ProjectForm component, destructure onAddProject from the props and invoke it from within the handleSubmit function, passing it the formData object:
const handleSubmit = (e) => {
e.preventDefault();
onAddProject(formData);
// after we have delivered the formData to the App component and updated state
// clear the form by setting the values back to empty strings:
setFormData({
name: "",
about: "",
phase: "",
link: "",
image: "",
});
};
State is a very integral part of the way that React applications operate and DOM manipulation to occur. React prefers using state to update the forms and keep track of the form fields values, making them controlled inputs. What our user sees in the input fields reflects the value of the state associated with that field.