Next.js Discord

Discord Forum

Need help with disappearing popup after onClick() event

Answered
chan posted this in #help-forum
Open in Discord
I'm creating a custom popup in React that shows up after a button click, and I want to go away from the screen after X amount of seconds. The issue I'm running into is that if you click the button multiple times before the initial timeout ends, you get very strange behavior like the popup jumping in and out or just not showing up at all.

I realize that this is due to me misusing React state, but I am not sure how to get around this issue/structure my code different. Can somebody please help me? I have tried multiple possible solutions for many hours but can't wrap my brain around it.

function popupHandler(popupText, setPopupVis, setPopupText) {
    setPopupText(popupText)
    setPopupVis("visible");
    // Works when you click a button ONCE and then don't touch anything during the timeout windows
    // Totally breaks if you do otherwise. 
    // I want to "extend" the timeout so that the 
    //  popup window keeps staying up if the button keeps getting clicked
    setTimeout(() => {
        setPopupVis("hidden")
    }, 3000);
}

function CustomButton({setPopupText, setPopupVis}) {
    return (
        <button onClick={() => popupHandler("Dynamic string", setPopupText, setPopupVis)}>
            I am a button
        </button>
    )
}

function MyPopUp({popupVis, popupText}) {
    return (
        <div style={{visibility: popupVis}}>
            {popupText}
        </div>
    )
}

export default function MyContent() {
    const [popupVis, setPopupVis] = useState('hidden');
    const [popupText, setPopupText] = useState('')
    return (
        <>
            <CustomButton setPopupText={setPopupText} setPopupVis={setPopupVis} />
            <MyPopUp popupVis={popupVis} popupText={popupText} />
        </>
    )
}
Answered by Ray
then add this to CustomerButton
function CustomerButton({setPopupText, setPopupVis}) {
  const timerRef = useRef();

  const handleClick = (popupText) => {
    setPopupText(popupText)
    setPopupVis("visible");
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      setPopupVis("hidden");
    }, 300);
  };
  
  return (
    <button onClick={() => handleClick("Dynamic string")}>I am a button</button>
  )
}
View full answer

14 Replies

@chan I'm creating a custom popup in React that shows up after a button click, and I want to go away from the screen after X amount of seconds. The issue I'm running into is that if you click the button multiple times before the initial timeout ends, you get very strange behavior like the popup jumping in and out or just not showing up at all. I realize that this is due to me misusing React state, but I am not sure how to get around this issue/structure my code different. Can somebody please help me? I have tried multiple possible solutions for many hours but can't wrap my brain around it. function popupHandler(popupText, setPopupVis, setPopupText) { setPopupText(popupText) setPopupVis("visible"); // Works when you click a button ONCE and then don't touch anything during the timeout windows // Totally breaks if you do otherwise. // I want to "extend" the timeout so that the // popup window keeps staying up if the button keeps getting clicked setTimeout(() => { setPopupVis("hidden") }, 3000); } function CustomButton({setPopupText, setPopupVis}) { return ( <button onClick={() => popupHandler("Dynamic string", setPopupText, setPopupVis)}> I am a button </button> ) } function MyPopUp({popupVis, popupText}) { return ( <div style={{visibility: popupVis}}> {popupText} </div> ) } export default function MyContent() { const [popupVis, setPopupVis] = useState('hidden'); const [popupText, setPopupText] = useState('') return ( <> <CustomButton setPopupText={setPopupText} setPopupVis={setPopupVis} /> <MyPopUp popupVis={popupVis} popupText={popupText} /> </> ) }
try this
useEffect(() => {
    if (popupVis==='visible') {
      const timeout = setTimeout(() => {
        setPopupVis("hidden");
      }, 3000);

      return () => {
        clearTimeout(timeout);
      };
    }
  }, [popupVis]);
I tested around with it and it still has strange behavior. If you continue clicking the button while the first timeout is counting down, your current popup will close once the first timeout is complete.
you want to reset the timer on every click?
yes
@chan yes
remove the useEffect on MyContent
then add this to CustomerButton
function CustomerButton({setPopupText, setPopupVis}) {
  const timerRef = useRef();

  const handleClick = (popupText) => {
    setPopupText(popupText)
    setPopupVis("visible");
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      setPopupVis("hidden");
    }, 300);
  };
  
  return (
    <button onClick={() => handleClick("Dynamic string")}>I am a button</button>
  )
}
Answer
I put the timerRef in MyContent and it worked
thank you!!!
eyes are opened after this experience. need to learn more about the React hooks besides just getState 😳
this solution also works with getState()
thank you again