react实现一次性密码输入

82 阅读1分钟

思路是写出用于展示密码的div(比如四位的密码那就是4个div),在div上面写一个实际的input控制输入,再设置input不可见。上代码

import React, { useEffect, useRef, useState } from "react";
import './index.scss';

interface IInputOTP {
  length: number;
  handleInputDone: Function;
  password: string;
}

const InputOTP = (props: IInputOTP) => {
  const prefixCls = 'input-OTP';

  const { handleInputDone, length, password, keyCode } = props
  
  const KEYCODE = keyCode
  const PIN_LENGTH = length;
  const [value, setValue] = useState("");
  const inputRef = useRef(null);

  useEffect(() => {
    setValue(password)
  }, [password])

  const handleClick = (e: any) => {
    e.preventDefault();
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }

  const handleChange = (e: any) => {
    let reg=/^[0-9]+.?[0-9]*$/
    if (e.target.value.length <= PIN_LENGTH) {
      if (e.target.value.length > 0 && reg.test(e.target.value) || e.target.value.length === 0) {
        const val = e.target.value || "";
        setValue(val);
        if (e.target.value.length === PIN_LENGTH) {
          handleInputDone(val)
        }
      } else {
        return
      }
    } else {
      return
    }
  }

  // 处理一些键盘特殊按键,清除默认行为
  const handleOnKeyDown = (e: any) => {
    switch (e.keyCode) {
      case keyCode.LEFT_ARROW:
      case keyCode.RIGHT_ARROW:
      case keyCode.HOME:
      case keyCode.END:
      case keyCode.SPACE:
        e.preventDefault();
        break;
      default:
        break;
    }
  }

  return (
    <div className={prefixCls}>
      <input
        ref={inputRef}
        className={`${prefixCls}-hiddenInput`}
        type="tel"
        pattern="[0-9]*"
        onChange={handleChange}
        onKeyDown={handleOnKeyDown}
        value={value}
        autoFocus
      />
      {Array.from({ length: PIN_LENGTH }).map((_, index) => {
        const focus =
          index === value.length ||
          (index === PIN_LENGTH - 1 && value.length === PIN_LENGTH);
        return (
          <input
            className={`${prefixCls}-pinInput ${focus ? `${prefixCls}-fucos` : null}`}
            key={index}
            value={value[index]? '∗' : ''}
            onClick={handleClick}
            readOnly={true}
          />
        );
      })}
    </div>
  );
}

export default InputOTP;

.input-OTP {
  display: flex;
  width: 100%;
  flex-wrap: nowrap;
  justify-content: center;
  &-hiddenInput {
    color: transparent;
    outline: none;
    padding: 0;
    border-width: 0;
    box-shadow: none;
    position: absolute;
    caret-color: transparent;
    text-shadow: 0 0 0 #000;
    margin-left: -200%
  }

  &-pinInput {
    box-sizing: border-box;
    padding: 0;
    outline: none;
    background-color: transparent;
    width: 120px;
    height: 120px;
    background: #EBEBEB;
    color: #000000;
    font-size: 52px;
    border-radius: 24px;
    border: none;
    margin-right: 24px;
    text-align: center;
    line-height: 120px;
  }
  &-fucos {
    border-color: orangered;
    border-width: 2px;
  }
}