Flow: ReactDom.findDOMNode() returns wrong type

Created on 21 Mar 2017  路  2Comments  路  Source: facebook/flow

This started happening when updated from 0.38.0 to 0.42.0. repro-component:

// @flow

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

export default class Tabs extends React.PureComponent {
  static defaultProps = {
    animation: false
  };

  componentDidMount() {
    this.mangleDomNode();
  }

  componentDidUpdate() {
    this.mangleDomNode();
  }

  render() {
    return <div {...this.props}/>;
  }

  mangleDomNode = () => {
    const node = ReactDOM.findDOMNode(this);
    if (node) {
      const links = node.querySelectorAll('a');
      links.forEach(link => link.removeAttribute('href'));
    }

  }
}

the error:

frontend/components/Tabs/Tabs.js:26
 26:       const links = node.querySelectorAll('a');
                              ^^^^^^^^^^^^^^^^ property `querySelectorAll`. Property not found in
 26:       const links = node.querySelectorAll('a');
                         ^^^^ Text

findDOMNode should return dom node instead, not Text.

Most helpful comment

It's not necessarily wrong. Check out the definition:

https://github.com/facebook/flow/blob/e397ea15ed7d274995e40add132f799906d65b70/lib/react.js#L245

Depending on your selector, findDOMNode can turn up either null, a Text element, or an actual Element.

It has no way of knowing for sure what your selector points to, so it has to capture all possible scenarios. To get around this, you can perform something along these lines:

if (node && typeof node.querySelectorAll === 'function') {
  const links = node.querySelectorAll('a');
  links.forEach(link => link.removeAttribute('href'));
}

I believe you could also use instanceof:

if (node instanceof HTMLElement) {
  const links = node.querySelectorAll('a');
  links.forEach(link => link.removeAttribute('href'));
}

All 2 comments

It's not necessarily wrong. Check out the definition:

https://github.com/facebook/flow/blob/e397ea15ed7d274995e40add132f799906d65b70/lib/react.js#L245

Depending on your selector, findDOMNode can turn up either null, a Text element, or an actual Element.

It has no way of knowing for sure what your selector points to, so it has to capture all possible scenarios. To get around this, you can perform something along these lines:

if (node && typeof node.querySelectorAll === 'function') {
  const links = node.querySelectorAll('a');
  links.forEach(link => link.removeAttribute('href'));
}

I believe you could also use instanceof:

if (node instanceof HTMLElement) {
  const links = node.querySelectorAll('a');
  links.forEach(link => link.removeAttribute('href'));
}

The instanceof check is the recommended way to do this.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

l2silver picture l2silver  路  3Comments

mjj2000 picture mjj2000  路  3Comments

damncabbage picture damncabbage  路  3Comments

cubika picture cubika  路  3Comments

ctrlplusb picture ctrlplusb  路  3Comments