Skip to content

Commit

Permalink
fix(): update lifecycles respect hierarchy (#1924)
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat authored Oct 4, 2019
1 parent b8d71a4 commit 29bdd8f
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 109 deletions.
15 changes: 2 additions & 13 deletions src/declarations/host-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ export interface HostElement extends HTMLElement {
*/
['s-cr']?: RenderNode;

/**
* Is Active Loading:
* Set of child host elements that are actively loading.
*/
['s-al']?: Set<HostElement>;

/**
* Lifecycle ready
*/
Expand All @@ -49,13 +43,6 @@ export interface HostElement extends HTMLElement {
*/
['s-sc']?: string;

/**
* Component Initial Load:
* The component has fully loaded, instance creatd,
* and has rendered. Method is on the host element prototype.
*/
['s-init']?: () => void;

/**
* Hot Module Replacement, dev mode only
*/
Expand All @@ -66,6 +53,8 @@ export interface HostElement extends HTMLElement {
*/
['s-hmr-load']?: () => void;

['s-p']?: Promise<void>[];

componentOnReady?: () => Promise<this>;
}

Expand Down
1 change: 1 addition & 0 deletions src/declarations/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export interface HostRef {
$onReadyResolve$?: (elm: any) => void;
$onInstancePromise$?: Promise<any>;
$onInstanceResolve$?: (elm: any) => void;
$onRenderResolve$?: () => void;
$vnode$?: VNode;
$queuedListeners$?: [string, any][];
$rmListeners$?: () => void;
Expand Down
20 changes: 7 additions & 13 deletions src/hydrate/platform/bootstrap-hydrate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as d from '../../declarations';
import { doc, getComponent, getHostRef, plt } from '@platform';
import { doc, getComponent, plt } from '@platform';
import { hydrateComponent } from './hydrate-component';
import { insertVdomAnnotations, postUpdateComponent } from '@runtime';
import { insertVdomAnnotations } from '@runtime';


export function bootstrapHydrate(win: Window, opts: d.HydrateDocumentOptions, done: (results: BootstrapHydrateResults) => void) {
Expand All @@ -20,13 +20,6 @@ export function bootstrapHydrate(win: Window, opts: d.HydrateDocumentOptions, do
connectElements(win, opts, results, this, connectedElements, waitPromises);
};

const patchedComponentInit = function patchedComponentInit(this: d.HostElement) {
const hostRef = getHostRef(this);
if (hostRef != null) {
postUpdateComponent(this, hostRef);
}
};

const patchComponent = function(elm: d.HostElement) {
const tagName = elm.nodeName.toLowerCase();
if (elm.tagName.includes('-')) {
Expand All @@ -37,10 +30,11 @@ export function bootstrapHydrate(win: Window, opts: d.HydrateDocumentOptions, do
elm.connectedCallback = patchedConnectedCallback;
}

if (typeof elm['s-init'] !== 'function') {
elm['s-rc'] = [];
elm['s-init'] = patchedComponentInit;
}
elm['s-p'] = [];
elm['s-rc'] = [];
// if (typeof elm['s-init'] !== 'function') {
// elm['s-init'] = patchedComponentInit;
// }
}
}
};
Expand Down
17 changes: 4 additions & 13 deletions src/runtime/bootstrap-lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { BUILD } from '@build-conditionals';
import { doc, getHostRef, plt, registerHost, supportsShadowDom, win } from '@platform';
import { hmrStart } from './hmr-component';
import { HYDRATE_ID, PLATFORM_FLAGS, PROXY_FLAGS } from './runtime-constants';
import { appDidLoad, forceUpdate, postUpdateComponent } from './update-component';
import { appDidLoad, forceUpdate } from './update-component';


export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.CustomElementsDefineOptions = {}) => {
Expand Down Expand Up @@ -66,7 +66,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.
const tagName = cmpMeta.$tagName$;
const HostElement = class extends HTMLElement {

['s-lr']: boolean;
['s-p']: Promise<void>[];
['s-rc']: (() => void)[];

// StencilLazyHost
Expand All @@ -75,10 +75,8 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.
super(self);
self = this;

if (BUILD.lifecycle) {
this['s-lr'] = false;
this['s-rc'] = [];
}
this['s-p'] = [];
this['s-rc'] = [];

registerHost(self);
if (BUILD.shadowDom && cmpMeta.$flags$ & CMP_FLAGS.shadowDomEncapsulation) {
Expand All @@ -105,13 +103,6 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.
plt.jmp(() => disconnectedCallback(this));
}

's-init'() {
const hostRef = getHostRef(this);
if (hostRef.$lazyInstance$) {
postUpdateComponent(this, hostRef);
}
}

's-hmr'(hmrVersionId: string) {
if (BUILD.hotModuleReplacement) {
hmrStart(this, cmpMeta, hmrVersionId);
Expand Down
15 changes: 7 additions & 8 deletions src/runtime/connected-callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { doc, getHostRef, nextTick, plt, supportsShadowDom } from '@platform';
import { HYDRATE_ID, NODE_TYPE, PLATFORM_FLAGS } from './runtime-constants';
import { initializeClientHydrate } from './client-hydrate';
import { initializeComponent } from './initialize-component';
import { safeCall } from './update-component';
import { safeCall, attachToAncestor } from './update-component';

export const fireConnectedCallback = (instance: any) => {
if (BUILD.lazyLoad && BUILD.connectedCallback) {
Expand Down Expand Up @@ -57,22 +57,21 @@ export const connectedCallback = (elm: d.HostElement, cmpMeta: d.ComponentRuntim
}
}

if (BUILD.lifecycle && BUILD.lazyLoad) {
if (BUILD.lifecycle || BUILD.lazyLoad) {
// find the first ancestor component (if there is one) and register
// this component as one of the actively loading child components for its ancestor
let ancestorComponent = elm;

while ((ancestorComponent = (ancestorComponent.parentNode as any || ancestorComponent.host as any))) {
// climb up the ancestors looking for the first
// component that hasn't finished its lifecycle update yet
if ((BUILD.hydrateClientSide && ancestorComponent.nodeType === NODE_TYPE.ElementNode && ancestorComponent.hasAttribute('s-id')) || (ancestorComponent['s-init'] && ancestorComponent['s-lr'] === false)) {
if (
(BUILD.hydrateClientSide && ancestorComponent.nodeType === NODE_TYPE.ElementNode && ancestorComponent.hasAttribute('s-id')) ||
(ancestorComponent['s-p'])
) {
// we found this components first ancestor component
// keep a reference to this component's ancestor component
hostRef.$ancestorComponent$ = ancestorComponent;

// ensure there is an array to contain a reference to each of the child components
// and set this component as one of the ancestor's child components it should wait on
(ancestorComponent['s-al'] = ancestorComponent['s-al'] || new Set()).add(elm);
attachToAncestor(hostRef, (hostRef.$ancestorComponent$ = ancestorComponent));
break;
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/runtime/initialize-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ export const initializeComponent = async (elm: d.HostElement, hostRef: d.HostRef
if (BUILD.watchCallback) {
hostRef.$flags$ |= HOST_FLAGS.isWatchReady;
}
if (BUILD.method) {
hostRef.$onInstanceResolve$(elm);
}
fireConnectedCallback(hostRef.$lazyInstance$);

} else {
Expand Down Expand Up @@ -103,7 +100,7 @@ export const initializeComponent = async (elm: d.HostElement, hostRef: d.HostRef
const ancestorComponent = hostRef.$ancestorComponent$;
const schedule = () => scheduleUpdate(elm, hostRef, cmpMeta, true);

if (BUILD.lifecycle && BUILD.lazyLoad && ancestorComponent && ancestorComponent['s-lr'] === false && ancestorComponent['s-rc']) {
if (BUILD.lifecycle && BUILD.lazyLoad && ancestorComponent && ancestorComponent['s-rc']) {
// this is the intial load and this component it has an ancestor component
// but the ancestor component has NOT fired its will update lifecycle yet
// so let's just cool our jets and wait for the ancestor to continue first
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/set-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const setValue = (ref: d.RuntimeRef, propName: string, newVal: any, cmpMe
}
}

if (BUILD.updatable && (flags & (HOST_FLAGS.isActiveRender | HOST_FLAGS.hasRendered | HOST_FLAGS.isQueuedForUpdate)) === HOST_FLAGS.hasRendered) {
if (BUILD.updatable && (flags & (HOST_FLAGS.hasRendered | HOST_FLAGS.isQueuedForUpdate)) === HOST_FLAGS.hasRendered) {
if (BUILD.cmpShouldUpdate && instance.componentShouldUpdate) {
if (instance.componentShouldUpdate(newVal, oldVal, propName) === false) {
return;
Expand Down
91 changes: 90 additions & 1 deletion src/runtime/test/lifecycle-sync.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Prop, Watch, h } from '@stencil/core';
import { Component, Element, Host, Prop, Watch, h } from '@stencil/core';
import { newSpecPage } from '@stencil/core/testing';


Expand Down Expand Up @@ -193,4 +193,93 @@ describe('lifecycle sync', () => {
expect(rootInstance.renders).toBe(2);
});

describe('childrens', () => {
it ('sync', async () => {

const log: string[] = [];
@Component({
tag: 'cmp-a'
})
class CmpA {
@Prop() prop: string;
componentWillLoad() {
log.push('componentWillLoad a');
}
componentDidLoad() {
log.push('componentDidLoad a');
}
componentWillUpdate() {
log.push('componentWillUpdate a');
}
componentDidUpdate() {
log.push('componentDidUpdate a');
}
render() {
return (
<Host>
<cmp-b id='b1' prop={this.prop}>
<cmp-b id='b2' prop={this.prop}>
<cmp-b id='b3' prop={this.prop}></cmp-b>
</cmp-b>
</cmp-b>
</Host>
);
}
}
@Component({
tag: 'cmp-b'
})
class CmpB {
@Element() el: HTMLElement;
@Prop() prop: string;
componentWillLoad() {
log.push(`componentWillLoad ${this.el.id}`);
}
componentDidLoad() {
log.push(`componentDidLoad ${this.el.id}`);
}
componentWillUpdate() {
log.push(`componentWillUpdate ${this.el.id}`);
}
componentDidUpdate() {
log.push(`componentDidUpdate ${this.el.id}`);
}
}
const {root, waitForChanges} = await newSpecPage({
components: [CmpA, CmpB],
template: () => <cmp-a></cmp-a>
});
expect(log).toEqual([
'componentWillLoad a',
'componentWillLoad b1',
'componentWillLoad b2',
'componentWillLoad b3',
'componentDidLoad b3',
'componentDidLoad b2',
'componentDidLoad b1',
'componentDidLoad a',
]);
log.length = 0;
root.forceUpdate();
await waitForChanges();
expect(log).toEqual([
'componentWillUpdate a',
'componentDidUpdate a',
]);

log.length = 0;
root.prop = 'something else';
await waitForChanges();
expect(log).toEqual([
'componentWillUpdate a',
'componentWillUpdate b1',
'componentWillUpdate b2',
'componentWillUpdate b3',
'componentDidUpdate b3',
'componentDidUpdate b2',
'componentDidUpdate b1',
'componentDidUpdate a',
]);
});
});
});
Loading

0 comments on commit 29bdd8f

Please sign in to comment.