From 990a08dc04de825040351fafa62d246fa8097da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 1 Apr 2019 15:42:24 +0200 Subject: [PATCH] imp: add componentDidUpdate hook --- src/component.ts | 19 +++++++++++--- tests/component.test.ts | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/component.ts b/src/component.ts index 638a96a1..10ae0d5d 100644 --- a/src/component.ts +++ b/src/component.ts @@ -149,6 +149,16 @@ export class Component< */ mounted() {} + /** + * This hook is called whenever a component did actually update its props, + * state or env. + * + * This method is not called on the initial render. It is useful to interact + * with the DOM (for example, through an external library) whenever the + * component was updated. + */ + componentDidUpdate() {} + /** * 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. @@ -281,8 +291,9 @@ export class Component< } Object.assign(this.env, nextEnv); if (this.__widget__.isMounted) { - return this.render(true); + await this.render(true); } + this.componentDidUpdate(); } async updateProps( @@ -312,8 +323,9 @@ export class Component< } Object.assign(this.state, nextState); if (this.__widget__.isStarted) { - return this.render(); + await this.render(); } + this.componentDidUpdate(); } //-------------------------------------------------------------------------- @@ -322,7 +334,8 @@ export class Component< async _updateProps(nextProps: Props): Promise { this.props = nextProps; - return this.render(); + await this.render(); + this.componentDidUpdate(); } _patch(vnode) { diff --git a/tests/component.test.ts b/tests/component.test.ts index 52129a89..f5d9997a 100644 --- a/tests/component.test.ts +++ b/tests/component.test.ts @@ -387,6 +387,61 @@ describe("lifecycle hooks", () => { ]); }); + test("componentDidUpdate hook is called after updateState", async () => { + let n = 0; + + class TestWidget extends Widget { + state = { a: 1 }; + + componentDidUpdate() { + n++; + } + } + const widget = new TestWidget(env); + await widget.mount(fixture); + expect(n).toBe(0); + + await widget.updateState({}); // empty update, should do nothing + expect(n).toBe(0); + + await widget.updateState({ a: 3 }); + expect(n).toBe(1); + }); + + test("componentDidUpdate hook is called after updateProps", async () => { + let n = 0; + + class TestWidget extends Widget { + componentDidUpdate() { + n++; + } + } + const widget = new TestWidget(env, { a: 1 }); + await widget.mount(fixture); + expect(n).toBe(0); + + await widget.updateProps({ a: 2 }); + expect(n).toBe(1); + }); + + test("componentDidUpdate hook is called after updateEnv", async () => { + let n = 0; + + class TestWidget extends Widget { + state = { a: 1 }; + + componentDidUpdate() { + n++; + } + } + const widget = new TestWidget(env); + await widget.mount(fixture); + expect(n).toBe(0); + + await widget.updateEnv({ isMobile: true }); + expect(n).toBe(1); + }); + test("shouldUpdate hook prevent rerendering", async () => { let shouldUpdate = false; class TestWidget extends Widget {