From 07dac970f72d9e12700e4bdf41b783143965d10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 9 Oct 2019 15:09:26 +0200 Subject: [PATCH] [IMP[ component/hooks: add onWillStart and onWillUpdateProps --- src/component/component.ts | 11 ++++++++-- src/hooks.ts | 20 +++++++++++++++++-- tests/hooks.test.ts | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/component/component.ts b/src/component/component.ts index 91f78903..5a470f97 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -86,6 +86,8 @@ interface Internal { willUnmountCB: Function | null; willPatchCB: Function | null; patchedCB: Function | null; + willStartCB: Function | null; + willUpdatePropsCB: Function | null; classObj: { [key: string]: boolean } | null; refs: { [key: string]: Component | HTMLElement | undefined } | null; } @@ -192,6 +194,8 @@ export class Component { willUnmountCB: null, willPatchCB: null, patchedCB: null, + willStartCB: null, + willUpdatePropsCB: null, observer: null, render: qweb.render.bind(qweb, this.__getTemplate(qweb)), classObj: null, @@ -510,7 +514,10 @@ export class Component { if (defaultProps) { nextProps = this.__applyDefaultProps(nextProps, defaultProps); } - await this.willUpdateProps(nextProps); + await Promise.all([ + this.willUpdateProps(nextProps), + this.__owl__.willUpdatePropsCB && this.__owl__.willUpdatePropsCB(nextProps) + ]); this.props = nextProps; const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber); fiber.patchQueue.push(fiber); @@ -566,7 +573,7 @@ export class Component { } async __prepareAndRender(fiber: Fiber): Promise { try { - await this.willStart(); + await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]); } catch (e) { errorHandler(e, this); return Promise.resolve(h("div")); diff --git a/src/hooks.ts b/src/hooks.ts index dc9a3d47..f1ca40ca 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -64,11 +64,28 @@ function makeLifecycleHook(method: string, reverse: boolean = false) { } } +function makeAsyncHook(method: string) { + return function(cb) { + const component: Component = Component._current; + if (component.__owl__[method]) { + const current = component.__owl__[method]; + component.__owl__[method] = function(...args) { + return Promise.all[(current.call(component, ...args), cb.call(component, ...args))]; + }; + } else { + component.__owl__[method] = cb; + } + }; +} + export const onMounted = makeLifecycleHook("mountedCB", true); export const onWillUnmount = makeLifecycleHook("willUnmountCB"); export const onWillPatch = makeLifecycleHook("willPatchCB"); export const onPatched = makeLifecycleHook("patchedCB", true); +export const onWillStart = makeAsyncHook("willStartCB"); +export const onWillUpdateProps = makeAsyncHook("willUpdatePropsCB"); + // ----------------------------------------------------------------------------- // useRef // ----------------------------------------------------------------------------- @@ -100,7 +117,6 @@ export function useRef(name: string): Ref { // useSubEnv // ----------------------------------------------------------------------------- - /** * This hook is a simple way to let components use a sub environment. Note that * like for all hooks, it is important that this is only called in the @@ -109,4 +125,4 @@ export function useRef(name: string): Ref { export function useSubEnv(nextEnv) { const component = Component._current; component.env = Object.assign(Object.create(component.env), nextEnv); -} \ No newline at end of file +} diff --git a/tests/hooks.test.ts b/tests/hooks.test.ts index b165e0d3..46c8b5f9 100644 --- a/tests/hooks.test.ts +++ b/tests/hooks.test.ts @@ -7,6 +7,8 @@ import { useRef, onPatched, onWillPatch, + onWillStart, + onWillUpdateProps, useSubEnv } from "../src/hooks"; import { xml } from "../src/tags"; @@ -430,4 +432,43 @@ describe("hooks", () => { await component.mount(fixture); expect(fixture.innerHTML).toBe("
3
5
"); }); + + test("can use onWillStart, onWillUpdateProps", async () => { + const steps: string[] = []; + function useMyHook() { + onWillStart(() => { + steps.push("onWillStart"); + }); + onWillUpdateProps(nextProps => { + expect(nextProps).toEqual({value: 2}); + steps.push("onWillUpdateProps"); + }); + } + class MyComponent extends Component { + static template = xml``; + constructor(parent, props) { + super(parent, props); + useMyHook(); + } + } + class App extends Component { + static template = xml`
`; + static components = { MyComponent }; + state = useState({ value: 1}); + } + + const app = new App(env); + await app.mount(fixture); + expect(app).not.toHaveProperty("willStart"); + expect(app).not.toHaveProperty("willUpdateProps"); + expect(fixture.innerHTML).toBe("
1
"); + expect(steps).toEqual(["onWillStart"]); + + app.state.value = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("
2
"); + expect(steps).toEqual(["onWillStart", "onWillUpdateProps"]); + }); + + });