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. Can somebody please help me?
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} />
</>
)
}
3
Answers
You can get the required functionality by adding another state variable that keeps track of your setTimeout() timers.
The issue you’re facing stems from the fact that each time the button is clicked, a new timeout is set without clearing the previous one. This leads to unexpected behavior, especially when rapid clicks occur. To address this, you can use the clearTimeout function to clear the previous timeout before setting a new one. Here’s how you can modify your code to handle this:
In this modification, I’ve introduced a timerId property attached to the popupHandler function to keep track of the current timeout. Before setting a new timeout, it checks if there’s an existing timeout and clears it using clearTimeout. This ensures that only one timeout is active at any given time, preventing the erratic behavior you were experiencing with rapid button clicks.
give popupHandler acces to popusVis. if popupVis is already "visible," return early.