Jeffrey Onuigbo

@

About Jeffrey Onuigbo

Jeffrey Onuigbo is a Web Developer focused on building usable products that move the needle for businesses and organizations. He is also a Designer and Technical writer sharing what he learns as he experiments with new technology.

Build a Verification Code Component With a React Custom Hook
Languages, frameworks, tools, and trends

How to Build a Fancy Verification Code Component with React Custom Hooks?

This blog will help you build a verification code component in React by creating and using a React custom hooks library and it will also help you implement this functionality in projects you’re working on.

How do you build a verification code component with React custom hooks? Well, if you build a lot of authentication pages (Sign up / Login), chances are you have come across something like this from a designer:

Feature used in mobile apps to verify users using a PIN code

This is a very popular feature used in mobile apps to verify users using a PIN code sent to their phone or email. In this article, I’ll be showing you how to recreate this for the web using ReactJS and create a custom hook for its functionality. At the end of this article, you can implement this functionality quickly in any project you’re working on.

Table of Contents

  1. First things first
  2. Section 1: Build a custom hook for a verification code component
    1. Tracking values for each input box
    2. Handling changes to input boxes and moving focus accordingly
    3. Preventing non-numeric characters from being entered
    4. Compiling the user’s entered code
  3. Section 2: Using the React custom hook
  4. Testing the custom hook
  5. Conclusion

First things first

This article is split into two sections:

  1. The first section covers how the functionality of the verification component works and at the end we’ll refactor that functionality into a React custom hook we can install and re-use in any project we’re working on.
  2. The second section covers how we can use the React custom hook easily to add interactivity to the verification code component.

If you are not interested in how the custom hook works, you can skip the first section and move on to installing the custom React hook I’ve published on npm.

Section 1: Build a custom hook for a verification code component

Create a `/lib` folder in your project directory and in that folder create a file called `useVerificationHook.js` (or `.ts` if you use TypeScript). This file will contain the code that will make our component interactive, it is a React custom hook we will import into our project to make our verification code component interactive.

The file should contain the following function:

```js
export default function useVerificationHook (codeLength) {
	return {};
}
```

This function will accept a `codeLength` parameter which determines the number of input boxes we can generate, and it returns an object.

Now looking at the design below, we have 6 input fields that each take 1 digit before moving focus to the next input. This is our happy path:

Tracking values for each input box

Depending on your project, the length of the verification code might differ, so we need a flexible way to generate any number of input boxes needed, and we can do so using an array.

```js
// in useVerificationHook function
let inputStates = []
```

We also need to keep track of the state of each input box so we can populate the array with stateful values like so:

```js
// top of file
import { useState } from 'React';

// in useVerificationHook function
for (let i = 0; i < codeLength; i++) {
    const [digit, setDigit] = useState("");
    inputStates.push({ digit, setDigit });
  }
```

In the loop above, we create an object containing a stateful value `digit` and its setter `setDigit`. So depending on our code length we can then use this array to keep track of every input in our custom hook.

Looping through `inputStates` we can generate input boxes that have their own state and reference any input by using its index in the array. More on this in the next section.

We also need a stateful value to keep track of the compiled code entered by the user. Ideally, we would want this value to be `null` until the complete code is entered.

In the function add this line:

```js
// in useVerificationHook function
let [code, setCode] = useState(null);
```

Handling changes to input boxes and moving focus accordingly

Now when the user changes the value of a box, we have to move focus to the next box if the input box is filled:

Or move focus to the previous one if the input box was cleared:

We can achieve both using this function expression:

```js
// in useVerificationHook function
const handleChange = (e, index) => {
    let entry = e.target.value; // stores user's entry

	// limit user entry per input to 1 numeric character
    if (entry.length <= 1 && !Number.isNaN(entry)) {

      // set and limit code per input box to 1 digit
      inputStates[index].setDigit(e.target.value);

      if (entry.length === 1) {
        /* user entered a digit
	       move focus to next empty input box unless it's the last one,
        */
        if (index < codeLength - 1) {
          let nextInput = document.querySelectorAll(`.${inputClass}`)[index + 1];
          if (nextInput.value === "") nextInput.focus();
        }
      } else if (entry.length === 0) {
		/* user deleted a code
	       move focus to the previous input box
        */
        let prevInput = document.querySelectorAll<HTMLInputElement>(`.${inputClass}`)[index - 1];
  
        // focus if prevInput is defined
        if (prevInput !== undefined) prevInput.focus();
      }
    } else return;
  };
```

In the function above, we accept 2 parameters: the event object  `e` and an `index` which stores the index of the input in the array.

The function checks if the user’s entry is numeric else it returns nothing. Then it goes on to check if the user entered a digit (where `entry.length` will be 1) whereby it will get and move focus to the next input.

Whereas if the user deleted a digit (where `entry.length` will be 0), the function will get and move focus to the previous input (unless the current input is the first one in the array)

To target next and previous inputs, we have to target them with a class name contained in an `inputClass` variable, so we need to create this variable at the top of our `useVerificationHook` function:

```js
// in useVerificationHook function
let inputClass = "code-digit"; // classname used to target the inputs
```

The class name can be anything, although make sure no other elements in your code use that class name.

Preventing non-numeric characters from being entered

Now Javascript is popular for being a quirky and odd language, and it processes the character `E` as a number along with some other sneaky characters we don’t want the user to be able to enter in the input boxes.

So we need a way to check if the current key the user pressed is one of those characters and prevent that event from reaching our `handleChange` function.

We can do that using a `keyDown` event handler like so:

```js
// in useVerificationHook function
  const handleKeyDown = (e) =>
    ["e", "E", "+", "-", "."].includes(e.key) && e.preventDefault();
```

Compiling the user’s entered code

Now we need a way to compile our verification code whenever any input changes, and we can use `useEffect` to watch for these changes and do something:

```js
// top of file
import { useState, useEffect } from 'React';

// in useVerificationHook function
  useEffect(() => {
    let finalCode = inputStates
      .map((input) => {
        return input.digit;
      })
      .join("");

    // provide the complete code only if it is complete
    if (finalCode.length === codeLength) {
      setCode(finalCode);
    } else setCode(null);
  }, [inputStates]);
```

The effect watches `inputStates` and compiles the user-entered code by mapping through the `digit` of each input box’s state and joining them as a string.

Also, we don’t want to return the code incomplete to the user so we only set the state of `code` to `finalCode` if it equals the parameter `codeLength` else `null` otherwise. This ensures that if we need a code length of 6, we will get a 6-digit code else we will get a nullish value.

And finally, at the end of `useVerificationHook` we need to return these values to the file we will be importing our custom hook into:

```js
return { code, inputStates, inputClass, handleChange, handleKeyDown };
```

Section 2: Using the custom hook

If you decided not to follow the first section above on building the hook, you can still use the custom hook by installing the custom hook library I published on npm like so:

```
npm i React-code-hook
```

It is built with TypeScript so you can use any flavor of JS you want.

Moving on, import the React custom hook into your component file:

```js
// if you built the hook yourself
import useVerificationHook from "./lib/useVerificationCode";

// if you installed the library
import useVerificationHook from "React-code-hook"
```

Now we can use it in our project like so:
```jsx
function App() {
	const { code, inputStates, inputClass, handleChange, handleKeyDown } =
    useVerificationHook(6);
}
```

The custom hook, `useVerificationHook` is a function that takes a number as an argument which is the length of the code to be entered by the user, and it returns an object with the following values

  • `code`: The actual code (`string`) entered by the user if it is complete or `null` otherwise
  • `inputStates`: An array containing stateful objects we’ll use to track each input box. The shape of the objects stored inside is `{ digit, setDigit }`. The array’s length is equal to the number passed to the hook as its argument.
  • `inputClass`: String used to target our input boxes so that `handleChange` can move focus whenever they change
  • `handleChange`: `onChange` event handler that moves focus to the next or previous input if the current input is changed. It accepts 2 arguments, the event `e` and the index of the current input state `ii`.
  • `handleKeyDown`: `keyDown` event handler that prevents characters like `E` from being entered into an input box. For some quirky reason, Javascript processes it as a number.

Finally, we can use the destructured values in our JSX like so:

```js
function App() {
	const { code, inputStates, inputClass, handleChange, handleKeyDown } =
    useVerificationHook(6);

   return (
	<div>
		{inputStates.map((state, ii) => {
		  return (
			<input
			  type="number"
			  value={state.digit}
			  className={inputClass}
			  onChange={(e) => handleChange(e, ii)}
			  onKeyDown={handleKeyDown}
			/>
		  );
		})}
	  </div>
	)
}
```

Here we loop through `inputStates` and return an input box with its value attribute set to `state.digit` which is the state of an input box in the loop. We also add the other values to their respective attributes above.

You can also check the value of `code` to see if it’s `null` (which would mean the user has not completely filled the input boxes) or a `string` (Which means the input boxes are completely filled) like so:

```jsx
function App() {
	const { code, inputStates, inputClass, handleChange, handleKeyDown } =
    useVerificationHook(6);

   return (
   <>
	   <div>
		{inputStates.map((state, ii) => {
		  return (
			<input
			  type="number"
			  value={state.digit}
			  className={inputClass}
			  onChange={(e) => handleChange(e, ii)}
			  onKeyDown={handleKeyDown}
			/>
		  );
		})}
	  </div>
	  <div>
		  <p>
              <b>Code:</b>{" "}
              {code ? code : "Fill up the boxes to see the code here..."}
            </p>
	  </div>
   </>
	
	)
}
```

Testing the custom hook

Now visit your browser and test your component and you should see that entering a digit moves focus to the next input box and deleting a digit moves focus to the previous one. Sweet.

Conclusion

In this blog post, you have learned how to accept verification codes from your users in an interactive way by either creating a React custom hook for the functionality or consuming my published React custom hook library.

Thanks for reading, I hope this article helps you in spicing up your interfaces with something interactive and if you have not done so yet, please star the library on Github 😄.

You can also connect with me via my website below, I’m open to work and collaboration opportunities: Here is my website. 

Thank you and have a great day.

If you’re a developer looking for high-paying remote React jobs with top US companies, try Turing. Visit the Apply for Jobs page to know more.

Join a network of the world's best developers and get long-term remote software jobs with better compensation and career growth.

Apply for Jobs

By Mar 30, 2023