Behavior that several elements need, such as closing when you click outside, fits in a function that sets up the work and returns a ref callback:
function clickOutside(close) { let element; onSettled(() => { const onPointerDown = (event) => { if (!element.contains(event.target)) close(); }; document.addEventListener("pointerdown", onPointerDown); return () => document.removeEventListener("pointerdown", onPointerDown); }); return (el) => (element = el);}Call it in a component body, const outside = clickOutside(close), and attach the result with ref={outside}. The listener then lives exactly as long as that component.
Keep the setup in the function body, not in the returned callback. A ref callback only receives the element; it isn't a place to create effects or register cleanup.
Try it: the modal opens but never closes. Finish clickOutside: add the document listener in onSettled, and return its cleanup.
Loading editor…