close icon
daily.dev platform

Discover more from daily.dev

Personalized news feed, dev communities and search, much better than what’s out there. Maybe ;)

Start reading - Free forever
Start reading - Free forever
Continue reading >

ReactJS useForm for Dynamic Forms

ReactJS useForm for Dynamic Forms
Author
Nimrod Kramer
Related tags on daily.dev
toc
Table of contents
arrow-down

🎯

Learn how to use React Hook Form for managing dynamic forms in React applications. Discover the basics of useForm, dynamic forms, form validation, styling forms, form submission, and common pitfalls.

React Hook Form is a powerful tool for managing forms in React applications, simplifying form state and validation. Whether you're creating forms that need to dynamically change based on user input or fetch additional fields from a server, useForm helps you efficiently handle these requirements. This guide covers everything from initializing your React project with React Hook Form to building dynamic forms with validation and advanced features. Here's a quick overview:

  • Getting Started: Learn how to set up a React project and add React Hook Form.
  • useForm Basics: Dive into registering inputs, handling submissions, and managing form state.
  • Dynamic Forms: Use useFieldArray to add, remove, and reorder fields dynamically.
  • Form Validation: Implement validation using built-in rules or custom logic.
  • Styling Forms: Style your forms using CSS, Material UI, or Bootstrap.
  • Form Submission: Handle form data submission with handleSubmit.
  • Common Pitfalls: Avoid common mistakes when using React Hook Form.

This article is intended for developers with a basic understanding of JavaScript, React, and form management who are looking to enhance their form handling capabilities in React applications.

Initializing a React Project

To start using React Hook Form for building dynamic forms, you first need to set up a basic React project. Here's how to do it step by step:

  • Download Node.js
  • First, go to the Node.js website (nodejs.org) and download the version that says LTS. This includes npm, which is a tool you'll use to install React and other things you need.
  • Get Create React App
  • Open your terminal or command prompt and type npm install -g create-react-app. This command installs Create React App globally on your computer, making it easy to create new React projects.
  • Make a New React App
  • Type npx create-react-app my-app in your terminal. Replace my-app with whatever you want to name your project. This command creates a new folder with everything you need for a React project.
  • Add React Hook Form
  • Go into your new project folder by typing cd my-app and then npm install react-hook-form. This adds the React Hook Form library to your project, which is what you'll use to make your forms work better.

Now, you have everything set up for a React project. You're ready to start using React Hook Form to create dynamic forms.

In the following parts, we'll show you how to bring useForm into your app and set up your first form with it. Let's dive in!

Understanding useForm Basics

The useForm hook from React Hook Form is like a toolkit for dealing with forms in React:

Registering Inputs

Think of the register() method as a way to tell React Hook Form about your inputs, like text boxes or dropdowns, so it can keep an eye on what's typed into them.

For example:

const { register } = useForm();

<input {...register("username")} />

You can tell register() about rules for checking inputs right here.

Handling Form Submission

The handleSubmit() method helps with what happens when you submit a form. You use it by adding it to the form's onSubmit.

For example:

const { handleSubmit } = useForm();

<form onSubmit={handleSubmit(onValid)}>
  {/* inputs */}
</form>

It calls the function you give it with the form's data if everything's good, or with errors if something's wrong.

Tracking State

useForm() also gives you formState to keep track of things like whether the form's been changed.

For example:

const { formState } = useForm(); 

{formState.isDirty && <p>Form has been edited</p>}

Integration with Inputs

For working with UI libraries like Material UI, React Hook Form has a <Controller> that makes it easier to deal with external components.

For example:

<Controller
  name="firstName"
  control={control} 
  render={({ field }) => <TextField {...field} />}
/>

This makes it easier to use these components in your forms.

In short, useForm takes care of the tricky parts of handling forms, letting you focus on making them work well for your users.

Building a Simple Form

To start making a simple form with React Hook Form, here's what you need to do:

1. Import useForm

First, bring in the useForm hook from React Hook Form into your file:

import { useForm } from \"react-hook-form\";

2. Initialize the Form

Next, set up useForm and pick the parts you'll use:

const { register, handleSubmit } = useForm();

3. Define Form Fields

Create your form fields by using the register() method:

<input {...register(\"firstName\")} />

<input {...register(\"lastName\")} />

This step makes sure they're included in the form.

4. Handle Form Submission

Use handleSubmit for when the form is submitted:

<form onSubmit={handleSubmit(onSubmit)}>
  {/* fields */}
</form>

This checks the data and then calls onSubmit.

5. Get Form Data

When onSubmit happens, here's how you get the data:

function onSubmit(data) {
  console.log(data); 
}

And that's the basic way to make a form with React Hook Form!

Example Form

Here's what a basic sign up form might look like:

function App() {

  const { register, handleSubmit } = useForm();
  
  function onSubmit(data) {
    console.log(data);
  }
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
    
      <input {...register(\"firstName\")} />
        
      <input {...register(\"lastName\")} />
  
      <input {...register(\"email\")} />

      <button>Submit</button>
      
    </form>
  );
}

Next up, we'll dive into adding checks (validation), making fields change based on what you do (dynamic forms), and more.

Implementing Dynamic Fields

To make your forms in React more flexible, you can use something called useFieldArray from React Hook Form. This lets you add, delete, or change the order of your form fields as needed, like when you want users to enter a list of skills and be able to add more as they think of them.

Here's a simple way to do that:

import { useForm, useFieldArray } from 'react-hook-form';

function MyForm() {
  const { register, control, handleSubmit } = useForm();
  const { fields, append, remove } = useFieldArray({
    control,
    name: "skills"
  });

  const onSubmit = data => console.log(data);
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>

      {fields.map((field, index) => (
        <div key={field.id}>
          <input 
            {...register(`skills.${index}.name`)} 
          />
          <button type="button" onClick={() => remove(index)}>Delete</button>
        </div>
      ))}

      <button type="button" onClick={() => append({ name: "" })}>
        Add Skill
      </button>

      <input type="submit" />
    </form>
  );
}

In this code:

  • We use fields to keep track of each skill field.
  • We loop through fields to show each skill input.
  • Each input is uniquely named using register.
  • To delete a skill, we use the remove function with the field's index.
  • To add a new skill field, we use the append function.

Reordering Fields

You can also change the order of the fields with actions like move or swap.

For example, to move a field up:

<button onClick={() => move(index, index - 1)}>
  Move Up  
</button>

This lets you rearrange the fields however you like.

Managing State

When you add or remove fields, you might need to update the form's state to make sure everything works right. A simple way to do this is to use the reset function from useForm after changing the fields.

For example:

const { reset } = useForm();

function addField() {
  append({ name: '' }); 
  reset();
}

This resets the form and makes sure it updates correctly.

Or, you could use React's useState or useEffect to manage the state when fields change.

Using useFieldArray with useForm makes it easy to have dynamic fields in your forms, letting users add or remove fields as needed.

Validating Your Form

Making sure your form data is correct is important, and React Hook Form gives you some easy tools to do this. Let's look at the main ways you can check your form data:

Using Built-In Validation

React Hook Form comes with some simple checks you can use right away, like:

  • required - This makes sure a field can't be left empty
  • minLength - This makes sure the text is not too short
  • maxLength - This ensures the text isn't too long
  • pattern - This checks the text matches a specific format

For example:

register({  
  required: true,
  maxLength: 20
})

These checks are perfect for basic needs like making sure a field is filled out or that a password is long enough.

Custom Validation

If you need a special check, you can write your own function in validate. This function should give back an error message if something's wrong, or true if everything's okay.

For example:

validate: value => 
  value !== "hello" ? "Must be hello" : true

This example checks if the text is exactly "hello".

The function gets the value of the field to check.

Validation Schema

For more complex checks, you can set up a separate check plan with tools like Yup or Joi, and then tell React Hook Form about it.

For example:

import * as yup from "yup";

const schema = yup.object().shape({
  email: yup.string().required(),
  
  age: yup.number().positive()   
});

useForm({
  resolver: yupResolver(schema)  
})

This way, you can have detailed checks that you can use over and over in different forms.

React Hook Form makes it straightforward to check your forms with these built-in and custom options.

sbb-itb-bfaad5b

Advanced useForm Features

The useForm hook in React Hook Form has some cool advanced features that help you deal with more complicated forms:

Nested Values

Imagine your form has some deep levels, like an address inside a contact info section. You can easily work with these using dot notation. Here's how:

// Register nested fields
register('contact.phoneNumber');
register('contact.email');

// Get nested value  
getValues('contact.phoneNumber');

This lets you handle complex data structures in your forms.

Arrays

Remember how we talked about useFieldArray for dynamic lists? There are more tricks, like insert and swap, to move items around in your lists.

Context API

Sometimes, you need to peek into the form's inner workings or change things around. That's where the context API comes in handy:

const { formState } = useFormContext();

// Check if the form was submitted
formState.isSubmitted;  

// Look for any errors
formState.errors;

External Form Libraries

And if you're using other libraries like Formik for forms, you can still make them work with React Hook Form. The <Controller> component helps connect everything together.

For more details on these advanced tricks and others, you can check out the documentation. While React Hook Form has a lot of powerful features, starting with the basics will help you build even the most complex forms.

Styling Your Form

When using React Hook Form, you can make your forms look good by using styles from popular libraries like Material UI and Bootstrap, or just plain CSS. These options help you quickly give your forms a professional look.

Using Material UI

Here's how to style your form using Material UI:

  • First, add Material UI to your project:
npm install @material-ui/core
  • Then, use Material UI components like TextField for text inputs and Button for submit buttons in your form:
import { TextField, Button } from '@material-ui/core';

function MyForm() {
  return (
    <form>
      <TextField 
        label="Name"
        {...register('name')}
      />

      <Button 
        variant="contained" 
        color="primary" 
        type="submit"
      >
        Submit
      </Button>
    </form>
  )
}
  • You can change the look of these components by adjusting their styles directly:
<TextField 
  {...register('email')}
  style={{ marginBottom: '20px' }} 
/>

Using Bootstrap

For Bootstrap styling:

  • Add Bootstrap to your project:
npm install react-bootstrap bootstrap
  • Use Bootstrap's Form and Button components to build your form:
import { Form, Button } from 'react-bootstrap';

function MyForm() {
  return (
    <Form>
      <Form.Group>
        <Form.Label>Email</Form.Label>
        <Form.Control 
          type="email"
          {...register('email')} 
        />
      </Form.Group>

      <Button variant="primary" type="submit">
        Submit
      </Button>
    </Form>
  )
}
  • Adjust their styles as you like:
<Form.Control
  className="mb-3"
  {...register('name')}
/>

Basic CSS

Or, use CSS for custom styles:

form {
  max-width: 500px;
  margin: 0 auto;
}

input {
  display: block;
  margin-bottom: 10px;
  padding: 5px;
}

button[type="submit"] {
  background: blue;
  color: white;
  border: none;
  border-radius: 5px;
  padding: 10px 15px;
}

This method lets you center the form, space out inputs nicely, and style the submit button.

The idea is to use what's already available for quick and easy styling, or to tweak things yourself as needed.

Handling Form Submission

When you submit a form using React Hook Form, you use something called handleSubmit. This function makes it easy to deal with the form once it's submitted. Here's how you can use it:

First, you need to create a function that will do something with the form's data when the form is submitted:

const onSubmit = async (data) => {
  // What to do with the form data
};

const { handleSubmit } = useForm();

return (
  <form onSubmit={handleSubmit(onSubmit)}>
    {/* place for your form fields */}
  </form>
)

Next, you can use this function to send your form data somewhere, like to a server. Here's an example using fetch to send the data:

const onSubmit = async (data) => {
  try {
    const response = await fetch('/api/form', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });
    
    if(!response.ok) {
      throw new Error('Something went wrong'); 
    }
    
    const json = await response.json();
    console.log(json);

  } catch (error) {
    console.log(error);
  }
}

Here's a simple breakdown of what's happening:

  • It waits for the form to be submitted.
  • It sends the form data to a specific address on the internet (/api/form).
  • It makes sure to tell the server it's sending the data in a format called JSON.
  • It checks if everything went well with sending the data. If not, it lets you know there was an error.
  • It turns the server's response back into JSON so you can see it.
  • Finally, it shows you the response from the server or any errors that happened.

This process makes it super simple to send your form data to where it needs to go and to deal with the response. It's a handy way to connect your forms to the backend.

Common Pitfalls

When you're using React Hook Form's useForm to make your forms, there are a few common mistakes that can make things harder than they need to be. Knowing about these problems ahead of time can save you a lot of trouble.

Forgetting Validation

Sometimes, we get so caught up in making the form that we forget to check if the data people are entering is actually what we want. If you don't set up rules for what's allowed in your form, you might end up with data that doesn't make sense for your needs.

// Example without validation
const { register, handleSubmit } = useForm();

<input {...register("email")} /> 

<button>Submit</button>

How to fix it: Make sure you add checks, like making sure a field is filled out:

register("email", { required: true })  

No Keys with Mapped Data

If you're showing a bunch of fields based on a list, you need to give each one a unique key. If you forget, React can get confused and not show your fields correctly.

// Forgetting keys in a list
{data.map(item => (
  <input {...register(`fields.${item.id}`)} /> 
))}

How to fix it: Always use keys, like the item's ID or the position in the list:

{data.map((item, index) => (
  <input key={index} {...register(`fields.${item.id}`)} />
))} 

Controlled vs Uncontrolled Inputs

If you try to control an input's value directly, instead of letting React Hook Form handle it, you might run into problems with inputs not behaving as expected.

// This might cause issues
<input {...register("firstName")} value={inputValue} />

How to fix it: Use Controller to let React Hook Form manage the input for you:

<Controller
  name="firstName"
  control={control}
  render={({ field }) => <input {...field} />}  
/>

State Mismatch from Async Updates

Sometimes, when you update your form's data after getting info from somewhere else, like a server, your form and the actual data can get out of sync.

How to fix it: You can use the reset method from React Hook Form to make sure your form matches the new data.

Or, use React's useState and useEffect to manage the data and updates more carefully.

Infinite Loop from Internal State Change

Updating the form's state inside the form itself, like when a field changes, can cause endless re-renders or useEffect calls. This means your form might keep resetting or updating without stopping.

How to fix it: Use the shouldUnregister option to keep internal updates from causing resets.

Or, handle your form's state outside of the form itself to avoid these loops.

By keeping these tips in mind, you can avoid common issues and make building forms with React Hook Form a lot smoother.

Conclusion

React Hook Form is a handy tool that helps you make forms in React easily. Here's what we've learned about it:

  • Checking inputs is simple with rules that come with React Hook Form and the option to add your own checks.
  • Adding or removing fields can be done on the fly with useFieldArray, which is great for forms where the number of inputs can change.
  • Showing or hiding fields based on what the user does is straightforward, helping you make your forms react to user actions.
  • Submitting forms is managed with handleSubmit, which helps send form data and deal with the responses.
  • Keeping track of changes in your form, even when getting new data from somewhere else, is part of what React Hook Form does well.

React Hook Form also works well with other tools like Material UI or Bootstrap, making it easy to make your forms look good without a lot of work. And for more complex forms, it has features for dealing with deeper levels of data and connecting with other parts of your app.

While we've covered the basics here, React Hook Form can do a lot more. It's great for things like uploading files, checking inputs in the background, making forms that have several steps, updating parts of your form without refreshing, and working with other tools. The documentation is a great place to learn more about these advanced features.

In short, React Hook Form is a great choice for making forms in React. It's flexible and can grow with your project's needs, making it easier to create good experiences for your users.

Resources

Here are some helpful links if you want to learn more about using React Hook Form:

These resources should help you get a good start on your next project with React Hook Form. If you have any questions, feel free to ask.

Related posts

Why not level up your reading with

Stay up-to-date with the latest developer news every time you open a new tab.

Read more