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

Tell us what’s happening:

Tests 12 and 13 aren’t passing, even though the UI is working fine. Another post ( Build a One-Time Password Generator - Build a One-Time Password Generator ) mentions a solution using setInterval(), but that didn’t work for me. Any ideas why these tests aren’t passing?

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 */
* {
  font-family: sans-serif;
}

.container {
  width: 70%;
  margin: auto;
  margin-top: 20vh;
  text-align: center;
  border: 1px solid grey;
  border-radius: 10px;
  padding: 10px;
}
/* file: index.jsx */
const { useState, useEffect, useRef } = React;

export const OTPGenerator = () => {
  const [otp, setOtp] = useState("Click 'Generate OTP' to get a code");
  const [expirationMsg, setExpirationMsg] = useState("");
  const [countdown, setCountdown] = useState(null);
  const [countdownTimer, setCountdownTimer] = useState(null);
  const buttonRef = useRef(null);

  const countdownMaxValue = 5;

  useEffect(() => {
    if (otp === "Click 'Generate OTP' to get a code") return;

    setCountdown(countdownMaxValue)

    setCountdownTimer(
        setInterval(() => {setCountdown(prev => prev - 1)}, 1000)
    )
  }, [otp])

  useEffect(() => {
    if (countdown === null) return;

    if (countdown === 0) {
      setExpirationMsg("OTP expired. Click the button to generate a new OTP.");

      clearInterval(countdownTimer);
      return;
    }

    setExpirationMsg(`Expires in: ${countdown} seconds`)
  }, [countdown])

  function handleOtpGeneration() {
    const newOtpNumbers = []
    for (let i = 0; i < 6; i++) {
      newOtpNumbers[i] = Math.floor(Math.random() * 10)
    }
    setOtp(newOtpNumbers.join(""));
  }

  return (
    <div className="container">
      <h1 id="otp-title">OTP Generator</h1>
      <h2 id="otp-display">{otp}</h2>
      <p id="otp-timer" aria-live="polite">
        {expirationMsg}
      </p>
      <button
        id="generate-otp-button"
        onClick={handleOtpGeneration}
        ref={buttonRef}
        disabled={countdown > 0}
      >
        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/139.0.0.0 Safari/537.36

Challenge Information:

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

https://www.freecodecamp.org/learn/full-stack-developer/lab-one-time-password-generator/build-a-one-time-password-generator

If you take a look at the browser’s console (not the one on the page), there’s error with more details. When tests run, the text on page is still the initial one. Remember that state changes are not instantly reflected on page, but at least after the next render.

That was so hard to work out what was going on! :sweat_smile: I learned a lot about the guts of the React render cycle though. I had to have chatGPT coach me through it, but I got there eventually! (For anyone interested, when I decide to recruit chatGPT to help me learn something in programming, I tell it specifically to not give me any answers, but only to nudge me in the right direction.) But of course thanks for the initial hint @sanity.