use-onclickoutside
use-onclickoutside copied to clipboard
Doesn't handle portals
I don't know if this should be classed as a bug or not, but its something I'd want a hook like this to do if it were to replace the horrendous hacky code I wrote to do the same.
Events in React propagate through component hierarchies rather than DOM hierarchies but in this case, because you're looking at DOM events, an outside click doesn't behave the same as a conventional click. I have some use cases where this is distinction is important (for example a popout within a modal, both of which use portals).
I'm interested to know how / if this can be achieved with in this hook?
I'm afraid that's not possible without hacks. React doesn't give access to its tree and we can't just traverse it.
My solution for this was to create nested portals in the DOM:
import * as React from "react";
import * as ReactDOM from "react-dom";
const PortalContext = React.createContext(
typeof document !== "undefined" ? document.body : null
);
export function Portal({ children }) {
// if it's a nested portal, context is the parent portal
// otherwise it's document.body
const context = React.useContext(PortalContext);
const [container] = React.useState(() => {
if (typeof document !== "undefined") {
return document.createElement("div");
}
// ssr
return null;
});
React.useLayoutEffect(() => {
if (!container || !context) return undefined;
context.appendChild(container);
return () => {
context.removeChild(container);
};
}, [container, context]);
if (container) {
const portal = ReactDOM.createPortal(children, container);
return (
<PortalContext.Provider value={container}>
{portal}
</PortalContext.Provider>
);
}
// ssr
return null;
}
Then you can pass the portal element to useOnClickOutside
.
@diegohaz this is interesting, would you be able to prepare a simple codesandbox showcasing how this could be used? I'm not sure if I understand how this is supposed to work with DOM and outside clicks
@Andarist There you go: https://codesandbox.io/s/o9086wk8j5
It doesn't close nested portals when closing the parent ones though. But I think it's doable. I just made it really fast.