DEV Community

Rushi Arumalla
Rushi Arumalla

Posted on

4 2

Building reusable form components in React

I've read and watched many demos that show you how to create form components with a form and component library, but wrapping inputs in form controls can get quite repetitive especially if you are working with a large application. I'll be using CRA(create-react-app), Chakra UI's form components and react-hook-form for this demo (Feel free to use any libraries you like ex. Formik, Material UI, etc.).

To follow along run this command inside of your react app:

yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion react-hook-form
Enter fullscreen mode Exit fullscreen mode

Here's a traditional pattern you will see for a form input:

import { useForm } from "react-hook-form";
import {
  FormControl,
  FormLabel,
  FormErrorMessage,
  Input,
} from "@chakra-ui/react";

function App() {
  const { register, errors, handleSubmit } = useForm({
    defaultValues: {
      name: "",
    },
  });

  const onSubmit = (values) => {
    console.log("Form Submitted", { values });
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <FormControl>
          <FormLabel>Name: </FormLabel>
          <Input
            id="name"
            name="name"
            ref={register({ required: "Please enter your name" })}
          />
          <FormErrorMessage>{errors.name}</FormErrorMessage>
        </FormControl>
      </div>
      <button type='submit'>Submit</button>
    </form>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

You might be thinking that it doesn't look too bad. But what if you had > 10 fields on this screen, or had to do this over and over across your application. We can clean this up by creating an input that you could reuse all over your application. Let's create a file called InputField.js and add the below code.

import { useController } from "react-hook-form";
import {
  FormControl,
  FormErrorMessage,
  FormLabel,
  Input,
} from "@chakra-ui/react";

const useMetaError = ({ invalid, isTouched }) => {
  const errorMessage = (isTouched && invalid?.message) || "";
  return {
    errorMessage,
    hasError: !!errorMessage,
  };
};

export const InputField = (props) => {
  const { type = "text", label } = props;
  const { field, meta } = useController(props);
  const { errorMessage, hasError } = useMetaError(meta);
  return (
    <div>
      <FormControl isInvalid={hasError}>
        <FormLabel>{label}</FormLabel>
        <Input {...field} type={type} />
        <FormErrorMessage>{errorMessage}</FormErrorMessage>
      </FormControl>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

So let's start with useController, a hook that lets us create a controlled input, giving us access to the form we created in App.js. Using the meta prop we can create our own function to handle the error useMetaError, which will return an error if the input has been touched and has an error. The rest is just passing in props from our form, so let's take a look at how this looks in App.js:

import { useForm } from "react-hook-form";
import { InputField } from "./inputField";

function App() {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      name: "",
    },
  });

  const onSubmit = (values) => {
    console.log("Form Submitted", { values });
  };
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <InputField
        name="name"
        label="Name: "
        control={control}
        rules={{ required: "Please enter your name" }}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now all we have to do is pass in the control that we get from useForm instead and our InputField.js will handle the rest.

And boom πŸš€ , we can now use our InputField component inside of any form handled by react-hook-form in our application!

Image of Stellar post

Check out Episode 1: How a Hackathon Project Became a Web3 Startup πŸš€

Ever wondered what it takes to build a web3 startup from scratch? In the Stellar Dev Diaries series, we follow the journey of a team of developers building on the Stellar Network as they go from hackathon win to getting funded and launching on mainnet.

Read more

Top comments (1)

Collapse
 
lucasfrutig0 profile image
lucasfrutig0 β€’

cool, i trying this with typescript

Jetbrains image

Build Secure, Ship Fast

Discover best practices to secure CI/CD without slowing down your pipeline.

Read more

πŸ‘‹ Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay