Flow: How to refine EventTarget to Node?

Created on 4 Sep 2017  路  3Comments  路  Source: facebook/flow

Is there any way to refine an EventTarget to a Node?
Some sample code of this requirement:

import * as React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component<{}> {
  handleMouseDown = (e: MouseEvent) => {
    const domNode = ReactDOM.findDOMNode(this);
    if (!domNode || !domNode.contains(e.currentTarget)) {
      // ...
    }
  };
}

This produces this error as of 0.54:

7:     if (!domNode || !domNode.contains(e.currentTarget)) {
                                         ^ EventTarget. This type is incompatible with the expected param type of
[LIB] static/v0.54.0/flowlib/dom.js:441:   contains(other: ?Node): boolean;
                                                            ^ Node

https://flow.org/try/#0JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcA5FDvmQNwBQoksmVMAIgPICycRJ5lPDAC0AExK06dXABs06LgE8AwiUgA7LOvhYAHjC2j02IQDpV4CJu0AeAN4BfAHxw7dOHAAWydaJlYuCABXVCw2CAB3dTgAXjgACiwALjhAkKwAUQA3LRgASliXNw8PXCtUeHEQADkIUSxYliFOLlMCYF8W2vr4mE9gVDz6ErhgAgSAQiruhoAfWbgpkhnTMu1kDtRE1aCoSm0AFWQoAHMsfILikbgAehu4U0f3Eodnh3pXoA

Most helpful comment

You can either check that e.currentTarget is an instanceof Node

class MyComponent extends React.Component<{}> {
  handleMouseDown = (e: MouseEvent) => {
    const el = e.currentTarget;
    const domNode = ReactDOM.findDOMNode(this);
    if (!(domNode && el instanceof Node && domNode.contains(el))) {
      // ...
    }
  };
}

Or you can use the classic any escape hatch (not recommended):

class MyComponent extends React.Component<{}> {
  handleMouseDown = (e: MouseEvent) => {
    const domNode = ReactDOM.findDOMNode(this);
    if (!domNode || !domNode.contains((e.currentTarget: any))) {
      // ...
    }
  };
}

All 3 comments

You can either check that e.currentTarget is an instanceof Node

class MyComponent extends React.Component<{}> {
  handleMouseDown = (e: MouseEvent) => {
    const el = e.currentTarget;
    const domNode = ReactDOM.findDOMNode(this);
    if (!(domNode && el instanceof Node && domNode.contains(el))) {
      // ...
    }
  };
}

Or you can use the classic any escape hatch (not recommended):

class MyComponent extends React.Component<{}> {
  handleMouseDown = (e: MouseEvent) => {
    const domNode = ReactDOM.findDOMNode(this);
    if (!domNode || !domNode.contains((e.currentTarget: any))) {
      // ...
    }
  };
}

Great! instanceof Node did the trick! Thank you :)

Here's hooks version I wrote, maybe someone will find it of use :)

```import { RefObject, useEffect } from 'react';

/**

  • Function adding/removing event listeners that execute an action depending on a state flag, if clicked outside of target element.
    *
  • @param {RefObject} ref - target element
  • @param {boolean} state - state flag (ex. isOpen)
  • @param {() => void} action - action to execute (ex. closeMenu)
    *
    */

const useOutsideClick = (ref: RefObject, state: boolean, action: () => void) => {

const eventAction = (ev: Event) => {
if (ref.current && ev.target instanceof Node && !ref.current.contains(ev.target)) {
ev.preventDefault();
action();
removeGlobalListeners();
}
};

const addGlobalListeners = () => {
document.addEventListener('click', eventAction, false);
document.addEventListener('touchend', eventAction, false);
window.addEventListener('popstate', eventAction, false);
};

const removeGlobalListeners = () => {
document.removeEventListener('click', eventAction, false);
document.removeEventListener('touchend', eventAction, false);
window.removeEventListener('popstate', eventAction, false);
};

useEffect(() => {
if (state) {
addGlobalListeners();
}

return function cleanup() {
  removeGlobalListeners();
};

});
};

export default useOutsideClick;
```

Was this page helpful?
0 / 5 - 0 ratings