Search code examples
reactjswebpackecmascript-6babeljsreact-dom

Why can I not get a reference to my component from the ReactDOM.render() callback?


I have the following code:

const priceCalculator = ReactDOM.render(<PriceCalculator />, reactHolder);

I need to use priceCalculator later in my code but ESLint is complaining that I shouldn't use the return value of ReactDOM.render(). That's when I discovered that you can pass a 3rd argument to ReactDOM.render() which is a callback. Great I thought...

ReactDOM.render(<PriceCalculator />, reactHolder, function(priceCalculator) {
    // do something with priceCalculator
});

But priceCalculator is undefined. In the debugger I pause on exceptions and find that this is set to my React Component when I'm inside this function. So I rewrite it...

ReactDOM.render(<PriceCalculator />, reactHolder, function() {
    const priceCalculator = this;
    // do something with priceCalculator
});

It's still undefined. What's going on?

I'm using Webpack to compile es6 React code (using babel).


Solution

  • In the ESLint docs page you linked it says to use a callback ref:

    ReactDOM.render() currently returns a reference to the root ReactComponent instance. However, using this return value is legacy and should be avoided because future versions of React may render components asynchronously in some cases. If you need a reference to the root ReactComponent instance, the preferred solution is to attach a callback ref to the root element.

    Source: React Top-Level API documentation

    So you can pass a callback to the root component via a prop and call this with reference to the component's root node from within its render method, via the root node's ref prop.

    For example (working fiddle):

    class Hello extends React.Component {
      render () {
        return (
          <div ref={(node) => this.props.cb(node)}>
            Hello {this.state.name}
          </div>
        )
      }
    }
    
    let node
    
    ReactDOM.render(<Hello cb={(n) => node = n} />, ...);
    
    console.log(node)
    

    Note: this approach is naive as ReactDOM.render may not always render synchronously, in which case the console.log statement would print "undefined". (See the above quote: "future versions of React may render components asynchronously in some cases".)