2

I have a header component as a function component. I want show a popup when logo text is clicked. After for a time it should close automatically. I use hooks for state of popup. But set state function doesn't work in setTimeout function. How can fix this?

import Link from 'next/link'
import style from './header.module.css'

const Header = () => {
  const [popupOpen, setPopupOpen] = React.useState(false)

  return (
    <header className={style.header}>
      <nav className={style.nav}>
        <div
          className={style.popupContainer}
          onClick={() => {
            setPopupOpen(!popupOpen)
            console.log(popupOpen)
            setTimeout(() => {
              console.log(popupOpen)
              setPopupOpen(!popupOpen)
              console.log(popupOpen)
            }, 1000)
          }}
        >
          <span className={style.logo}>Logo</span>

          <span
            className={`${style.popupText} ${
              popupOpen ? style.show : style.hide
            }`}
          >
            Popup Text
          </span>
        </div>

        <ul className={style.ul}>
          <li>
            <Link href='/'>
              <a>.home</a>
            </Link>
          </li>
          <li>
            <Link href='/contact'>
              <a>.contact</a>
            </Link>
          </li>
        </ul>
      </nav>
    </header>
  )
}

export default Header

Console log:

console log

FK7
  • 43
  • 2
  • 6

2 Answers2

0

Let me suggests, this is the same question as:

React - useState - why setTimeout function does not have latest state value?

const _onClick = () => {
     setPopupOpen(!popupOpen);

     setTimeout(() => {
         setPopupOpen(popupOpen => !popupOpen)
     }, 2000);
};
AymericFi
  • 140
  • 1
  • 8
0

Its happening because setPopupOpen is asynchronous. So by the time setPopupOpen(!popupOpen) is called it has same value as onClick first setPopupOpen(!popupOpen) so eventually when it called both setPopup doing same state update i.e both updating as false. Better way is to usesetPopupOpen callback function to update the value. I added this code.

import { useState } from "react";
import Link from "next/link";
import style from "./style.module.css";

const Header = () => {
  const [popupOpen, setPopupOpen] = useState(false);

  const toggle = () => {
    setPopupOpen((prev) => !prev);
  };
  const onClick = () => {
    setPopupOpen(!popupOpen);
    setTimeout(() => {
      toggle();
    }, 1000);
  };

  return (
    <header className={style.header}>
      <nav className={style.nav}>
        <div className={style.popupContainer} onClick={onClick}>
          <span className={style.logo}>Logo</span>

          {popupOpen && (
            <span
              className={`${style.popupText} ${
                popupOpen ? style.show : style.hide
              }`}
            >
              Popup Text
            </span>
          )}
        </div>

        <ul className={style.ul}>
          <li>
            <Link href="/">
              <a>.home</a>
            </Link>
          </li>
          <li>
            <Link href="/contact">
              <a>.contact</a>
            </Link>
          </li>
        </ul>
      </nav>
    </header>
  );
};

export default function IndexPage() {
  return (
    <div>
      <Header />
    </div>
  );
}


Here is the demo: https://codesandbox.io/s/pedantic-haibt-iqecz?file=/pages/index.js:0-1212

Shubham Verma
  • 4,918
  • 1
  • 9
  • 22