16
How to make a robust form validation in react with material ui fields.
Form validation is one of the main task that a developer mainly or a web developer specificaly faces from time to time.
There are many ways to validate a form, and many modules to make use of it to help us to validate the form data.
But, what I want to do, Is validating our form on our own to know what goes on under the hood packages like react-hook-form and other modules that do the same job.
You can clone the code from the repo
To start we need a react app, which we will have by using npx create-react-app <your app name>
, besides that we need to npm install material-ui/core
, after doing both those steps we are then ready to code.
First we need to have a structure of the form, where I'm going to use material-ui helper components to build it
function App() {
<Some code is going to be written here in the next steps for validation>
return (
<Container className={classes.container} >
<form noValidate onSubmit={handleSubmit} >
<Typography
variant="h6">
Please enter your data
</Typography>
<TextField
placeholder="Enter your name"
label="Name"
name="name"
variant="outlined"
fullWidth
required
className={classes.field}
value={formValues.name.value}
onChange={handleChange}
error={formValues.name.error}
helperText={formValues.name.error && formValues.name.errorMessage}
/>
<TextField
placeholder="Enter your age"
label="Age"
name="age"
variant="outlined"
fullWidth
required
type="number"
className={classes.field}
value={formValues.age.value}
onChange={handleChange}
error={formValues.age.error}
helperText={formValues.age.error && formValues.age.errorMessage}
/>
<TextField
placeholder="Describe the best tech stack you worked with and you like most?"
label="Likes"
name="likes"
variant="outlined"
fullWidth
required
className={classes.field}
value={formValues.likes.value}
multiline
rows={4}
onChange={handleChange}
error={formValues.likes.error}
helperText={formValues.likes.error && formValues.likes.errorMessage}
/>
<FormControl className={classes.field} >
<FormLabel>Job title</FormLabel>
<RadioGroup name="jobTitle" value={formValues.jobTitle.value} onChange={handleChange} >
<FormControlLabel value="full-stack" control={<Radio />} label="Full stack" />
<FormControlLabel value="backend" control={<Radio />} label="Backend" />
<FormControlLabel value="frontend" control={<Radio />} label="Frontend" />
</RadioGroup>
</FormControl>
<Button
type="submit"
variant="outlined"
color="secondary"
endIcon={<KeyboardArrowRight />}
>
Submit
</Button>
</form>
</Container>
)
}
So, what are we missing so far? 3 things actualy like so:
- Where are the classes, this is a thing that is not in out interest right now, and you can clone the code from the repo and see more about makeStyles hook
- handleChange function
- handleSubmit function
For 2 & 3 we are going to discuss deeply right now, first before handling any change we need to have the form state saved.
so inside our App component
const [formValues, setFormValues] = useState({
name:{
value:'',
error:false,
errorMessage:'You must enter a name'
},
age:{
value:21,
error:false,
errorMessage:'You must enter an age'
},
likes:{
value:'',
error:false,
errorMessage:'You must enter your liked tech stacks'
},
jobTitle:{
value:'full-stack',
error:false,
errorMessage:'You must choose your job title'
}
})
where for every field we need to have a key that matches the name property of the field and we can store the default value in it or the value that is going to be stored on input field change, also whether there is an error and maybe the error message we need to use.
Then the user will start typing and we need to handle that change like so
const handleChange = (e) => {
const {name, value} = e.target;
setFormValues({
...formValues,
[name]:{
...formValues[name],
value
}
})
}
where we match the object inside form values by the field name, and that's why it was quite important for them to match.
After the user finishes, the user of the form will try to submit it, and here comes the handleSubmit function into the game.
const handleSubmit = (e) => {
e.preventDefault();
const formFields = Object.keys(formValues);
let newFormValues = {...formValues}
for (let index = 0; index < formFields.length; index++) {
const currentField = formFields[index];
const currentValue = formValues[currentField].value;
if(currentValue === ''){
newFormValues = {
...newFormValues,
[currentField]:{
...newFormValues[currentField],
error:true
}
}
}
}
setFormValues(newFormValues)
}
We get all the form fields values and check if any of them is empty, if so we destruct the old state and just replace the current one that is empty with error set to true so the TextField component show an error message.
16