Form states are a separate topic that needs to be discussed. There are two approaches to handling form states. One of them gives control over the state to the form itself, while the other involves storing it in the user's application. Both approaches have their pros and cons, and these need to be taken into account when choosing one solution or another.
React's documentation calls these approaches controlled and uncontrolled forms. These names are quite an accurate description of what's happening, so we'll use the same terminology here.
This is an approach where the state of a given form is stored within the form itself and is retrieved only when it is submitted. This is the usual way of working with forms outside of frameworks:
form.addEventListener('submit', (e) => {
const formData = new FormData(e.target);
// Data processing, e.g., sending data to the server
});
The advantages of this method include:
Although it's easy and obvious, this approach has one drawback. This approach makes it impossible to respond to changes in the form as it's being filled out. Where might it be needed? Here are a few examples:
In this situation, we need controlled forms.
With this approach, the state of all form elements is tracked by the application code. Any change is analyzed and saved. Depending on the current state of the data, the code decides what to show and what not to show. These forms often don't even have a submit button. Especially when forms carry out data filtering.
const state = {
registrationForm: {
state: 'valid',
data: {
name: '',
email: '',
},
errors: [],
},
};
// https://github.com/sindresorhus/on-change
const watchedState = onChange(state, (path, value) => {
if (path === 'registrationForm.state') {
if (value === 'invalid') {
// Rendering errors stored somewhere in the state
// watchedState.registrationForm.errors
}
}
});
form.elements.name.addEventListener('change', (e) => {
watchedState.registrationForm.data.name = e.target.value;
// Actions: validation, queries, ...
});
form.elements.email.addEventListener('change', (e) => {
watchedState.registrationForm.data.email = e.target.value;
// Actions: validation, queries, ...
});
If you need to submit this form, it'll be much easier to do so than with uncontrolled forms. The data has already been extracted from the form and is ready to be sent.
form.addEventListener('submit', (e) => {
// Data processing, e.g., sending data to the server
// watchedState.registrationForm.data
});
The advantages of this approach:
And its flaws:
Controlled forms require so much more code that people try to automate them in every way possible. Libraries have been created, such as garlic.js, that automatically track form changes and provide callbacks to react to those changes.
There are very many of these libraries available for different frameworks. React, for example, has dozens of them.
In pure JS, unsupervised forms are preferred. It's so much easier and faster. And you can still introduce controls for form data if you need an instant reaction. It's not really a binary choice between one or the other, either. You can use a hybrid approach, introducing controls for data only when you can't do without it.
The Hexlet support team or other students will answer you.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.
Programming courses for beginners and experienced developers. Start training for free
Our graduates work in companies:
Sign up or sign in
Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.