[IMP[ component/hooks: add onWillStart and onWillUpdateProps

This commit is contained in:
Géry Debongnie
2019-10-09 15:09:26 +02:00
parent 5f6f081ae2
commit 07dac970f7
3 changed files with 68 additions and 4 deletions
+9 -2
View File
@@ -86,6 +86,8 @@ interface Internal<T extends Env, Props> {
willUnmountCB: Function | null; willUnmountCB: Function | null;
willPatchCB: Function | null; willPatchCB: Function | null;
patchedCB: Function | null; patchedCB: Function | null;
willStartCB: Function | null;
willUpdatePropsCB: Function | null;
classObj: { [key: string]: boolean } | null; classObj: { [key: string]: boolean } | null;
refs: { [key: string]: Component<T, any> | HTMLElement | undefined } | null; refs: { [key: string]: Component<T, any> | HTMLElement | undefined } | null;
} }
@@ -192,6 +194,8 @@ export class Component<T extends Env, Props extends {}> {
willUnmountCB: null, willUnmountCB: null,
willPatchCB: null, willPatchCB: null,
patchedCB: null, patchedCB: null,
willStartCB: null,
willUpdatePropsCB: null,
observer: null, observer: null,
render: qweb.render.bind(qweb, this.__getTemplate(qweb)), render: qweb.render.bind(qweb, this.__getTemplate(qweb)),
classObj: null, classObj: null,
@@ -510,7 +514,10 @@ export class Component<T extends Env, Props extends {}> {
if (defaultProps) { if (defaultProps) {
nextProps = this.__applyDefaultProps(nextProps, 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; this.props = nextProps;
const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber); const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber);
fiber.patchQueue.push(fiber); fiber.patchQueue.push(fiber);
@@ -566,7 +573,7 @@ export class Component<T extends Env, Props extends {}> {
} }
async __prepareAndRender(fiber: Fiber<Props>): Promise<VNode> { async __prepareAndRender(fiber: Fiber<Props>): Promise<VNode> {
try { try {
await this.willStart(); await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]);
} catch (e) { } catch (e) {
errorHandler(e, this); errorHandler(e, this);
return Promise.resolve(h("div")); return Promise.resolve(h("div"));
+18 -2
View File
@@ -64,11 +64,28 @@ function makeLifecycleHook(method: string, reverse: boolean = false) {
} }
} }
function makeAsyncHook(method: string) {
return function(cb) {
const component: Component<any, any> = 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 onMounted = makeLifecycleHook("mountedCB", true);
export const onWillUnmount = makeLifecycleHook("willUnmountCB"); export const onWillUnmount = makeLifecycleHook("willUnmountCB");
export const onWillPatch = makeLifecycleHook("willPatchCB"); export const onWillPatch = makeLifecycleHook("willPatchCB");
export const onPatched = makeLifecycleHook("patchedCB", true); export const onPatched = makeLifecycleHook("patchedCB", true);
export const onWillStart = makeAsyncHook("willStartCB");
export const onWillUpdateProps = makeAsyncHook("willUpdatePropsCB");
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// useRef // useRef
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -100,7 +117,6 @@ export function useRef(name: string): Ref {
// useSubEnv // useSubEnv
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/** /**
* This hook is a simple way to let components use a sub environment. Note that * 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 * 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) { export function useSubEnv(nextEnv) {
const component = Component._current; const component = Component._current;
component.env = Object.assign(Object.create(component.env), nextEnv); component.env = Object.assign(Object.create(component.env), nextEnv);
} }
+41
View File
@@ -7,6 +7,8 @@ import {
useRef, useRef,
onPatched, onPatched,
onWillPatch, onWillPatch,
onWillStart,
onWillUpdateProps,
useSubEnv useSubEnv
} from "../src/hooks"; } from "../src/hooks";
import { xml } from "../src/tags"; import { xml } from "../src/tags";
@@ -430,4 +432,43 @@ describe("hooks", () => {
await component.mount(fixture); await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>3<div>5</div></div>"); expect(fixture.innerHTML).toBe("<div>3<div>5</div></div>");
}); });
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<any, any> {
static template = xml`<span><t t-esc="props.value"/></span>`;
constructor(parent, props) {
super(parent, props);
useMyHook();
}
}
class App extends Component<any, any> {
static template = xml`<div><MyComponent value="state.value"/></div>`;
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("<div><span>1</span></div>");
expect(steps).toEqual(["onWillStart"]);
app.state.value = 2;
await nextTick();
expect(fixture.innerHTML).toBe("<div><span>2</span></div>");
expect(steps).toEqual(["onWillStart", "onWillUpdateProps"]);
});
}); });