use-onclickoutside icon indicating copy to clipboard operation
use-onclickoutside copied to clipboard

Doesn't handle portals

Open jamiewinder opened this issue 6 years ago • 4 comments

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?

jamiewinder avatar Feb 08 '19 18:02 jamiewinder

I'm afraid that's not possible without hacks. React doesn't give access to its tree and we can't just traverse it.

Andarist avatar Feb 09 '19 12:02 Andarist

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 avatar Mar 14 '19 17:03 diegohaz

@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 avatar Mar 15 '19 09:03 Andarist

@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.

diegohaz avatar Mar 15 '19 13:03 diegohaz