Coder Social home page Coder Social logo

Comments (9)

corysimmons avatar corysimmons commented on May 30, 2024 3

Not niche. I'm also trying to create multiple things where if you click outside any of them, they close.

Like... imagine an image gallery. Pretty big use case right there.

In my case it's swatches using react-color and trying to do something like

<Swatch ref={someRef} />
<Swatch ref={anotherRef} />

from use-onclickoutside.

garand avatar garand commented on May 30, 2024 2

I updated my listener method to this which allows me to pass a single ref or an array of refs.

const listener = (event: PossibleEvent) => {
    const shouldTrigger = !Array.of(refs)
      .flat()
      .some(
        (ref) => !ref.current || ref.current.contains(event.target as Node),
      )

    if (shouldTrigger && handler) handler(event)
}

from use-onclickoutside.

Andarist avatar Andarist commented on May 30, 2024

Could you describe an example use case in more detail?

from use-onclickoutside.

ryanscottaudio avatar ryanscottaudio commented on May 30, 2024

let's say we have two modals on the screen, both in portals, and we want to close both of them if the user clicks the space outside of either one of them

from use-onclickoutside.

Andarist avatar Andarist commented on May 30, 2024

But you never want to close any of them if a user clicks inside any of them? Do those modals stack/overlap?

from use-onclickoutside.

ryanscottaudio avatar ryanscottaudio commented on May 30, 2024

they wouldnt stack/overlap, no, but we'd want to detect a click outside of either one of them

from use-onclickoutside.

Andarist avatar Andarist commented on May 30, 2024

This seems like a rather less popular use case. I would recommend composing this in your codebase like this:

export default (refs, handler) => refs.forEach(ref => useOnClickOutside(ref, handler))

Or just establish parent-child relationship and set up a single useOnClickOutside hook in each modal with the same handler plugged to them. If 2 handlers will call smth like setShow(false) then React will just bailout from the second processing because your state will already be false, no harm done.

from use-onclickoutside.

ryanscottaudio avatar ryanscottaudio commented on May 30, 2024

export default (refs, handler) => refs.forEach(ref => useOnClickOutside(ref, handler)) won't work because if you click inside modal 2, you're also clicking outside modal 1, so the callback will trigger, when in reality you want it to trigger only when you click outside of either modal

from use-onclickoutside.

Andarist avatar Andarist commented on May 30, 2024

Right - no of the proposes approaches would work for you. I still believe that this use case is rather niche though and I wouldn't like overloading the implementation.

If we can come up with a neat implementation then I could consider merging it. At the moment there is no way to configure containment check, so maybe if we would make this configurable it would solve your issue as well? A very WIP implementation could look like this:

export default function useOnClickOutside(
  refOrContainsCheck: React.RefObject<HTMLElement> | (node: Node) => boolean,
  handler: Handler | null,
) {
  if (!isBrowser) {
    return
  }

  const latestRef = useLatest({
    handler,
    contains: typeof refOrContainsCheck === 'function'
      ? refOrContainsCheck
      : node => {
          const refNode = refOrContainsCheck.current
          return !!refNode && refNode.contains(node)
      }
  })

  useEffect(() => {
    if (!handler) {
      return
    }

    const listener = (event: PossibleEvent) => {
      const { contains, handler } = latestRef.current
      
      if (!handler || contains(event.target as Node)) {
        return
      }

      handler(event)
    }

    events.forEach(event => {
      document.addEventListener(event, listener, getOptions(event))
    })

    return () => {
      events.forEach(event => {
        document.removeEventListener(event, listener, getOptions(
          event,
        ) as EventListenerOptions)
      })
    }
  }, [!handler])
}

Then you could do smth like this:

useOnClickOutside(
  targetNode => refs.some(ref => ref.current.contains(targetNode),
  handler,
)

from use-onclickoutside.

Related Issues (14)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.