react-click-outside-listener
v3.0.0
Published
Wrapping component to register click outside
Downloads
158
Maintainers
Readme
react-click-outside-listener
Wrapping component to register click outside.
Demo
Here is a small playground with couple of examples.
Installation
As any other npm package react-click-outside-listener
can be added to your project by following command:
npm i -S react-click-outside-listener
It requires any version of react with new context API support as peer dependency, so it should be installed as well.
npm i -S react
Quick start
Nothing is easier than use ClickOutsideListener
component - just wrap your content with it:
import { ClickOutsideListener } from 'react-click-outside-listener';
const Parent = () => {
const handleClickOutside = ...;
return (
<ClickOutsideListener onClickOutside={ handleClickOutside }>
<div>Just put your content inside</div>
<div>You can put several elements, if you need</div>
<div>ClickOutsideListener component will call listener only if none of those clicked</div>
<div>
<div>Of course we can nest items</div>
</div>
</ClickOutsideListener>
);
}
It is only possible to track clicks on html elements, so children should be html elements or React components forwarding refs:
const Child = React.forwardRef((props, ref) => (
<div ref={ ref }>
We should attach ref to html container
</div>
));
const Parent = () => {
...
return (
<ClickOutsideListener onClickOutside={ handleClickOutside }>
<Child />
</ClickOutsideListener>
);
}
If you need to support custom properties for refs or want to track selected items, use render prop as a child:
const Parent = () => {
return (
<ClickOutsideListener onClickOutside={ handleClickOutside }>
{refs => (
<div>
Click here to invoke callback
<div ref={ refs(0) }>
No callback when click here
</div>
<div>Beware callbacks</div>
<div ref={ refs(1) }>
Another safe zone
</div>
</div>
)}
</ClickOutsideListener>
);
}
renderProp argument can be also used as regular ref:
const Component = () => {
return (
<ClickOutsideListener onClickOutside={ handleClickOutside }>
{ref => (
<div>
<div ref={ ref }>
No callback when click here
</div>
</div>
)}
</ClickOutsideListener>
);
}
Let's consider small example with dropdown menu:
const Menu = ({ label, items }) => {
const [ isShown, setIsShown ] = useState(false);
const toggleMenu = useCallback(
() => setIsShown(isShown => !isShown),
[]
);
const handleClickOutside = useCallback(
() => setIsShown(false),
[]
);
return (
<ul className="menu">
<ClickOutsideListener onClickOutside={ handleClickOutside }>
{refs => (
<React.Fragment>
<button
className="menu__trigger"
onClick={ toggleMenu }
ref={ refs(0) }
>
{ label }
</button>
<Dropdown
items={ items }
wrapperRef={ refs(1) }
/>
</React.Fragment>
)}
</ClickOutsideListener>
</ul>
);
}
...
const Dropdown = ({ items, wrapperRef }) => (
<ul
className='dropdown'
ref={ wrapperRef }
>
{ items.map(
({ id, label, action }) => (
<li
key={ id || label }
className="dropdown__item"
onClick={ action }
>
{ label }
</li>
)
) }
</ul>
);
Hook
react-click-outside-listener
package also exposes hook to achieve the same functionality. It returns refs
item similar to ClickOutsideListener
renderProp argument. It cab be used to set several refs or only one:
import { useClickOutsideListener } from 'react-click-outside-listener';
const Component = () => {
const refs = useClickOutsideListener(
() => { ... }
);
// several refs
return (
<div>
Click here to invoke callback
<div ref={ refs(0) }>
No callback when click here
</div>
<div>Beware callbacks</div>
<div ref={ refs(1) }>
Another safe zone
</div>
</div>
);
}
// or
const Component = () => {
const ref = useClickOutsideListener(
() => { ... }
);
// one ref
return (
<div>
<div ref={ ref }>
No callback when click here
</div>
</div>
);
}