mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
wip
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState } from "../reactivity";
|
||||
import type { App, Env } from "../app/app";
|
||||
import { BDom, VNode } from "../blockdom";
|
||||
import { clearReactivesForCallback, Reactive, reactive } from "../reactivity";
|
||||
import { batched, Callback } from "../utils";
|
||||
import { Component, Props } from "./component";
|
||||
import { fibersInError, handleError } from "./error_handling";
|
||||
import {
|
||||
@@ -18,6 +19,43 @@ import { applyStyles } from "./style";
|
||||
|
||||
let currentNode: ComponentNode | null = null;
|
||||
|
||||
export function getCurrent(): ComponentNode | null {
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Integration with reactivity system (useState)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
|
||||
/**
|
||||
* Creates a reactive object that will be observed by the current component.
|
||||
* Reading data from the returned object (eg during rendering) will cause the
|
||||
* component to subscribe to that data and be rerendered when it changes.
|
||||
*
|
||||
* @param state the state to observe
|
||||
* @returns a reactive object that will cause the component to re-render on
|
||||
* relevant changes
|
||||
* @see reactive
|
||||
*/
|
||||
export function useState<T extends object>(state: T): Reactive<T> {
|
||||
if (!batchedRenderFunctions.has(currentNode!)) {
|
||||
batchedRenderFunctions.set(
|
||||
currentNode!,
|
||||
batched(() => currentNode!.render())
|
||||
);
|
||||
}
|
||||
const render = batchedRenderFunctions.get(currentNode!)!;
|
||||
const reactiveState = reactive(state, render);
|
||||
|
||||
// manual implementation of onWillUnmount to break cyclic dependency
|
||||
currentNode!.willUnmount.unshift( clearReactivesForCallback.bind(null, render))
|
||||
return reactiveState;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// component function (used in compiled template code)
|
||||
// -----------------------------------------------------------------------------
|
||||
function arePropsDifferent(props1: Props, props2: Props): boolean {
|
||||
for (let k in props1) {
|
||||
if (props1[k] !== props2[k]) {
|
||||
@@ -34,6 +72,7 @@ export function component(
|
||||
parent: any,
|
||||
hasSlots: boolean = false
|
||||
): ComponentNode {
|
||||
console.warn('asdf')
|
||||
let node: any = ctx.children[key];
|
||||
let isDynamic = typeof name !== "string";
|
||||
|
||||
@@ -51,7 +90,9 @@ export function component(
|
||||
|
||||
const parentFiber = ctx.fiber!;
|
||||
if (node) {
|
||||
console.warn('coucou');
|
||||
if (hasSlots || parentFiber.deep || arePropsDifferent(node.component.props, props)) {
|
||||
console.warn('coucou3');
|
||||
node.updateAndRender(props, parentFiber);
|
||||
}
|
||||
} else {
|
||||
@@ -75,14 +116,9 @@ export function component(
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Component VNode
|
||||
// Component VNode class
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
export function getCurrent(): ComponentNode | null {
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
type LifecycleHook = Function;
|
||||
|
||||
export class ComponentNode<T extends typeof Component = typeof Component>
|
||||
@@ -119,11 +155,9 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
||||
applyDefaultProps(props, C);
|
||||
const env = (parent && parent.childEnv) || app.env;
|
||||
this.childEnv = env;
|
||||
if (props) {
|
||||
props = useState(props);
|
||||
} else {
|
||||
console.trace()
|
||||
}
|
||||
// if (props) {
|
||||
// props = useState(props);
|
||||
// }
|
||||
this.component = new C(props, env, this) as any;
|
||||
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
|
||||
if (C.style) {
|
||||
|
||||
+2
-1
@@ -34,6 +34,7 @@ import type { AppConfig } from "./app/app";
|
||||
import { App } from "./app/app";
|
||||
import { Component } from "./component/component";
|
||||
import { getCurrent } from "./component/component_node";
|
||||
export { useState } from "./component/component_node";
|
||||
|
||||
export { App, Component };
|
||||
|
||||
@@ -55,7 +56,7 @@ export { status } from "./component/status";
|
||||
export { Portal } from "./portal";
|
||||
export { Memo } from "./memo";
|
||||
export { css, xml } from "./tags";
|
||||
export { useState, reactive } from "./reactivity";
|
||||
export { reactive } from "./reactivity";
|
||||
export { useEffect, useEnv, useExternalListener, useRef, useSubEnv } from "./hooks";
|
||||
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
||||
|
||||
|
||||
+3
-29
@@ -1,6 +1,4 @@
|
||||
import { onWillUnmount } from "./component/lifecycle_hooks";
|
||||
import { ComponentNode, getCurrent } from "./component/component_node";
|
||||
import { batched, Callback } from "./utils";
|
||||
import { Callback } from "./utils";
|
||||
|
||||
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
|
||||
const TARGET = Symbol("Target");
|
||||
@@ -9,7 +7,7 @@ const KEYCHANGES = Symbol("Key changes");
|
||||
|
||||
type ObjectKey = string | number | symbol;
|
||||
type Target = object;
|
||||
type Reactive<T extends Target = Target> = T & {
|
||||
export type Reactive<T extends Target = Target> = T & {
|
||||
[TARGET]: any;
|
||||
};
|
||||
|
||||
@@ -81,7 +79,7 @@ const callbacksToTargets = new WeakMap<Callback, Set<Target>>();
|
||||
*
|
||||
* @param callback the callback for which the reactives need to be cleared
|
||||
*/
|
||||
function clearReactivesForCallback(callback: Callback): void {
|
||||
export function clearReactivesForCallback(callback: Callback): void {
|
||||
const targetsToClear = callbacksToTargets.get(callback);
|
||||
if (!targetsToClear) {
|
||||
return;
|
||||
@@ -190,27 +188,3 @@ export function reactive<T extends Target>(target: T, callback: Callback): React
|
||||
return reactivesForTarget.get(callback) as Reactive<T>;
|
||||
}
|
||||
|
||||
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
|
||||
/**
|
||||
* Creates a reactive object that will be observed by the current component.
|
||||
* Reading data from the returned object (eg during rendering) will cause the
|
||||
* component to subscribe to that data and be rerendered when it changes.
|
||||
*
|
||||
* @param state the state to observe
|
||||
* @returns a reactive object that will cause the component to re-render on
|
||||
* relevant changes
|
||||
* @see reactive
|
||||
*/
|
||||
export function useState<T extends object>(state: T): Reactive<T> {
|
||||
const node = getCurrent()!;
|
||||
if (!batchedRenderFunctions.has(node)) {
|
||||
batchedRenderFunctions.set(
|
||||
node,
|
||||
batched(() => node.render())
|
||||
);
|
||||
}
|
||||
const render = batchedRenderFunctions.get(node)!;
|
||||
const reactiveState = reactive(state, render);
|
||||
onWillUnmount(() => clearReactivesForCallback(render));
|
||||
return reactiveState;
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ describe("basics", () => {
|
||||
expect(fixture.innerHTML).toBe("<div><span></span></div>");
|
||||
});
|
||||
|
||||
test("child can be updated", async () => {
|
||||
test.only("child can be updated", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<t t-esc="props.value"/>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user