From 91e7e00f97f3a050caaead4386b71f9d395ed432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 19 Mar 2019 13:46:18 +0100 Subject: [PATCH] clean component file, add some documentation --- src/component.ts | 97 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/src/component.ts b/src/component.ts index c552c118..bce24264 100644 --- a/src/component.ts +++ b/src/component.ts @@ -69,6 +69,25 @@ export class Component< // Lifecycle //-------------------------------------------------------------------------- + /** + * Creates an instance of Component. + * + * The root widget of a component tree needs an environment: + * + * ```javascript + * const root = new RootWidget(env, props); + * ``` + * + * Every other widget simply needs a reference to its parent: + * + * ```javascript + * const child = new SomeWidget(parent, props); + * ``` + * + * Note that most of the time, only the root widget needs to be created by + * hand. Other widgets should be created automatically by the framework (with + * the t-widget directive in a template) + */ constructor(parent: Component | T, props?: Props) { super(); @@ -77,16 +96,14 @@ export class Component< // Con: this is not really safe // Pro: but creating widget (by a template) is always unsafe anyway this.props = props; - let id: number; + let id: number = getId(); let p: Component | null = null; if (parent instanceof Component) { p = parent; this.env = parent.env; - id = getId(); parent.__widget__.children[id] = this; } else { this.env = parent; - id = getId(); } this.__widget__ = { id: id, @@ -104,13 +121,51 @@ export class Component< }; } + /** + * willStart is an asynchronous hook that can be implemented to perform some + * action before the initial rendering of a component. + * + * It will be called exactly once before the initial rendering. It is useful + * in some cases, for example, to load external assets (such as a JS library) + * before the widget is rendered. + * + * Note that a slow willStart method will slow down the rendering of the user + * interface. Therefore, some effort should be made to make this method as + * fast as possible. + * + * Note: this method should not be called manually. + */ async willStart() {} + /** + * mounted is a hook that is called each time a component is attached to the + * DOM. This is a good place to add some listeners, or to interact with the + * DOM, if the component needs to perform some measure for example. + * + * Note: this method should not be called manually. + * + * @see willUnmount + */ mounted() {} + /** + * willUnmount is a hook that is called each time a component is detached from + * the DOM. This is a good place to remove some listeners, for example. + * + * Note: this method should not be called manually. + * + * @see mounted + */ willUnmount() {} + /** + * destroyed is a hook called exactly once, when a component is destroyed. + * When a component is destroyed, its children will be destroyed first. + * + * Note: this method should not be called manually. + */ destroyed() {} + //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- @@ -143,7 +198,7 @@ export class Component< target.appendChild(this.el!); if (document.body.contains(target)) { - this.visitSubTree(w => { + this._visitSubTree(w => { if (!w.__widget__.isMounted && this.el!.contains(w.el)) { w.__widget__.isMounted = true; w.mounted(); @@ -156,7 +211,7 @@ export class Component< detach() { if (this.el) { - this.visitSubTree(w => { + this._visitSubTree(w => { if (w.__widget__.isMounted) { w.willUnmount(); w.__widget__.isMounted = false; @@ -168,6 +223,20 @@ export class Component< } } + async render(): Promise { + if (this.__widget__.isDestroyed) { + return; + } + const renderVDom = this._render(); + const renderId = this.__widget__.renderId; + const vnode = await renderVDom; + if (renderId === this.__widget__.renderId) { + // we only update the vnode and the actual DOM if no other rendering + // occurred between now and when the render method was initially called. + this._patch(vnode); + } + } + destroy() { if (!this.__widget__.isDestroyed) { for (let id in this.__widget__.children) { @@ -250,20 +319,6 @@ export class Component< return this.render(); } - async render(): Promise { - if (this.__widget__.isDestroyed) { - return; - } - const renderVDom = this._render(); - const renderId = this.__widget__.renderId; - const vnode = await renderVDom; - if (renderId === this.__widget__.renderId) { - // we only update the vnode and the actual DOM if no other rendering - // occurred between now and when the render method was initially called. - this._patch(vnode); - } - } - _patch(vnode) { this.__widget__.renderPromise = null; this.__widget__.vnode = patch( @@ -335,12 +390,12 @@ export class Component< } } - visitSubTree(callback: (w: Component) => boolean) { + _visitSubTree(callback: (w: Component) => boolean) { const shouldVisitChildren = callback(this); if (shouldVisitChildren) { const children = this.__widget__.children; for (let id in children) { - children[id].visitSubTree(callback); + children[id]._visitSubTree(callback); } } }