Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected Component Lifecycle on LWC OSS in native Shadow DOM mode #1452

Closed
jmpisson opened this issue Aug 16, 2019 · 4 comments
Closed

Unexpected Component Lifecycle on LWC OSS in native Shadow DOM mode #1452

jmpisson opened this issue Aug 16, 2019 · 4 comments
Labels

Comments

@jmpisson
Copy link

jmpisson commented Aug 16, 2019

Description

In native shadow DOM mode, the component lifecycle of slot content and the slot receiver is not running in the sequence expected.

Steps to Reproduce

Considering my-app as the Root Component inside LWC project, we have two components, Parent and Child, in the app markup:

my/app/app.html

<template>
    <div>
        <my-parent>
                <my-child></my-child>
        </my-parent>
    </div>
</template>

my/parent/parent.html

<template>
    <div>
        <h2>Parent</h2>
        <slot></slot>
    </div>
</template>

my/parent/parent.js

import { LightningElement } from "lwc";

export default class Parent extends LightningElement {
    connectedCallback() {
        console.log('Connected Parent');
    }

    renderedCallback() {
        console.log('Rendered Parent');
    }
}

my/child/child.html

<template>
    <div>
        <h2>Child</h2>
    </div>
</template>

my/child/child.js

import { LightningElement } from "lwc";

export default class Child extends LightningElement {
    connectedCallback() {
        console.log('Connected Child');
    }

    renderedCallback() {
        console.log('Rendered Child');
    }
}

Load the page and see the JavaScript Console Output

Playground link

Run with Native Shadow DOM option selected
https://playground.lwcjs.org/projects/3-XFTm3xZ/2/edit

Expected Results

Console Output:

Connected Parent
Connected Child
Rendered Child
Rendered Parent

Expected behavior in native web components: https://jsbin.com/vavenef/edit?html,js,console

Connected Parent
Connected Child

Actual Results

Console Output:

Connected Child
Rendered Child
Connected Parent
Rendered Parent

Browsers Affected

Firefox 68.173.0.3683.75 built on Debian buster/sid, running on Debian 10.0 (64-bit)
Chromium 73.0.3683.75 (Developer Build) built on Debian buster/sid, running on Debian 10.0 (64-bit)
Gnome Web 3.32 built on Debian buster/sid, running on Debian 10.0 (64-bit)

Version

Node project

  • package.json
    "lwc-services": "^1.2.2"
  • LWC: 1.0.0
@caridy
Copy link
Contributor

caridy commented Aug 24, 2019

@jmpisson there might be something going on here... let me loop in @ravijayaramappa who is working on this area at the moment. There are a couple of things:

  • the virtual dom library does some optimizations when appending elements, usually works from bottom to top.
  • the rendered phase is something that we, the LWC engine, will control, we can decide to do render on connect, or render on the microtask, so, in that case, it is not deterministic.

@ravijayaramappa ravijayaramappa changed the title Unexpected Component Lifecycle on LWC OSS Unexpected Component Lifecycle on LWC OSS in native Shadow DOM mode Aug 27, 2019
@ravijayaramappa
Copy link
Contributor

ravijayaramappa commented Aug 28, 2019

Investigation summary:
The issues is manifested only when running LWC in native shadow DOM mode. In synthetic shadow mode, the behavior matches the native web components behavior. The problem seems to be in the way we process slot content in the engine.

The insertion hooks for custom elements inserts a given node first, then inserts its children(slot content) and then invokes the insertCustomElmHook for the given vnode.

    insert: (vnode: VCustomElement, parentNode: Node, referenceNode: Node | null) => {
        insertNodeHook(vnode, parentNode, referenceNode); // <my-parent> is inserted into the DOM
        createChildrenHook(vnode);  // <my-child> is inserted into the DOM and its lifecycle callbacks invoked
        insertCustomElmHook(vnode); // <my-parent>'s vnode is processed and its lifecycle callbacks are invoked
    }

In synthetic mode, we have some special logic for handling slot content which makes sure the slot content() is not processed as the children of the slotted parent().

export function allocateChildrenHook(vnode: VCustomElement) {
    const elm = vnode.elm as HTMLElement;
    const vm = getCustomElementVM(elm);
    const { children } = vnode;
    vm.aChildren = children;
    if (isTrue(useSyntheticShadow)) {
        // slow path
        allocateInSlot(vm, children);
        // every child vnode is now allocated, and the host should receive none directly, it receives them via the shadow!
        vnode.children = EmptyArray;
    }
}

if (isTrue(useSyntheticShadow)) {
// slow path
allocateInSlot(vm, children);
// every child vnode is now allocated, and the host should receive none directly, it receives them via the shadow!
vnode.children = EmptyArray;
}

@ravijayaramappa
Copy link
Contributor

ravijayaramappa commented Aug 28, 2019

This should be solved with the node-reactions proposal salesforce/lwc-rfcs#11 which guarantees that lifecycle callbacks are processed synchronously immediately after an element is connected to the DOM.

Reference PR: #1431

@nolanlawson
Copy link
Collaborator

AIUI this issue is fixed, and may be further fixed by #3198. Closing for now, please reopen as necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants