I have a custom dropdown that is triggered by a button. If it is expanded, then isExpanded===true. To close it, I use useEffect, which adds a click handler to the entire document only if isExpanded is true.
The problem: the click handler is triggered as soon as I click on the button even before it is assigned, and therefore the dropdown content immediately disappears (does not appear on the page).
It feels like document.addEventListener is assigned before useEffect fires.
const [isExpanded, setIsExpanded] = useState(false);
useEffect(() => {
if (!isExpanded) {
return;
}
const handleDocumentClick = () => {
setIsExpanded(false);
};
document.addEventListener('click', handleDocumentClick);
return () => {
document.removeEventListener('click', handleDocumentClick);
};
}, [isExpanded]);
<button
type="button"
onClick={setIsExpanded(current => !current)}
>
Sort by:
</button>
<div
style={{ display: isExpanded ? 'block' : 'none' }}
>
Some content
</div>
I’ve tried moving the logic from useEffect directly to the button handler, but the document.addEventListener still fires before it’s assigned.
2
Answers
You can stop the event propagation in the button click handler.
Modify your button’s click handler to accept the event parameter and call event.stopPropagation() to prevent the event from propagating further:
By stopping the event propagation, the document-level click event won’t be triggered immediately after clicking the button, allowing the dropdown content to appear correctly.