I create an app using React hooks. I have a helper function onClickOutsideHook(ref, callback)
that trigger the callback
when you click outside of the component that provide the ref
using React.useRef
:
export const onClickOutsideHook = (ref, callback) => {
// Hook get from https://stackoverflow.com/a/42234988/8583669
React.useEffect(() => {
const handleClickOutside = event => {
if (ref?.current && !ref.current.contains(event.target)) {
callback();
}
};
// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, [callback, ref]);
};
I have a component Dropdown
that use this helper so it close when you click outside of it. This component has a Modal
component as children that use ReactDOM.createPortal
. I use it to render the Modal
in body
so it can cover all the app screen. My Modal
contain a button that alert a message when you clicked on it:
function Modal() {
return ReactDOM.createPortal(
<div
style={{
position: "absolute",
top: 0,
left: 0,
height: "100%",
width: "100%",
background: "rgba(0,0,0,0.6)"
}}
>
<button onClick={() => alert("Clicked on Modal")}>Click</button>
</div>,
document.body
);
}
function Dropdown(props) {
const [isModalOpen, setIsModalOpen] = React.useState(false);
const dropdownRef = React.useRef(null);
onClickOutsideHook(dropdownRef, props.onClose);
return (
<div ref={dropdownRef}>
Click outside and I will close
<button onClick={() => setIsModalOpen(true)}>open Modal</button>
{isModalOpen ? <Modal /> : <></>}
</div>
);
}
The problem is when I click on the Modal
button to trigger the alert, the Dropdown
is closed before since I clicked outside of it (Modal
is not rendered as a children of Dropdown
but in body
). So my alert is never triggered.
Is there a way to define Modal
as a children of Dropdown
using ref
but still render it in body
using ReactDOM.createPortal
?
Just have a look to the CodeSandbox.