React: ReactDOM.findDOMNode returns `undefined` unexpectedly in `componentDidMount`

Created on 25 Jan 2017  路  2Comments  路  Source: facebook/react

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
I have two versions of a simple page. The page produces the text Hello World! and tries to register DOM event handlers for a few mouse events on the HTML element:

  • underline text on mouse over
  • remove underline on mouse out
  • make text red color on mouse down
  • return text to black color on mouse up

And, finally also sets the elements style.cursor to pointer. The only difference between the two pages are the way the DOM event handlers are registered on the object returned by ReactDOM.findDOMNode(...).

Bad Version

Link: https://jsfiddle.net/rationalrevolt/e2j1sasw/

This version binds event handlers like this on componentDidMount:
componentDidMount: function () { var _this = this; (ReactDOM.findDOMNode(_this.refs.comp)).onmouseover = function () { (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'underline'; }
The JSFiddle link for this version produces the below error:
Uncaught TypeError: Cannot read property 'style' of undefined at Object.componentDidMount ((index):91) at e.notifyAll (react-dom.min.js:12) at r.close (react-dom.min.js:14) at r.closeAll (react-dom.min.js:15) at r.perform (react-dom.min.js:15) at s (react-dom.min.js:14) at r.perform (react-dom.min.js:15) at Object.batchedUpdates (react-dom.min.js:14) at Object.i [as batchedUpdates] (react-dom.min.js:14) at Object._renderNewRootComponent (react-dom.min.js:14)

Good Version

Link: https://jsfiddle.net/rationalrevolt/zarwv09p/

This version binds event handlers like this on componentDidMount:
componentDidMount: function () { (ReactDOM.findDOMNode(this.refs.comp)).onmouseover = function () { (ReactDOM.findDOMNode(this.refs.comp)).style.textDecoration = 'underline'; }.bind(this);
This JSFiddle link for this version behaves as expected.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).
JSFiddle links to Bad and Good versions are provided.

What is the expected behavior?
In my opinion, both the good version and bad version should behave in React 15.4 in the exact same way. Why does the bad version produce an error in React 15.4?

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Tested in Chrome (OS X and Windows), Safari (OS X).
Bad version does not work with React 15.4
Bad version worked with React 0.13.12

Most helpful comment

Hey @viswans83! Thanks for the issue. The reason this is happening is actually really interesting and not at all related to React. The issue is that you're not using semicolons in a scenario where automatic semicolon insertion (ASI) does not work.

What that means is, you expect your code to be parsed like:

// First expression, assigning  function to node.mouseover
(ReactDOM.findDOMNode(_this.refs.comp)).onmouseover = function () {
      (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'underline';
 }
// Second expression, assigning function to node.onmouseout
(ReactDOM.findDOMNode(_this.refs.comp)).onmouseout = function () {
   (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'none';
}

But what is actually parsing is an immediately invoked function

// First expression, assigning function to node.mouseover
(ReactDOM.findDOMNode(_this.refs.comp)).onmouseover = function () {
      (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'underline';
 }(ReactDOM.findDOMNode(_this.refs.comp)).onmouseout = function () {
// ^ This paren is being treated as if it was *calling* the function previously defined
   (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'none';
}

So to minimize that example, you're expecting:

(foo.bar) = function() {
};
(foo.baz) = function() {
};

But due to the paren and the lack of semicolon, it's being parsed as

(foo.bar) = function() {
}(foo.baz) = function() {}

To fix this you can either remove the extra parens around ReactDOM.findDOMNode or add a semicolon after the function body. Here's an example that does both and you'll see that the issue is resolved!

Hope that helps!

All 2 comments

Hey @viswans83! Thanks for the issue. The reason this is happening is actually really interesting and not at all related to React. The issue is that you're not using semicolons in a scenario where automatic semicolon insertion (ASI) does not work.

What that means is, you expect your code to be parsed like:

// First expression, assigning  function to node.mouseover
(ReactDOM.findDOMNode(_this.refs.comp)).onmouseover = function () {
      (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'underline';
 }
// Second expression, assigning function to node.onmouseout
(ReactDOM.findDOMNode(_this.refs.comp)).onmouseout = function () {
   (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'none';
}

But what is actually parsing is an immediately invoked function

// First expression, assigning function to node.mouseover
(ReactDOM.findDOMNode(_this.refs.comp)).onmouseover = function () {
      (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'underline';
 }(ReactDOM.findDOMNode(_this.refs.comp)).onmouseout = function () {
// ^ This paren is being treated as if it was *calling* the function previously defined
   (ReactDOM.findDOMNode(_this.refs.comp)).style.textDecoration = 'none';
}

So to minimize that example, you're expecting:

(foo.bar) = function() {
};
(foo.baz) = function() {
};

But due to the paren and the lack of semicolon, it's being parsed as

(foo.bar) = function() {
}(foo.baz) = function() {}

To fix this you can either remove the extra parens around ReactDOM.findDOMNode or add a semicolon after the function body. Here's an example that does both and you'll see that the issue is resolved!

Hope that helps!

Wow, thanks for the nice explanation @aweary!

Was this page helpful?
0 / 5 - 0 ratings