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

Tell us what’s happening:

I have problem with tests 12 and 13. The results seems the same to me as in preview project. I tried playing a little with position of setMsg but with no luck.

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 */

/* file: index.jsx */
const { useState, useEffect, useRef } = React;

export const OTPGenerator = () => {
  const [timer,setTimer] = useState(0);
  const [buttonClicked,setButtonClicked] = useState(false)
  const [otp,setOtp] = useState("Click 'Generate OTP' to get a code");
  const [msg,setMsg] = useState(null);


  const generateOTP = () => {
    const otp = [];
    for(let i=0; i<6; i++){
  let number = parseInt((Math.random()*6)+1);
  otp.push(number);
    }
    console.log(otp);
    return otp.join('');
  }


  const handleButtonClick = () =>{
    setButtonClicked(true);
    setOtp(generateOTP);
    setTimer(5);
  
  }

  useEffect(()=>{
    if(timer === 0){
      if(buttonClicked){setMsg("OTP expired. Click the button to generate a new OTP.");}
      return
    }
    
    const timeoutId = setTimeout(()=>{
      setTimer(timer - 1)
    },1000);
    setMsg(`Expires in: ${timer} seconds`);
    
    return ()=>{
      clearTimeout(timeoutId);
    }
    
  },[timer,otp])


  return(
    <div className="container">
    <h1 id="otp-title">OTP Generator</h1>
    <h2 id="otp-display">{otp}</h2>
    <p id="otp-timer" aria-live="assertive">{msg}</p>
    <button id="generate-otp-button" disabled={timer > 0} onClick={handleButtonClick}>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/142.0.0.0 Safari/537.36

Challenge Information:

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

If you take a look at the browser’s console (not the one visible on the page), there’s couple details regarding failing tests.

It looks the message that should be displayed, is not yet there when tests checks. I think this could be caused due to how state updates work in React - they might not be instant.

The way I understand it is:

  1. Button is clicked and handler updates state
  2. This triggers render
  3. Effect is run, message is updated
  4. This triggers another render

Therefore there’s at least one render after the new code is displayed, with the outdated message.