mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP[ component/hooks: add onWillStart and onWillUpdateProps
This commit is contained in:
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user