MCPcopy
hub / github.com/Pomax/react-onclickoutside

github.com/Pomax/react-onclickoutside @v6.13.2 sqlite

repository ↗ · DeepWiki ↗ · release v6.13.2 ↗
79 symbols 121 edges 11 files 6 documented · 8%
README

npm version Build Status npm

:warning: Open source is free, but developer time isn't :warning:

This package needs your support to stay maintained. If you work for an organization whose website is better off using react-onclickoutside than rolling its own code solution, please consider talking to your manager to help fund this project. Open Source is free to use, but certainly not free to develop. If you have the means to reward those whose work you rely on, please consider doing so.

An onClickOutside wrapper for React components

This is a React Higher Order Component (HOC) that you can use with your own React components if you want to have them listen for clicks that occur somewhere in the document, outside of the element itself (for instance, if you need to hide a menu when people click anywhere else on your page).

Note that this HOC relies on the .classList property, which is supported by all modern browsers, but not by deprecated and obsolete browsers like IE (noting that Microsoft Edge is not Microsoft Internet Explorer. Edge does not have any problems with the classList property for SVG elements). If your code relies on classList in any way, you want to use a polyfill like dom4.

This HOC supports stateless components as of v5.7.0, and switched to using transpiled es6 classes rather than createClass as of v6.

Sections covered in this README

Installation

Only install this HoC if you're still extending the Component class, something which the React documentation doesn't even cover anymore because they went all-in on functional components with hooks.

If you're using hooks, which React says you should be, don't install this HoC and instead read this section, below.

If you're still stuck with Component classes, then you can install this HoC using npm:

$> npm install react-onclickoutside --save

(or --save-dev depending on your needs). You then use it in your components as:

Usage

Functional Component with UseState Hook

This HoC does not support functional components, as it relies on class properties and component instances. However, you almost certainly don't need this HoC in modern (React 16+) functional component code, as a simple function will do the trick just fine. E.g.:

function listenForOutsideClicks(listening, setListening, menuRef, setIsOpen) {
  return () => {
    if (listening) return;
    if (!menuRef.current) return;
    setListening(true);
    [`click`, `touchstart`].forEach((type) => {
      document.addEventListener(`click`, (evt) => {
        if (menuRef.current.contains(evt.target)) return;
        setIsOpen(false);
      });
    });
  }
}

Used in a functional component as:

import React, { useEffect, useState, useRef } from "react";
import listenForOutsideClicks from "./somewhere";

const Menu = () => {
  const menuRef = useRef(null);
  const [listening, setListening] = useState(false);
  const [isOpen, setIsOpen] = useState(false);  
  const toggle = () => setIsOpen(!isOpen);

  useEffect(listenForOutsideClick(
    listening,
    setListening,
    menuRef,
    setIsOpen,
  ));

  return (



      <h1 onClick={toggle}>...</h1>
      <ul>...</ul>



  );
};

export default Menu;

Example: https://codesandbox.io/s/trusting-dubinsky-k3mve

ES6 Class Component

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  handleClickOutside = evt => {
    // ..handling code goes here...
  };
}

export default onClickOutside(MyComponent);

CommonJS Require

// .default is needed because library is bundled as ES6 module
var onClickOutside = require("react-onclickoutside").default;
var createReactClass = require("create-react-class");

// create a new component, wrapped by this onclickoutside HOC:
var MyComponent = onClickOutside(
  createReactClass({
    // ...,
    handleClickOutside: function(evt) {
      // ...handling code goes here...
    }
    // ...
  })
);

Ensuring there is a click handler

Note that if you try to wrap a React component class without a handleClickOutside(evt) handler like this, the HOC will throw an error. In order to use a custom event handler, you can specify the function to be used by the HOC as second parameter (this can be useful in environments like TypeScript, where the fact that the wrapped component does not implement the handler can be flagged at compile-time):

// load the HOC:
import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

// create a new component, wrapped below by onClickOutside HOC:
class MyComponent extends Component {
  // ...
  myClickOutsideHandler(evt) {
    // ...handling code goes here...
  }
  // ...
}
var clickOutsideConfig = {
  handleClickOutside: function(instance) {
    return instance.myClickOutsideHandler;
  }
};
var EnhancedComponent = onClickOutside(MyComponent, clickOutsideConfig);

Note that if you try to wrap a React component with a custom handler that the component does not implement, the HOC will throw an error at run-time.

Regulate which events to listen for

By default, "outside clicks" are based on both mousedown and touchstart events; if that is what you need, then you do not need to specify anything special. However, if you need different events, you can specify these using the eventTypes property. If you just need one event, you can pass in the event name as plain string:

<MyComponent eventTypes="click" ... />

For multiple events, you can pass in the array of event names you need to listen for:

<MyComponent eventTypes={["click", "touchend"]} ... />

Regulate whether or not to listen for outside clicks

Wrapped components have two functions that can be used to explicitly listen for, or do nothing with, outside clicks

  • enableOnClickOutside() - Enables outside click listening by setting up the event listening bindings.
  • disableOnClickOutside() - Disables outside click listening by explicitly removing the event listening bindings.

In addition, you can create a component that uses this HOC such that it has the code set up and ready to go, but not listening for outside click events until you explicitly issue its enableOnClickOutside(), by passing in a properly called disableOnClickOutside:

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  // ...
  handleClickOutside(evt) {
    // ...
  }
  // ...
}
var EnhancedComponent = onClickOutside(MyComponent);

class Container extends Component {
  render(evt) {
    return <EnhancedComponent disableOnClickOutside={true} />;
  }
}

Using disableOnClickOutside() or enableOnClickOutside() within componentDidMount or componentWillMount is considered an anti-pattern, and does not have consistent behaviour when using the mixin and HOC/ES7 Decorator. Favour setting the disableOnClickOutside property on the component.

Regulate whether or not to listen to scrollbar clicks

By default this HOC will listen for "clicks inside the document", which may include clicks that occur on the scrollbar. Quite often clicking on the scrollbar should close whatever is open but in case your project invalidates that assumption you can use the excludeScrollbar property to explicitly tell the HOC that clicks on the scrollbar should be ignored:

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  // ...
}
var EnhancedComponent = onClickOutside(MyComponent);

class Container extends Component {
  render(evt) {
    return <EnhancedComponent excludeScrollbar={true} />;
  }
}

Alternatively, you can specify this behavior as default for all instances of your component passing a configuration object as second parameter:

import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";

class MyComponent extends Component {
  // ...
}
var clickOutsideConfig = {
  excludeScrollbar: true
};
var EnhancedComponent = onClickOutside(MyComponent, clickOutsideConfig);

Regulating evt.preventDefault() and evt.stopPropagation()

Technically this HOC lets you pass in preventDefault={true/false} and stopPropagation={true/false} to regulate what happens to the event when it hits your handleClickOutside(evt) function, but beware: stopPropagation may not do what you expect it to do.

Each component adds new event listeners to the document, which may or may not cause as many event triggers as there are event listening bindings. In the test file found in ./test/browser/index.html, the coded uses stopPropagation={true} but sibling events still make it to "parents".

Marking elements as "skip over this one" during the event loop

If you want the HOC to ignore certain elements, you can tell the HOC which CSS class name it should use for this purposes. If you want explicit control over the class name, use outsideClickIgnoreClass={some string} as component property, or if you don't, the default string used is ignore-react-onclickoutside.

Older React code: "What happened to the Mixin??"

Due to ES2015/ES6 class syntax making mixins essentially impossible, and the fact that HOC wrapping works perfectly fine in ES5 and older versions of React, as of this package's version 5.0.0 no Mixin is offered anymore.

If you absolutely need a mixin... you really don't.

But how can I access my component? It has an API that I rely on!

No, I get that. I constantly have that problem myself, so while there is no universal agreement on how to do that, this HOC offers a getInstance() function that you can call for a reference to the component you wrapped, so that you can call its API without headaches:

import React, { Component } from 'react'
import onClickOutside from 'react-onclickoutside'

class MyComponent extends Component {
  // ...
  handleClickOutside(evt) {
    // ...
  }
  ...
}
var EnhancedComponent = onClickOutside(MyComponent);

class Container extends Component {
  constructor(props) {
    super(props);
    this.getMyComponentRef = this.getMyComponentRef.bind(this);
  }

  someFunction() {
    var ref = this.myComponentRef;
    // 1) Get the wrapped component instance:
    var superTrueMyComponent = ref.getInstance();
    // and call instance functions defined for it:
    superTrueMyComponent.customFunction();
  }

  getMyComponentRef(ref) {
    this.myComponentRef = ref;
  }

  render(evt) {
    return <EnhancedComponent disableOnClickOutside={true} ref={this.getMyComponentRef}/>
  }
}

Note that there is also a getClass() function, to get the original Class that was passed into the HOC wrapper, but if you find yourself needing this you're probably doing something wrong: you really want to define your classes as real, require'able etc. units, and then write wrapped components separately, so that you can always access the original class's statics etc. properties without needing to extract them out of a HOC.

Which version do I need for which version of React?

If you use React 0.12 or 0.13, version 2.4 and below will work.

If you use React 0.14, use v2.5 through v4.9, as these specifically use react-DOM for the necessary DOM event bindings.

If you use React 15, you can use v4.x, which offers both a mixin and HOC, or use v5.x, which is HOC-only.

If you use React 15.5, you can use v5.11.x, which relies on createClass as supplied by create-react-class rather than React.createClass.

If you use React 16 or 15.5 in preparation of 16, use v6.x, which uses pure class notation.

Support-wise, only the latest version will receive updates and bug fixes.

I do not believe in perpetual support for outd

Core symbols most depended-on inside this repo

getInstance
called by 12
src/index.js
rerender
called by 5
test/test.js
mergeAll
called by 4
rollup.config.js
handleClickOutside
called by 3
test/test.js
toggleEnableClickOutside
called by 2
test/test.js
render
called by 2
test/test.js
setClickOutsideRef
called by 2
test/test.js
hide
called by 2
test/browser/test3.js

Shape

Method 39
Class 24
Function 16

Languages

TypeScript100%

Modules by API surface

test/test.js25 symbols
test/browser/test3.js14 symbols
test/browser/test2.js11 symbols
src/index.js10 symbols
test/browser/test1.js7 symbols
test/no-dom-test.js4 symbols
src/dom-helpers.js3 symbols
src/detect-passive-events.js3 symbols
src/uid.js1 symbols
rollup.config.js1 symbols

Dependencies from manifests, versioned

@babel/core7.14.2 · 1×
@babel/plugin-proposal-class-properties7.13.0 · 1×
@babel/plugin-proposal-object-rest-spread7.14.2 · 1×
@babel/preset-env7.14.2 · 1×
@babel/preset-stage-07.8.3 · 1×
@rollup/plugin-babel5.3.0 · 1×
babel-eslint8.0.2 · 1×
babel-loader8.2.2 · 1×
chai4.1.2 · 1×
eslint4.12.0 · 1×
husky0.14.3 · 1×
lint-staged5.0.0 · 1×

For agents

$ claude mcp add react-onclickoutside \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact