Component Lifecycle Methods

Components have numerous lifecycle methods which can be used to know when the component "will" and "did" load, update, and unload. These methods can be added to a component to hook into operations at the right time.

Implement one of the following methods within a component class and Stencil will automatically call them in the right order:

Component initialized componentDidLoad() componentDidUpdate() componentWillUpdate() componentDidUnload() @Watch(‘propName’) Change in a value of prop or state triggers re-render Component removed render() componentWillLoad()

Rendering State

It's always recommended to make any rendered state updates within componentWillLoad() or componentWillUpdate(), since these are the methods which get called before the render() method. Alternatively, updating rendered state with the componentDidLoad() or componentDidUpdate() methods will cause another re-render, which isn't ideal for performance.

If state must be updated in componentDidUpdate(), it has the potential of getting components stuck in an infinite loop. If updating state within componentDidUpdate() is unavoidable, then the method should also come with a way to detect if the props or state is "dirty" or not (is the data actually different or is it the same as before). By doing a dirty check, componentDidUpdate() is able to avoid rendering the same data, and which in turn calls componentDidUpdate() again.

Lifecycle Hierarchy

A useful feature of lifecycle methods is that they take their child component's lifecycle into consideration too. For example, if the parent component, cmp-a, has a child component, cmp-b, then cmp-a isn't considered "loaded" until cmp-b has finished loading. Another way to put it is that the deepest components finish loading first, then the componentDidLoad() calls bubble up.

It's also important to note that even though Stencil can lazy-load components, and has asynchronous rendering, the lifecycle methods are still called in the correct order. So while the top-level component could have already been loaded, all of its lifecycle methods are still called in the correct order, which means it'll wait for a child components to finish loading. The same goes for the exact opposite, where the child components may already be ready while the parent isn't.

In the example below we have a simple hierarchy of components. The numbered list shows the order of which the lifecycle methods will fire.

  <cmp-a>
    <cmp-b>
      <cmp-c></cmp-c>
    </cmp-b>
  </cmp-a>
  1. cmp-a - componentWillLoad()
  2. cmp-b - componentWillLoad()
  3. cmp-c - componentWillLoad()
  4. cmp-c - componentDidLoad()
  5. cmp-b - componentDidLoad()
  6. cmp-a - componentDidLoad()

Even if some components may or may not be already loaded, the entire component hierarchy waits on its child components to finish loading and rendering.

Async Lifecycle Methods

Lifecycle methods can also return promises which allows the method to asynchronously retrieve data or perform any async tasks. A great example of this is fetching data to be rendered in a component. For example, this very site you're reading first fetches content data before rendering. But because fetch() is async, it's important that componentWillLoad() returns a Promise to ensure its parent component isn't considered "loaded" until all of its content has rendered.

Below is a quick example showing how componentWillLoad() is able to have its parent component wait on it to finish loading its data.

componentWillLoad() {
  return fetch('/some-data.json')
    .then(response => response.json())
    .then(data => {
      this.content = data;
    });
}

Example

This simple example shows a clock and updates the current time every second. Since componentDidLoad is only called once, we will only ever have one instance of the timer running. Once the component unloads, the timer is stopped.

import { Component, State } from '@stencil/core';

@Component({
  tag: 'custom-clock'
})
export class CustomClock {

  timer: number;

  @State() time: number = Date.now();

  componentDidLoad() {
    this.timer = window.setInterval(() => {
      this.time = Date.now();
    }, 1000);
  }

  componentDidUnload() {
    window.clearInterval(this.timer);
  }

  render() {
    const time = new Date(this.time).toLocaleTimeString();

    return (
      <span>{ time }</span>
    );
  }
}

Here is the example running. If you want to see it in action then just inspect it with dev tools. 4:50:35 PM

Back Next
Contributors