Skip to content

andres-kovalev/react-click-outside-listener

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ci codecov downloads node npm MIT npm bundle size Conventional Commits

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>
    );
}

About

Wrapping component to register click outside

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published