Create flexible forms with custom React Hooks

·9 min read

Create flexible forms with custom React Hooks

Introduction

If you are a frontend developer you know the importance of the form element. You can create a form easily with the native HTML <form> element and style it with some CSS declarations.

But let's not settle on a simple form element with basic inputs, labels, and buttons. Let's also apply a bit of JavaScript to add some validation checks.

But what if your application is built with React?

Will you still use the same old technique or will you start using some cool and awesome features that come out of the box with React? This might look like a very common thing to do but its important to understanding how it works, how to use React Hooks with forms, and then how to make it flexible with custom Hooks!

That’s what we'll discuss here. All you need as a prerequisite is knowledge of React and the rest will be discussed in detail.

 

About React Hooks

Before we start coding something, is crucial to know the basics on Reacts Hooks. This sets up the foundation for what we will be doing later on.

In short, react hooks lets you use state in an application and other React features without writing a class.

Now, what does this even mean? Well, with Hooks, you can extract stateful logic from a component so that it can be tested and reused independently. These are the functions that essentially ‘hook into’ the React state.

All of this happens without changing your component hierarchy. With this, you can easily share or reuse the Hooks across the application to any component(s) you need.

One thing to note here is that Hooks work on functional components of React rather than class components. The best part? It’s backward-compatible and it doesn’t replace your existing React knowledge.

There are various built-in Hooks in React. Some of the common ones are:

  • useState: used to declare a state with the useState() functional component for setting and retrieving state. It uses the following syntax:
    const [state, setState] = useState(initialState);​
  • useEffect: allows side effects (or additional actions) and is usually used for DOM updating, fetching/consuming data from an API. It uses the following syntax:
    useEffect(() => {});
  • useContext: used to accept a context object and returns the current value of the context. Here is the syntax used:
    const value = useContext(MyContext);

 

 

And then there are these additional hooks:

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useLayoutEffect

As we can see, all the Hooks start with the “use” keyword before the actual context word of the Hook. 

 

Why React Hooks are preferred? 

Before Hooks were added in React 16.8, whenever we had to access or add a state to a functional component, we had to convert it into a class component and get the local state by using props. But with Hooks, we can access the state directly in the functional component. Apart from this, we should know that Hooks in React are:

  1. Completely an opt-in process: there is no need to rewrite any existing code to use Hooks in React. If you don’t want it, you don’t need it!
  2. Synchronisation: with these Hooks, we can more easily use them whenever we want to use synchronization like in updating the DOM, fetching the data, etc,
  3. Sharing non-visual logic: of course there is no specific Hook for sharing patterns like React Higher-Order Components (HOCs) or render props. But the good thing is we can create our custom Hook (just like we will be doing in the following demo) which is decoupled from any visual logic.

 

Create a custom React Hook form

Great, now that you know all the basics we need to make a flexible React Hooks form. Here’s what we will be building:

React Hooks App Demo

As you can see, we will be making a dead simple signup form in React where we have four input fields: first name, last name, email, and password. When you submit the details here it will log the first name, last name, and email in the console. Of course, the main thing to discuss here is not the styling of the form or the actual signup functionality.

By building this quick demo app, we will get to know how easy and flexible it is to make React Hooks forms without adding any new dependencies or libraries. Everything is built-in, we just need to create the Hook and actually ‘hook’ into our form component. Okay, enough talk, let’s dive into code!

 

Setup a new React project

Fire up your terminal or command line app and run the following command to kickstart a new React project:

npx create-react-app custom-hook-demo && cd custom-hook-demo

 

Note: For this to work, you will need a copy of Node installed on your machine. 

If you're new to React, feel free to checkout the beginner's guide before moving forward.

This command uses the Create React App setup to create and configure a new React project at the specified directory location. Next, open up the newly created project in your favorite code editor.

 

Creating the Signup component

Open the App.js file and remove all the boilerplate code. Then add the “Signup” heading so that you're left with the following:

import React from 'react';

function App() {
 return (
   <div className='App'>
        <h1>Signup</h1>
   </div>
 );
}


export default App;

With this, if you run the app, you will end up with a clean webpage with a single heading.

 

Now, it’s time to add our component. So let’s create a new file called Signup.jsx inside the src/ folder. Here, inside the return() we will start creating our form.

With the <form> element, we have three children; the first is for the names (first name and last name), the second for email, and the third for password. These are all wrapped up in a container div separate from the button below it.

Make sure that all the <input> fields have the required name attribute. This is crucial in order to handle events later on. Let’s give all four of them the following name values:

<form>
     <div>
       <label>First Name</label>
       <input
         type="text"
         name="firstName"
         required />
       <label>Last Name</label>
       <input
         type="text"
         name="lastName"
         required />
     </div>

     <div>
       <label>Email</label>
       <input
         type="email"
         name="email"
         required />
     </div>

     <div>
       <label>Password</label>
       <input
         type="password"
         name="password" />
     </div>

     <button type="submit">Sign up</button>
</form>

After applying some basic CSS styles for inputs and the buttons, you should get a result similar to this on your browser:

  

React Hooks Signup Form Demo

 

Creating the custom Hook

Now comes the interesting part. If you enter any values on the input fields and hit the “Sign up” button, you will see nothing happens, rather, the browser will refresh with all the values gone!

Of course, we don’t want this behavior for any form we build. To solve this, let's create a React Hook for the form.

Create a new file named CustomHooks.js and make a new useSignUpForm() functional component. Make sure you export it as well because we will need to use it in our “SignUp” component.

const useSignUpForm = () => {};

export default useSignUpForm;

 

Remember Hooks? We need to start with a functional component. Then name the Hook, starting with “use”, so we gave “useSignUpForm”. This indicates the Hook will be used for the signup form component we made above.

For our case, we will be using the useState Hook as we need to access the current state of the form in order to know whether there is any input provided by the user or not and if so, then what all we need to do. First, import useState as:

import { useState } from "react";

 

Then, we define the useState Hook with two inputs, formInputs and setFormInputs, initializing it with an empty array:

const [formInputs, setFormInputs] = useState({});

 

Here formInputs is the state variable and setFormInputs is the state function. Next, we add the handleFormSubmit() function which essentially handles what action is done to any event (input event in our case) when it’s fired. For this, we can have an extra check to see whether there is an event provided or not. If it is, then we tell the browser to stop any default behavior (like clearing out the input field values and reloading the page) with preventDefault():

const handleFormSubmit = (event) => {
   if (event) {
     event.preventDefault();
   }
 };

After this, let’s make a new function called handleInputChange() which is responsible for managing the event when an input is provided. So, whenever you enter a value, this function will fire up.

Now, in order to update the state (prior to adding any value and after adding the value), we use the setFormInputs() state function to update the state of the inputs variable replacing it with the provided new input (event.target.value).

const handleInputChange = (event) => {
   event.persist();
   setFormInputs((formInputs) => ({
     ...formInputs,
     [event.target.name]: event.target.value
   }));
 };

Next, make sure you return the two functions along with the formInputs variable so that it can be accessed. After this, your custom Hook will look like this:

import { useState } from "react";

const useSignUpForm = () => {
 const [formInputs, setFormInputs] = useState({});
 const handleFormSubmit = (event) => {
   if (event) {
     event.preventDefault();
   }
 };

 const handleInputChange = (event) => {
   event.persist();
   setFormInputs((formInputs) => ({
     ...formInputs,
     [event.target.name]: event.target.value
   }));
 };

 return {
   handleFormSubmit,
   handleInputChange,
   formInputs
 };
};



export default useSignUpForm;

Up next, we need to connect this custom React Hook to our component so it works as expected.

Inside the Signup component, let’s first initialize our custom Hook. Just like we did it for the useState() Hook in CustomHooks.js file, we will be initializing our custom Hook as:

const { formInputs, handleInputChange, handleFormSubmit } = useSignUpForm();

 

Make sure you import the Hook also, else the code will break. See how we destructured all the objects/functions returned from the useSignUpForm custom Hook. Now let’s handle all the events inside the form.

  • First, on the <form>’s onSubmit, we will add the handleFormSubmit function.
  • Next, on each of the input elements we have, we add the handleInputChange function on the onChange attribute.
  • Also, on the same input element’s value attribute, we assign it as:
<input
         type="text"
         name="firstName"
         required
         onChange={handleInputChange}
         value={formInputs.firstName} />

Remember, we need to do this to every input element we have. So the next one will have value={formInputs.lastName}, then value={formInputs.email}, and value={formInputs.password}

 

Alright then, we connected our custom React Hook form with the component. It works as we defined but we do need to actually see it after submitting the form. Let’s do a simple console log to check this. 

Just after you initialized the custom Hook, add a console.log() statement which gets the firstName, lastName, and email as:

console.log(
   `Your name is: ${formInputs.firstName} ${formInputs.lastName} and your email address is: ${formInputs.email}`
 );

 

After this, your code for the form component should look like this:

import useSignUpForm from "./CustomHooks";

const Signup = () => {
 const { formInputs, handleInputChange, handleFormSubmit } = useSignUpForm();

 console.log(
   `Your name is: ${formInputs.firstName} ${formInputs.lastName} and your email address is: ${formInputs.email}`
 );

 return (
   <form
     onSubmit={handleFormSubmit}>
     <div>
       <label>First Name</label>
       <input
         type="text"
         name="firstName"
         required
         onChange={handleInputChange}
         value={formInputs.firstName} />
         
<label>Last Name</label>
       	<input
           type="text"
           name="lastName"
           required
           onChange={handleInputChange}
           value={formInputs.lastName} />
          </div>
     <div>
       <label>Email</label>
       <input
         type="email"
         name="email"
         required
         onChange={handleInputChange}
         value={formInputs.email} />
      </div>
     <div>
       <label>Password</label>
       <input
         type="password"
         name="password"
         onChange={handleInputChange}
         value={formInputs.password} />
      </div>

     <button type="submit">Sign up</button>
   </form>
 );
};

export default Signup;

Now whenever you hit the submit button, you can see the log is printed on the console with the exact values!

React Hooks Form Console

 

Conclusion

Forms are everywhere. Whether you want to register a new user to your application or simply add live chat functionality, our web applications need to use elements of forms. That’s why it's necessary for us as developers to create flexible forms so that other developers in the teams are able to extend them when needed.

In this tutorial, we got to know what is a React Hook and what are the advantages of using it in an application. There are some default basic and advanced forms provided by the React library and it’s up to us which one to choose from. We used the useState() basic Hook to create a custom React Hook form and then attached its functionality to the Sign-up component we had. Finally, we got the exact output the user enters in the form in the console. 

From here, you can read the following topics to better understand and see examples of React Hooks: