My goal is to detect a click outside of the components using this custom hook:
const useOutsideClick = (callback) => {
const reference = useRef();
useEffect(() => {
const handleClick = (event) => {
if (reference.current && !reference.current.contains(event.target)) {
console.log(reference.current);
console.log(event.target);
callback();
}
};
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
}, [reference]);
return reference;
};
I have a <HeaderSearch />
custom component which looks like this:
const HeaderSearch = ({ screenWidth }) => {
function checkInput(event) {
if (event.keyCode !== 13) {
setSearch(event.target.value);
}
}
const mobileRef = useOutsideClick(test);
const ref = useOutsideClick(test);
if (location.pathname !== "/") {
if (screenWidth == "mobile") {
return (
<div id="search-header" className="search-header mob" ref={mobileRef}>
<FontAwesomeIcon
className="search-icon"
id="search-icon"
icon={faSearch}
onClick={() => toggleSearchInput()}
/>
<input
id="search-input"
className={searchActive ? "search-input active" : "search-input"}
type="text"
value={search}
onKeyDown={(e) => checkInput(e)}
onChange={(e) => setSearch(e.target.value)}
// ref={searchInputRef}
// onClick={() => searchReqClientSide()}
placeholder="Search..."
/>
<button></button>
</div>
);
} else {
return (
<div id="search-header" className="search-header desk" ref={ref}>
<FontAwesomeIcon
className="search-icon"
id="search-icon"
icon={faSearch}
onClick={() => toggleSearchInput()}
/>
<input
id="search-input"
className={searchActive ? "search-input active" : "search-input"}
type="text"
value={search}
onKeyDown={(e) => checkInput(e)}
onChange={(e) => setSearch(e.target.value)}
// ref={searchInputRef}
// onClick={() => searchReqClientSide()}
placeholder="Search..."
/>
<button></button>
</div>
);
}
} else {
return null;
}
};
The HeaderSearch
component is displayed conditionally by CSS classes. If parent header element has class "active"
change display of header without active to display "none"
.
However when I click outside of the component, the custom hook is still being called for both of the HeaderSearch
components. See screenshot below:
How can I make it that only the <HeaderSearch/>
which is active calls the useOutsideClick
hook instead of both firing every click?
Here is the test
callback, it's a simple log:
function test() {
console.log("clicked outside");
}
Refactor the logic such that only one useOutsideClick
hook is necessary. Since the header content, i.e. the children, appear to be same for both mobile and desk view, you can refactor to replace the wrapping div
elements with a single div
that conditionally applies the correct CSS className
prop, and any other layout/UI differences between mobile and desk views.
Example Implementation:
const HeaderSearch = ({ screenWidth }) => {
const ref = useOutsideClick(test);
...
function checkInput(event) {
if (event.keyCode !== 13) {
setSearch(event.target.value);
}
}
if (location.pathname !== "/") {
return (
<div
ref={ref}
id="search-header"
className={[
"search-header",
screenWidth == "mobile" ? "mob" : "desk"
].join(" ")}
>
<FontAwesomeIcon
className="search-icon"
id="search-icon"
icon={faSearch}
onClick={() => toggleSearchInput()}
/>
<input
id="search-input"
className={["search-input", searchActive && "active"]
.filter(Boolean)
.join(" ")
}
type="text"
value={search}
onKeyDown={(e) => checkInput(e)}
onChange={(e) => setSearch(e.target.value)}
// ref={searchInputRef}
// onClick={() => searchReqClientSide()}
placeholder="Search..."
/>
<button></button>
</div>
);
} else {
return null;
}
};