Skip to content

Instantly share code, notes, and snippets.

@cassler
Last active December 10, 2020 20:06
Show Gist options
  • Save cassler/81221279a49a23b4177af38ef45bb40a to your computer and use it in GitHub Desktop.
Save cassler/81221279a49a23b4177af38ef45bb40a to your computer and use it in GitHub Desktop.
useOnClickOutside hook
import React, { useRef } from 'react'
import useOnClickOutside from './useOnClickOutside'
export default function Component() {
// Set a ref for the handler to use
const ref = useRef(null)
// Handler for outside clicks here
const handleClickOutside = () => {
// Your custom logic here
console.log('clicked outside')
}
// Handler for inside clicks here
const handleClickInside = () => {
// Your custom logic here
console.log('clicked inside')
}
useOnClickOutside(ref, handleClickOutside)
return (
<div
ref={ref}
onClick={handleClickInside}
style={{ width: 200, height: 200, background: 'cyan' }}
/>
)
}
import { useEffect, RefObject } from 'react'
type Event = MouseEvent | TouchEvent
// This looks scary becasue of the type definitions
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
handler: (event: Event) => void,
) {
// Assign the event listener on our ref whenever ref/hanlder are set
useEffect(() => {
const listener = (event: Event) => {
const el = ref?.current
// Do nothing if clicking ref's element or descendent elements
if (!el || el.contains((event?.target as Node) || null)) {
return
}
handler(event)
}
document.addEventListener(`mousedown`, listener)
document.addEventListener(`touchstart`, listener)
return () => {
document.removeEventListener(`mousedown`, listener)
document.removeEventListener(`touchstart`, listener)
}
// Reload only if ref or handler changes
}, [ref, handler])
}
export default useOnClickOutside
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment