skip to Main Content

I am using 4 digit OTP log-in in my React app and I want to convert the OTP characters to "*" after 0.5 seconds. I am successfully converting my 4 digit OTP to asterisks but it modifies my state to "****", and because of this the OTP does not match with the database and I can’t log in. How can I only show converted OTP in the UI such that it does not modify my state so it matches with the database?

const [OTP, setOTP] = useState('');

useEffect(() => {
  const timer = setTimeout(() => {
    const maskedOTP = OTP.replace(/d/g, '*');
    setOTP(maskedOTP);
  }, 500);

  return () => clearTimeout(timer);
}, [OTP]);

const onOtpChange = (value) => {
  if (value.length <= 3) {
    props.resetLogin();
  } else if (value.length === 4) {
    props.resetResendOtp();
    props.login({
      username: auth.get('userName'),
      password: auth.get('password'),
      otp: value,
      type: "WEB",
      serial: window.localStorage.getItem("visitorId"),
      showProfileWizard: true, // auth.get("showProfileWizard")
      showEftDialog : true
    })
  }
  setOTP(value)
}


<div className="mt-3 d-flex justify-content-center flex-column align-items-center">
  <h3 className="text-center text-lowercase">
    <b>{auth && auth.get('otpTarget')}</b>
  </h3>
  <OtpInput
    shouldAutoFocus
    onChange={onOtpChange}
    numInputs={4}
    isInputNum={true}
    value={OTP}
    separator={<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>}
    inputStyle={{
      width: mobileWidth || tabletWidth ? 40 : 60,
      height: mobileWidth || tabletWidth ? 40 : 60,
      backgroundColor: '#EDEDEC',
      border: 'none',
      borderRadius: 20,
    }}
    focusStyle={{
      borderRadius: 20,
      border: `2px solid ${theme.palette.primary.main}`,
      outline: 'none',
    }}
  />
</div>

I try to successfully convert it but don’t know how I show in the UI without modifying the state.

2

Answers


  1. Add a maskedOTP state and pass it in value property of OtpInput.
    When user type OTP, onOtpChange function triggers and setOTP(value) will update the OTP which further triggers useEffect and change maskedOTP to ‘*’ after 0.5 sec.

    const [OTP, setOTP] = useState('');
    const [maskedOTP, setMaskedOTP] = useState('');
    
    useEffect(() => {
        const timer = setTimeout(() => {
          const maskedOTP = OTP.replace(/d/g, '*');
          setMaskedOTP(maskedOTP);
        }, 500);
    
        return () => clearTimeout(timer);
      }, [OTP]);
    
    // ...
    
    <OtpInput
      shouldAutoFocus
      onChange={onOtpChange}
      numInputs={4}
      isInputNum={true}
      value={maskedOTP}
      // ...
    />
    
    Login or Signup to reply.
  2. If I’ve understood correctly, you want to maintain an "unmasked" OTP value while displaying a partially masked OTP value in the UI. It’s kind of tricky, but I believe the basic gist is that you should maintain two OTP states. One is updated and holds the actual numerical value and is never masked, the other is conditionally/partially masked based on input and is the state value displayed in the input.

    The reset/login logic should also be moved to the useEffect hook since the OTP state value is what you want to reference.

    You didn’t specify either way, but I’m assuming the input is user mutable and should handle input changes, so the solution I’m suggesting handles this use case.

    Example:

    const [OTP, setOTP] = useState('');
    const [maskedOTP, setMaskedOTP] = useState("");
    
    useEffect(() => {
      if (OTP.length <= 3) {
        props.resetLogin();
      } else if (OTP.length === 4) {
        props.resetResendOtp();
        props.login({
          username: auth.get('userName'),
          password: auth.get('password'),
          otp: OTP,
          type: "WEB",
          serial: window.localStorage.getItem("visitorId"),
          showProfileWizard: true, // auth.get("showProfileWizard")
          showEftDialog : true
        });
      }
    
      const timer = setTimeout(() => {
        const maskedOTP = OTP.replace(/d/g, '*');
        setMaskedOTP(maskedOTP);
      }, 500);
    
      return () => clearTimeout(timer);
    }, [OTP]);
    
    const onOtpChange = (value) => {
      setOTP((otp) => {
        if (value.length < otp.length) {
          // Removing characters, "trim" to length of input value
          return otp.slice(0, value.length);
        }
        // Adding characters, append new trailing input characters
        return otp.concat(value.slice(otp.length));
      });
      setMaskedOTP((maskedOTP) => {
        if (value.length > maskedOTP?.length) {
          // Adding characters, append new trailing input characters
          return maskedOTP.concat(value.slice(maskedOTP.length));
        }
        // Removing characters, "trim" to length of input value
        return maskedOTP.slice(0, value.length);
      });
    }
    
    ...
    
    <div className="mt-3 d-flex justify-content-center flex-column align-items-center">
      <h3 className="text-center text-lowercase">
        <b>{auth && auth.get('otpTarget')}</b>
      </h3>
      <OtpInput
        shouldAutoFocus
        onChange={onOtpChange}
        numInputs={4}
        isInputNum
        value={maskedOTP}
        separator={<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>}
        ...
      />
    </div>
    

    Demo

    Edit react-otp-input-change-but-it-modifies-my-state-because-of-this-i-cant-login-us

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search