salesforce / lwc

⚡️ LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation
https://lwc.dev
Other
1.59k stars 386 forks source link

`render()` method to be called before `connectedCallback()` #928

Open caridy opened 5 years ago

caridy commented 5 years ago

Current state of the art in LWC

As today, the order of those hooks is as following:

  1. constructor() invocation

  2. connectedCallback() host is now in the DOM (no children available yet)

  3. render() method invoked to collect which template to be rehydrated

  4. renderedCallback() notifies that the template returned by render was rehydrated

  5. something changes in the state

  6. render() method invoked to collect which template to be rehydrated

  7. renderedCallback() notifies that the template returned by render was rehydrated ... ... ...

  8. disconnectedCallback() host is not longer in the DOM

It is important to notice that the web components semantics dictates that connectedCallback must be call first on the parent, then on the children, likewise, disconnectedCallback follows the same ordering. In the case of our proprietary hook (renderedCallback), we do it backward, and children are fully rendered before the parent finishes its rendering process.

The Problem

There are actually few problems:

Example of the wrong assumptions today

export default class Foo extends LightningElement {
     @track normalizedItems;
     @api items = [];
     connectedCallback() {
           // oh, I can normalize items here so I don’t need a getter/setter for that because I’m lazy
           this.normalizedItems = this.items.map(…);
     }
}

then in the parent, I have a button that when pressed, adds a new item to the items list passed down, what are the two problems?

  1. pushing new items into the items in the parent will not rehydrate the parent neither the child, because it is not used during the reactive cycle since connectedCallback is not considered a reactive phase.
  2. changing items will have no effect on the re-normalization of items.

This is a common misconception about connectedCallback.

A Solution

One solution that I was able to test and validate is the inversion of the connectedCallback and the first render() invocation as follow:

  1. constructor() invocation

  2. render() method invoked to collect which template to be rehydrated <---- changed

  3. connectedCallback() host is now in the DOM (all children are now available) <---- changed

  4. renderedCallback() notifies that the template returned by render was rehydrated

  5. something changes in the state

  6. render() method invoked to collect which template to be rehydrated

  7. renderedCallback() notifies that the template returned by render was rehydrated ... ... ...

  8. disconnectedCallback() host is not longer in the DOM

This change is "almost" non-observable for existing code, and I say almost because of the following:

This should not be a deal breaker though.

Guidelines (that appeared to be missing from the documentation today)

caridy commented 4 years ago

We have abandoned this approach for the time being as far as I can tell. But time will tell once we implement the in-house SSR.