UNPKG

react-native-otp-ui-kit

Version:

react-native-otp-ui-kit is a simple and highly customizable React Native component for entering OTP (One-Time Password) on iOS, Android, and Web. It provides an intuitive and user-friendly interface for inputting one-time passwords in your React Native ap

113 lines (94 loc) 3.38 kB
import { useEffect, useRef, useState } from 'react'; import { TextInput } from 'react-native'; import { OtpInputProps } from './OTPInputProps'; interface UseOtpInputProps extends OtpInputProps { length: number; } export const useOtpInput = ({ length, onCodeChanged, onOTPFilled, disabled = false, autoFocus = true, }: UseOtpInputProps) => { const [otp, setOtp] = useState<string[]>(Array(length).fill('')); const [focusedIndex, setFocusedIndex] = useState<number | null>(null); const inputRefs = useRef<Array<TextInput | null>>([]); // Automatically focus the first input on mount if autofocus is true useEffect(() => { if (autoFocus) { inputRefs.current[0]?.focus(); } }, [autoFocus]); const handleTextChange = (index: number, text: string) => { if (isNaN(Number(text)) || disabled) return; const newOtp = [...otp]; newOtp[index] = text; setOtp(newOtp); const combinedOtp = newOtp.join(''); if (onCodeChanged) { onCodeChanged(combinedOtp); } if (combinedOtp.length === length && onOTPFilled) { setTimeout(() => onOTPFilled(combinedOtp), 100); } // Move to next input if there's text and it's not the last field if (text && index < otp.length - 1) { let nextIndex = index + 1; while (nextIndex < otp.length && newOtp[nextIndex]) { nextIndex++; } inputRefs.current[nextIndex]?.focus(); } }; const handleFocus = (index: number) => { // Get the index of the first empty OTP field const firstEmptyIndex = otp.indexOf(''); let targetIndex = index; // Check if the user is moving backward and focus the previous empty field if (otp[index]) { targetIndex = index; } else if (firstEmptyIndex !== -1) { // If there is an empty field, focus the first empty field targetIndex = firstEmptyIndex; } else { // If no empty field, maintain focus on the current index targetIndex = index; } setFocusedIndex(targetIndex); // Update the focused index inputRefs.current[targetIndex]?.focus(); // Focus the input field }; const handleBlur = () => { setFocusedIndex(null); }; const handleKeyDown = (index: number, event: { nativeEvent: { key: string } }) => { if (event.nativeEvent.key === 'Backspace' && index > 0) { const newOtp = [...otp]; if (!newOtp[index]) { // Move to the previous empty field if the current is empty const prevIndex = otp.slice(0, index).lastIndexOf(''); inputRefs.current[prevIndex !== -1 ? prevIndex : index - 1]?.focus(); } else { // Clear the current OTP field newOtp[index] = ''; setOtp(newOtp); } } }; const clear = () => { setOtp(Array(length).fill('')); inputRefs.current[0]?.focus(); }; return { otp, focusedIndex, inputRefs, actions: { handleTextChange, handleFocus, handleBlur, handleKeyDown, clear, }, }; };