Build OTP Input with ReactJS Hooks
Build OTP input from scratch with ReactJS Hooks
Hi, I come back after a long time no articles :)
Today, I show you how to create an OTP input with ReactJS from scratch. The target result is shown above.
Let’s go!
Summary
OTP Input is a common UI for every verification transaction step. It contains a number of inputs to fill out the OTP/Verification code sent. One character per input. In this article, I use ReactJS and hooks to implement OTP Input from scratch.
Prerequisites
- Basic understanding of ReactJS.
- Used to use React Hooks
- Basic TypeScript type checking understanding
- Can code :) just kidding
Project Structure
./src
├── App.css
├── App.test.tsx
├── App.tsx
├── components
│ └── OTPInput
│ ├── index.tsx
│ └── SingleInput.tsx
├── hooks
│ └── usePrevious.ts
├── index.css
├── index.tsx
├── react-app-env.d.ts
└── serviceWorker.ts
Coding time
Part 1 : The OTPInput
The OTPInput will be described as below:
Typings first
OTPInput component
We use the useState hook to define states for this component named “activeInput” and “otpValues”.
As you see, there are many event handlers to handle and pass down to SingleInput components. These will be defined step by step. One more important thing, using the useCallback hook to returns a memoized callback.
- The easiest one onFocus handler
Just return a function which focuses the given index input by using the
“focusInput” helper.
🔨 focusInput helper
Some magic logic here to prevent focus on an unreachable input index.
const selectedIndex = Math.max(Math.min(length - 1, inputIndex), 0);
2. The onBlur handler
Return a function with the main purpose is to make the focusing lost :) by setting the activeInput to -1. So easy!!
3. The harder one onChange handler
This is the main handler for our OTPInput component to handle the changed value of every single input.
Steps happen as follows: get the right value (string or just number) => change the code at the focusing input => focus the next input.
Otherwise do nothing.
Some helpers using here: “getRightValue”, “changeCodeAtFocus” and “focusNextInput”
🔨 “getRightValue ” helper
Check the given string if it is number based on the property called “isNumberInput” and return an empty string if it is not.
🔨 changeCodeAtFocus helper
Update state “otpValues” at the currently active input index and call the “handleOtpChange ” helper to publish the changes.
Using the passed down callback named “ onChangeOTP” with the OTP string value
🔨 focusNextInput helper
Just focus on the next input index. Really? yay, that’s all? :)
4. The special one onKeyDown handler
This event handler to handle some special cases when pressing the special keys :-) like Backspace, Delete,…etc…
🔨 focusPrevInput helper
As the helper name, it just makes focus on the previous input :)
5. The last supported event handler onPaste handler
We handle the pasting case ourselves. Pastedown the value from the currently focussing input and focus on the last changed index input.
Part 2: The SingleInput
I introduce this component later because it is quite easy to understand and implement.
Typings
We extend HtmlInputElement prop types by adding a new property called “focus” to identify if the input is focussing on.
Single Input Component
Some hooks used: “useRef”, “useLayoutEffect ”and the custom “usePrevious ” hook.
🔨 useLayoutEffect hook to use instead useEffect because we want to trigger synchronously after finishing the rendering cycle.
🔨 useRef hook to get the input DOM and make it focus programmatically.
- The first render:
Autofocus the input if the flag focus and autoFocus are turned on. - The updated render:
Focus and select the input value if it lost focus since the recently render time. Thank usePrevious hook we know the previous value of the property “focus” like the way we use in componentDidUpdate in React Class Component lifecycle
🔨 The custom usePrevious hook
Idea from usehooks.com. There are a lot of ideas and useful custom hooks. You can visit and build your own hooks :-)
Final product :)
Thank you very much because you’re still reading here! The full source you can check on my codesanbox or my Github.
If you have any suggestions or questions, do not hesitate to ask me. Leave your comments below this one.