question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Bug: componentDidMount fires before lazy-loaded components are mounted

See original GitHub issue

In a component with child components that are lazy loaded using React.lazy, the component’s componendDidMount() callback is fired before the lazy-loaded children are loaded/mounted. This makes it very difficult to perform logic on child components (eg. calling a component method via a ref, using ReactDOM.findDOMNode on the children, etc.) in componentDidMount. This all obviously works fine with components that are synchronously loaded but makes it very difficult to migrate an existing React application using synchronously loaded components to one that leverages React.lazy.

This behavior is observed with React version 16.12.

The issue can be illustrated with code like this:

import React, {Suspense} from 'react';
import ReactDOM from 'react-dom';

const SomeComponent = React.lazy(() => import("./SomeComponent"));

class Comp extends React.Component {
     constructor() {
           super();
           this.compRef = React.createRef();
     }

     componentDidMount() {
           this.compRef.current.blur();
     }

     render() {
         return 
             <Suspense fallback={<div>Loading...</div>} >
                  <SomeComponent ref={this.compRef} />
             </Suspense>
       }   
}

ReactDOM.render(<Comp/>, document.createElement("div"))

The current behavior

The current behavior is that the componentDidMount callback is fired before the lazy loaded component is rendered/mounted so using compRef fails.

The expected behavior

The componentDidMount method should only fire once all of the child components have loaded/mounted. Or, there should be some other way to consolidate code that performs operations on asynchronously loaded/rendered components.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
lxsmnsyccommented, Feb 27, 2020

Not really a bug but it is always implied that the suspended component will always fail first due to asynchronicity and so Suspense will always render the fallback, hence the render method is successful (since the pending/failing component is handled gracefully) then the componentDidMount is called. This is certainly the intended behavior.

0reactions
gaearoncommented, Mar 16, 2020

Yeah there’s not much we can do here. If we did like you described (e.g. wait for all child components) then this would cut out a huge amount of use cases — e.g. focusing a text field like

<>
  <input />
  <SomethingThatContainsLazyCodeDeepInside />
</>

and it would always be dangerous to add lazy deeply because that would break assumptions.

In general, findDOMNode is not a recommended pattern anyway — whether in componentDidMount or anywhere else. This is precisely because you won’t get notified about what arbitrary children do. You can use callback refs for this instead.

Read more comments on GitHub >

github_iconTop Results From Across the Web

reactjs - componentDidMount and React.lazy - Stack Overflow
It seems like componentDidMount should not be called until all of the rendered components are mounted. Am I missing something obvious?
Read more >
Component Lifecycle Methods - Stencil.js
When the component is first connected, this method is called before componentWillLoad . It's important to note that this method can be called...
Read more >
How to use componentWillUnmount in Functional ...
This post explains how functional components can carry out life-cycle events without ... Anything in here is fired on component mount.
Read more >
How to fetch data in React with performance in mind
It's the data we need to fetch before a component ends up on the screen. ... in useEffect (or in componentDidMount for class...
Read more >
useEffect(fn, []) is not the new componentDidMount()
Component renders for the first time. The return value of render() is used to mount new DOM. componentDidMount fires and sets state immediately ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found