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.