DEV Community

Omar Diaaeldine Elwakeel
Omar Diaaeldine Elwakeel

Posted on

How to make a robust form validation in react with material ui fields.

Form validation

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>
)

}
Enter fullscreen mode Exit fullscreen mode

So, what are we missing so far? 3 things actualy like so:

  1. 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
  2. handleChange function
  3. 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'
    }
  })
Enter fullscreen mode Exit fullscreen mode

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
      }
    })
  }
Enter fullscreen mode Exit fullscreen mode

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)
  }
Enter fullscreen mode Exit fullscreen mode

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.

Errors in form

Discussion (2)

Collapse
jonrandy profile image
Jon Randy • Edited on

You don't need to put the onChange handler on every element. Just put one handler for onChange on the form. Also, why not use the built in HTML form validation instead of re-inventing the wheel?

Collapse
omardiaa48 profile image
Omar Diaaeldine Elwakeel Author

Thanks for saying so, I will take that into consideration next time