[REF] app: reorganize startup code

The goal of this refactoring is to delay the instantiation of the
component node until we are ready to mount the component. This is useful
so we know if a component will be attached or mounted (which means that
it needs to have a template, or not)
This commit is contained in:
Géry Debongnie
2024-10-29 13:47:52 +01:00
parent b8d09e523d
commit b4f84513a6
3 changed files with 40 additions and 45 deletions
+39 -33
View File
@@ -53,7 +53,7 @@ declare global {
}
interface Root<P extends Props, E> {
node: ComponentNode<P, E>;
node: ComponentNode<P, E> | null;
mount(target: HTMLElement | ShadowRoot, options?: MountOptions): Promise<Component<P, E>>;
destroy(): void;
}
@@ -74,8 +74,8 @@ export class App<
props: P;
env: E;
scheduler = new Scheduler();
subRoots: Set<ComponentNode> = new Set();
root: ComponentNode<P, E> | null = null;
subRoots: Set<Root<any, any>> = new Set();
root: Root<P, E> | null = null;
warnIfNoStaticProps: boolean;
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
@@ -101,10 +101,8 @@ export class App<
target: HTMLElement | ShadowRoot,
options?: MountOptions
): Promise<Component<P, E> & InstanceType<T>> {
const root = this.createRoot(this.Root, { props: this.props });
this.root = root.node;
this.subRoots.delete(root.node);
return root.mount(target, options) as any;
this.root = this.createRoot(this.Root, { props: this.props });
return this.root.mount(target, options) as any;
}
createRoot<Props extends object, SubEnv = any>(
@@ -112,25 +110,24 @@ export class App<
config: RootConfig<Props, SubEnv> = {}
): Root<Props, SubEnv> {
const props = config.props || ({} as Props);
// hack to make sure the sub root get the sub env if necessary. for owl 3,
// would be nice to rethink the initialization process to make sure that
// we can create a ComponentNode and give it explicitely the env, instead
// of looking it up in the app
const env = this.env;
if (config.env) {
this.env = config.env as any;
}
const restore = saveCurrent();
const node = this.makeNode(Root, props);
restore();
if (config.env) {
this.env = env;
}
this.subRoots.add(node);
return {
node,
const root: Root<Props, SubEnv> = {
node: null,
mount: (target: HTMLElement | ShadowRoot, options?: MountOptions) => {
// hack to make sure the sub root get the sub env if necessary. for owl 3,
// would be nice to rethink the initialization process to make sure that
// we can create a ComponentNode and give it explicitely the env, instead
// of looking it up in the app
if (config.env) {
this.env = config.env as any;
}
const restore = saveCurrent();
const node = this.makeNode(Root, props);
root.node = node;
restore();
if (config.env) {
this.env = env;
}
App.validateTarget(target);
if (this.dev) {
validateProps(Root, props, { __owl__: { app: this } });
@@ -139,11 +136,16 @@ export class App<
return prom;
},
destroy: () => {
this.subRoots.delete(node);
node.destroy();
this.scheduler.processTasks();
this.subRoots.delete(root);
if (root.node) {
root.node?.destroy();
this.scheduler.processTasks();
}
},
};
this.subRoots.add(root);
return root;
}
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
@@ -178,13 +180,17 @@ export class App<
}
destroy() {
if (this.root) {
for (let subroot of this.subRoots) {
subroot.destroy();
}
this.root.destroy();
this.scheduler.processTasks();
const roots = [...this.subRoots].reverse();
for (let root of roots) {
root.destroy();
}
// if (this.root) {
// for (let subroot of this.subRoots) {
// subroot.destroy();
// }
// this.root.destroy();
this.scheduler.processTasks();
// }
apps.delete(this);
}
@@ -94,17 +94,6 @@ exports[`subroot can create a root in a setup function, then use a hook 1`] = `
}"
`;
exports[`subroot can create a root in a setup function, then use a hook 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`c\`);
}
}"
`;
exports[`subroot can mount subroot 1`] = `
"function anonymous(app, bdom, helpers
) {
+1 -1
View File
@@ -702,7 +702,7 @@ describe("props validation", () => {
const app = new App(Parent, { test: true });
await app.mount(fixture);
expect(fixture.innerHTML).toBe("12");
expect(app.root!.subscriptions).toEqual([{ keys: ["otherValue"], target: obj }]);
expect(app.root!.node!.subscriptions).toEqual([{ keys: ["otherValue"], target: obj }]);
});
test("props are validated whenever component is updated", async () => {