[IMP] hooks: implement useRef hook

closes #194
This commit is contained in:
Géry Debongnie
2019-09-26 21:56:01 +02:00
parent 91b9e78182
commit 5cdaa7b473
20 changed files with 321 additions and 171 deletions
+6 -10
View File
@@ -85,6 +85,7 @@ interface Internal<T extends Env, Props> {
render: CompiledTemplate | null;
mountedHandlers: { [key: number]: Function };
classObj: { [key: string]: boolean } | null;
refs: { [key: string]: Component<T, any> | HTMLElement | undefined } | null;
}
//------------------------------------------------------------------------------
@@ -97,6 +98,9 @@ export class Component<T extends Env, Props extends {}> {
static template?: string | null = null;
static _template?: string | null = null;
static _current?: any | null = null;
static components = {};
static props?: any;
static defaultProps?: any;
/**
* The `el` is the root element of the component. Note that it could be null:
@@ -105,19 +109,10 @@ export class Component<T extends Env, Props extends {}> {
get el(): HTMLElement | null {
return this.__owl__.vnode ? (<any>this).__owl__.vnode.elm : null;
}
static components = {};
env: T;
props: Props;
// type of props is not easily representable in typescript...
static props?: any;
static defaultProps?: any;
refs: {
[key: string]: Component<T, any> | HTMLElement | undefined;
} = {};
//--------------------------------------------------------------------------
// Lifecycle
//--------------------------------------------------------------------------
@@ -192,7 +187,8 @@ export class Component<T extends Env, Props extends {}> {
mountedHandlers: {},
observer: null,
render: null,
classObj: null
classObj: null,
refs: null
};
}
+3 -2
View File
@@ -263,9 +263,10 @@ QWeb.addDirective({
let refExpr = "";
let refKey: string = "";
if (ref) {
ctx.rootContext.shouldDefineRefs = true;
refKey = `ref${ctx.generateID()}`;
ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`);
refExpr = `context.refs[${refKey}] = w${componentID};`;
refExpr = `context.__owl__.refs[${refKey}] = w${componentID};`;
}
let transitionsInsertCode = "";
if (transition) {
@@ -273,7 +274,7 @@ QWeb.addDirective({
}
let finalizeComponentCode = `w${componentID}.${keepAlive ? "unmount" : "destroy"}();`;
if (ref && !keepAlive) {
finalizeComponentCode += `delete context.refs[${refKey}];`;
finalizeComponentCode += `delete context.__owl__.refs[${refKey}];`;
}
if (transition) {
finalizeComponentCode = `let finalize = () => {
+29 -4
View File
@@ -9,9 +9,9 @@ import { Observer } from "./core/observer";
* - useState (reactive state)
* - onMounted
* - onWillUnmount
* - useRef
*/
/**
* useState hook
*
@@ -20,7 +20,7 @@ import { Observer } from "./core/observer";
* trigger a rerendering of the current component.
*/
export function useState<T>(state: T): T {
const component: Component<any,any> = Component._current;
const component: Component<any, any> = Component._current;
const __owl__ = component.__owl__;
if (!__owl__.observer) {
__owl__.observer = new Observer();
@@ -34,7 +34,7 @@ export function useState<T>(state: T): T {
* mounted. Note that the component mounted method is called first.
*/
export function onMounted(cb) {
const component = Component._current;
const component: Component<any, any> = Component._current;
const current = component.mounted;
component.mounted = function() {
current.call(component);
@@ -47,10 +47,35 @@ export function onMounted(cb) {
* willUnmounted. Note that the component mounted method is called last.
*/
export function onWillUnmount(cb) {
const component = Component._current;
const component: Component<any, any> = Component._current;
const current = component.willUnmount;
component.willUnmount = function() {
cb();
current.call(component);
};
}
/**
* useRef hook
*
* The purpose of this hook is to allow components to get a reference to a sub
* html node or component.
*/
interface Ref {
el: HTMLElement | null;
comp: Component<any, any> | null;
}
export function useRef(name: string): Ref {
const __owl__ = Component._current.__owl__;
return {
get el(): HTMLElement | null {
const val = __owl__.refs && __owl__.refs[name];
return val instanceof HTMLElement ? val : null;
},
get comp(): Component<any, any> | null {
const val = __owl__.refs && __owl__.refs[name];
return val instanceof Component ? val : null;
}
};
}
+4
View File
@@ -20,6 +20,7 @@ export class Context {
shouldDefineParent: boolean = false;
shouldDefineQWeb: boolean = false;
shouldDefineUtils: boolean = false;
shouldDefineRefs: boolean = false;
shouldDefineResult: boolean = true;
shouldProtectContext: boolean = false;
shouldTrackScope: boolean = false;
@@ -62,6 +63,9 @@ export class Context {
if (this.shouldDefineResult) {
this.code.unshift(" let result;");
}
if (this.shouldDefineRefs) {
this.code.unshift(" context.__owl__.refs = context.__owl__.refs || {};");
}
if (this.shouldDefineOwner) {
// this is necessary to prevent some directives (t-forach for ex) to
// pollute the rendering context by adding some keys in it.
+2 -1
View File
@@ -79,9 +79,10 @@ QWeb.addDirective({
name: "ref",
priority: 95,
atNodeCreation({ ctx, value, addNodeHook }) {
ctx.rootContext.shouldDefineRefs = true;
const refKey = `ref${ctx.generateID()}`;
ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`);
addNodeHook("create", `context.refs[${refKey}] = n.elm;`);
addNodeHook("create", `context.__owl__.refs[${refKey}] = n.elm;`);
}
});