Are connectedCallback calls executed in a deterministic order?

783 views Asked by At

If I have 2 Lit elements, with a parent-child relationship, e.g.:

<x-parent>
    <x-child></x-child>
</x-parent>

Are the connectedCallback calls executed in a deterministic order?

In my tests looks like the connectedCallback for the parent is always called first, but I don't know if I can rely on this being always the case.


UPDATE AFTER @justin-fagnani answer

@justin-fagnani Not sure if we are talking about the same, you wrote about when the component is defined, but my question is about the order of the connectedCallback. Anyways, based on your answer, I think that the child callback happens first if it is rendered using a <slot>:

render() {
  return html`<slot></slot>`;    
}

lit playground example 1,

But parent callback happens first if the child is rendered with a lit-html template:

render() {    
  return html`<x-child></x-child>`;
}

lit playground example 2

Is my assumption correct?

2

There are 2 answers

2
Akshay Rajput On

connectedCallback: Is Invoked each time the custom element is appended into a document-connected element. This will happen each time the node is moved, and may happen before the element's contents have been fully parsed.

This is equivalent to ngOnInit (Angular) and componentDidMount (react) lifecycles,

So yes parent method will be invoked first.

Ref: Link to mdn docs

1
Justin Fagnani On

The exact order depends on a few things around element definitions and upgrades.

<x-parent>
    <x-child></x-child>
</x-parent>

For this snippet, if both <x-parent> and <x-child> are defined before the HTML is created, then yes, the order will be [<x-parent>, <x-child>].

If the HTML is created first, then the order will be the order in which the components are defined.

First, let's assume that the component definitions are in separate modules and don't import each other.

Here the parent will be defined first:

<x-parent>
  <x-child></x-child>
</x-parent>
<script type=module>
  import './parent.js';
  import './child.js';
</script>

Here the child will be defined first:

<x-parent>
  <x-child></x-child>
</x-parent>
<script type=module>
  import './child.js';
  import './parent.js';
</script>

If one of the component definitions imports the other, then the depth-first first imported component will go first:

Here the child will be defined first:

parent.js:

import './child.js';

HTML:

<x-parent>
  <x-child></x-child>
</x-parent>
<script type=module>
  import './parent.js';
</script>

So if you want a guarantee on which will be defined first in all cases, have the every component import the ones that need to be defined first.