React Component Lifetimes Made Simple

Steven Bradley
3 min readAug 3, 2018

--

When I first started learning React.js, the thing that confused me more than anything else about it was the lifetime of any given component: when does a component get created by React, when does it replace an existing one, and how can I know what “version” of a component is running at any given time?

It’s actually simple, but it’s difficult to describe in words. It’s also difficult to conceptualize with images, and I think these two reasons are why it didn’t click for me for a while. So I think it might be easier if we use pseudocode!

When you first render a component, React takes your component, and calls render on it. For functions, it just means returning the function. For classes, it means instantiating a temporary instance of it, and calling render:

function render(component) {
let view;
if (typeof component === 'function') {
view = component();
}
else {
const instance = new component();
view = instance.render();
}
// recursively render(view.children)
}

Since our views are hierarchical, React renders them recursively. Showing the recursion doesn’t add much here, but the important thing to note here is that every single class-based component in this render tree, no matter how deep in the tree, just got instantiated, creating a temporary new instance.

The first time you ever render to the DOM, these temporary instances have become the official instances, and React holds them in memory for you. Congratulations, your instances are here to stay! Well, for a while.

Any time a class-based component has setState called, React starts the process of Reconciliation. You can learn about the reconciliation algorithm in depth, but for this article, all you need to know is that it needs to check if anything changed in itself or its children. But how can it know? It has to re-render!

class Component {  // ...  __stateChanged() {
const a = this.existingView();
const b = render(this);
// ... diff ...
}
}

So now b holds the contents of an entirely new render tree, including brand new instances of all class-based components at this level in the tree and down!

That means if you have a class like this:

class MyComponent {  constructor() {
this.foo = GetSomeData(); // bad!
}
}

then React is going to call new MyComponent() then call myComponent.render() and possibly throw away the instance when it’s done, if nothing’s changed.

In this case, it means you may see redundant calls to GetSomeData! This is why you should put stuff like this in componentDidMount, because that’s the point when you know for sure that your component is “official”.

If anything results in your component getting new props, it will still keep the old component instance around, and just update its props with the new props:

class Component {  __maybeUpdate(temporaryInstance) {
if (differentProps(this.props, temporaryInstance.props)) {
const prevProps = this.props;
this.props = temporaryInstance.props;
this.componentDidUpdate(prevProps);
}
}
}

Now React has kept your “official” instance of your class-based component, and updated it with new state and new props! The old instance gets thrown away once again, since it was only used for the reconciliation process.

The only time that a component becomes “unofficial” is when it unmounts. And the only time it unmounts is when there’s no longer anything like it in that spot in the view tree after it renders again. This is part of Reconciliation, so refer to the aforementioned document for details.

So to summarize:

  1. React components get instantiated all the time for diffs and checks, so don’t rely on your constructors for anything serious or permanent.
  2. As long as it has mounted, it is the “official” version and remains in memory, starting with componentDidMount.
  3. The new versions of props are extracted from temporary instances of the class-based component, and overwrite the props of the “official” instance.
  4. The only time a component stops being “official” is when you stop seeing it on the screen, at which point componentWillUnmount is called.

I hope this helps to de-mystify React class-based component lifetimes.

--

--

Steven Bradley

I like making apps that get people excited about creating things, and that make it easy and fun for them to do! Looking for some freelance projects :)