I am creating a menu component in React where when the menu opens, and user clicks anywhere outside the menu it'll close the menu component. There are two approaches I saw, one is to use JS event listener like this
import React, { useState, useEffect } from 'react';
const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const toggleDropdown = () => setIsOpen(!isOpen);
useEffect(() => {
const handleClickOutside = (event) => {
if (isOpen && !event.target.closest('.dropdown')) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen]);
return (
<div className="dropdown" onClick={toggleDropdown}>
{isOpen ? (
<div className="dropdown-menu">
{/* Dropdown Content */}
</div>
) : null}
</div>
);
};
Another approach would be in with React event handler like this:
import React, { useState } from 'react';
const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
// Toggle Dropdown open/close
const toggleDropdown = () => setIsOpen(!isOpen);
// Close Dropdown if open, when clicking outside
const handleClickOutside = (e) => {
if (isOpen && !e.currentTarget.contains(e.target)) {
setIsOpen(false);
}
};
return (
<div onClick={handleClickOutside}>
<button onClick={(e) => {
e.stopPropagation(); // Prevents click from "bubbling" up to the div
toggleDropdown();
}}>
Toggle Dropdown
</button>
{isOpen && (
<div className="dropdown-menu">
{/* Dropdown content */}
</div>
)}
</div>
);
};
so which one is more of a best practice ? on one hand you have to assign global event listeners, on one hand you are dealing with event bubbling in react.
Well, the idiomatic way is for anything within React to be React. So yea, stick to synthetic events. Bear in mind that this is only applicable in "reactive" behavior.
Set that aside for a moment, and instead focus on Event API. So, once you click a button, that button gains focus. Once you click something else, said button loses focus and fires the blur event. Now, instead of listening to a generic click event we can ulitlize this, and thus:
Don't mind me registering onPointerDown instead of onClick. It's a force of habit. Here's the MDN