前言
最近开发忘记密码功能,需要通过邮箱或者短信接收验证码,然后通过一次性输入框的形式来输入验证码,如下图。然后antd中一次性密码框,在5.16.0版本是有该组件的,但是现有的项目的antd在该版本之前,所以只能自己手搓一个了:
代码实现
index.tsx
/**
*
* 一次性密码/验证码输入框
*/
import React, { useState, useRef, useEffect } from 'react';
import './index.less';
interface OtpInputProps {
length: number;
isNumber?: boolean;
onChange?: (otp: string) => void;
onComplete: (otp: string) => void;
}
const OtpInput: React.FC<OtpInputProps> = (props) => {
const { length, isNumber = false, onChange, onComplete } = props;
const [otp, setOtp] = useState<string[]>(Array(length).fill(''));
const inputsRef = useRef<HTMLInputElement[]>([]);
const handleChange = (value: string, index: number) => {
// 数字输入
if (isNumber && !/^\d*$/.test(value)) return;
const newOtp = [...otp];
newOtp[index] = value.slice(-1);
setOtp(newOtp);
if (onChange) {
onChange(newOtp.join(''));
}
// 如果当前输入不为空,则将焦点移至下一个输入字段
if (value && index < length - 1) {
inputsRef.current[index + 1].focus();
}
// 如果OTP完成,触发onComplete
if (newOtp.join('').length === length) {
// 输入完成
onComplete(newOtp.join(''));
}
};
const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
if (e.key === 'Backspace' && !otp[index] && index > 0) {
inputsRef.current[index - 1].focus();
}
};
useEffect(() => {
inputsRef.current[0]?.focus();
}, []);
return (
<div className={'my-otp'}>
{Array(length)
.fill('')
.map((_, index) => (
<input
key={index}
type="text"
maxLength={1}
value={otp[index]}
onChange={(e) => handleChange(e.target.value, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
ref={(el) => (inputsRef.current[index] = el!)}
className={'my-otp-input'}
/>
))}
</div>
);
};
export default OtpInput;
index.less
.my-otp {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
&-input {
width: 36px;
height: 36px;
border-radius: 8px 8px 8px 8px;
text-align: center;
border: solid 1px rgba(#0b2470, 0.1);
&:focus {
border: solid 1px rgba(#0b2470, 0.1);
outline: none;
box-shadow: 0 2px 4px 0 rgba(11, 36, 112, 0.1);
}
}
}
总结
至此就通过代码实现了一次性密码/验证码输入框。