Build a One-Time Password Generator - Build a One-Time Password Generator

Tell us what’s happening:

Only step 10 is failing but the application is functionally similar to the example project. Appreciate any insights!

Your code so far

<!-- file: index.html -->
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>OTP Generator</title>
    <link rel="stylesheet" href="styles.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
    <script
      data-plugins="transform-modules-umd"
      type="text/babel"
      src="index.jsx"
    ></script>
</head>

<body>
    <div id="root"></div>
    <script
      data-plugins="transform-modules-umd"
      type="text/babel"
      data-presets="react"
      data-type="module"
    >
      import { OTPGenerator } from './index.jsx';
      ReactDOM.createRoot(document.getElementById('root')).render(<OTPGenerator />);
    </script>
</body>

</html>
/* file: styles.css */
:root {
  text-align: center;
  display: flex;
  justify-content: center;
}

.container {
  background-color: #1ed233;
  padding: 10px;
  border-radius: 5px

}
/* file: index.jsx */

const { useState, useEffect, useRef } = React;


export function OTPGenerator () {
  const [count, setCount] = useState(5)
  const [started, setStarted] = useState(false) 
  const [btnPressed, setBtnPressed] = useState(false)
  const [password, setPassword] = useState('')
  const btnRef = useRef(null) // used to disable button

  function generateOtp () {
    return 100000 + Math.floor(Math.random()*900000)
  }

  useEffect(() => {
    if (!btnPressed) { // only start effect when button pressed
      return
    }
    setStarted(true)
    setPassword(generateOtp())
    
    btnRef.current.disabled = true
    const id = setInterval(() => {
    setCount(prev => {
      if (prev === 1) {
        clearInterval(id);
        setBtnPressed(false);
        btnRef.current.disabled = false
        return 5; // reset
      }
      return prev - 1;
    });
  }, 1000);

  return () => clearInterval(id);

  }, [btnPressed])

  return (
    <div className='container'>
      <h1 id='otp-title'>OTP Generator</h1>
      <h2 id='otp-display'>{password?password:"Click 'Generate OTP' to get a code"}</h2>
      <p id='otp-timer' aria-live='assertive' >{started?(btnPressed?`Expires in: ${count} seconds`:'OTP expired. Click the button to generate a new OTP.'):''}</p>
      <button id='generate-otp-button' ref={btnRef} onClick={() => {setBtnPressed(true)}} >Generate OTP</button>
    </div>
  )
};

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36

Challenge Information:

Build a One-Time Password Generator - Build a One-Time Password Generator

From what I’m seeing, the issue might be timing. If you open browser’s console (not console on page), there are some details regarding the failing test. When test checks the text, it’s still the Click ‘Generate OTP’ to get a code.

Remember that state updates are not reflected on page instantly. For the text to update couple things needs to happen, which slightly delays the text change (Keep in mind that in here it’s not something that can be easily noticed):

  • Clicking button changes btnPressed.
  • Effect runs with changed btnPressed, it changes started and password.
  • Only at this point the actual change of the text occurs and can be rendered.
1 Like