Search code examples
reactjsreact-toolbox

Update to React 16 findDOMNode stops working


I just tried to update React to version 16. Unfortunately React Toolbox which I use as UI library is not yet adapted for that.

I took on the quest but stumbled upon something where I could not find a solution

React Toolbox uses React.finDOMNode to do some positioning calculations. After upgrading to React 16 findDomNode is now always returning null and React Toolbox stops working properly.

I tried to isolate the case but I failed. In Isolation React.findDOMNode always returns the correct node.

Isolation code:

import PropTypes from 'prop-types';
import React, {Component} from 'React';
import ReactDOM from 'react-dom';
//import get from 'lodash/get';
//import classNames from 'classnames';
//import css from './css.css';

const yeah = () => {
    class YoloComp extends Component {
        render = () => (<div {...this.props} >YOLO</div>)
    }

    return YoloComp;
};

let Yeah = yeah();

export default class Test extends Component {
    static propTypes = {};

    click = () => {
        this.foo();
    };

    foo = () => {
        console.log('ref', this.node);
        console.log('dom this', ReactDOM.findDOMNode(this));
        console.log('dom node', ReactDOM.findDOMNode(this.node));
    };

    componentDidMount() {
        setTimeout(() => {
            this.foo();
        });
    }

    render() {
        return (

            <Yeah ref={(r) => this.node = r} onClick={this.click}/>
        );
    }
}

While in React Toolbox for example in the Ripple Component on line 88 it always returns null.

getDescriptor(x, y) {
        const { left, top, height, width } = ReactDOM.findDOMNode(this).getBoundingClientRect();
        const { rippleCentered: centered, rippleSpread: spread } = this.props;
        return {
          left: centered ? 0 : x - left - (width / 2),
          top: centered ? 0 : y - top - (height / 2),
          width: width * spread,
        };
      }

In most cases I was able to replace findDOMNode with refs but not in that case (A ref in Ripple would reference a React component not an Element) resolving that with React.finDOMNode also returns null.

  1. Has anything changed in React.findDOMNode so that it is not working anymore like in the past?
  2. How could I take that change into account and make React TOolbox compatible with React 16.
  3. Do you have any ideas of fixing this?

Best regards Tobias


Solution

  • I ran into the same issue. I'm not familiar enough with React DOM internals to know exactly what changed and if this is just user error on our part, but with a little inspecting I came up with this function that returns the correct DOM node. NB: I'm using private variables here, so use at your own risk.

    function dangerouslyFindDOMNode(_reactElement){
      try {
        console.warn("'dangerouslyFindDOMNode' is liable to break, and often")
        let fiberNode = _reactElement._reactInternalFiber
        while (fiberNode && !(fiberNode.stateNode instanceof Element)) {
          fiberNode = fiberNode.child
        }
        return fiberNode ? fiberNode.stateNode : null
      } catch(e){
        console.error(e)
        return null
      }
    }
    

    The code is pretty straightforward; you descend the Fiber node tree until the stateNode is a DOM element or you run out of children.