Files
owl/src/hooks.ts
T

134 lines
4.2 KiB
TypeScript
Raw Normal View History

2019-09-24 14:06:53 +02:00
import { Component } from "./component/component";
import { Observer } from "./core/observer";
/**
* Owl Hook System
*
* This file introduces the concept of hooks, similar to React or Vue hooks.
* We have currently an implementation of:
* - useState (reactive state)
* - onMounted
* - onWillUnmount
2019-09-26 21:56:01 +02:00
* - useRef
2019-09-24 14:06:53 +02:00
*/
// -----------------------------------------------------------------------------
// useState
// -----------------------------------------------------------------------------
2019-09-24 14:06:53 +02:00
/**
* This is the main way a component can be made reactive. The useState hook
* will return an observed object (or array). Changes to that value will then
* trigger a rerendering of the current component.
*/
export function useState<T>(state: T): T {
const component: Component<any, any> = Component.current!;
2019-09-24 14:06:53 +02:00
const __owl__ = component.__owl__;
if (!__owl__.observer) {
__owl__.observer = new Observer();
2019-11-14 14:09:42 +01:00
__owl__.observer.notifyCB = component.render.bind(component);
2019-09-24 14:06:53 +02:00
}
return __owl__.observer.observe(state);
}
// -----------------------------------------------------------------------------
// Life cycle hooks
// -----------------------------------------------------------------------------
2019-09-29 16:47:06 +02:00
function makeLifecycleHook(method: string, reverse: boolean = false) {
2019-10-08 11:12:02 +02:00
if (reverse) {
return function(cb) {
const component: Component<any, any> = Component.current!;
2019-10-08 11:12:02 +02:00
if (component.__owl__[method]) {
const current = component.__owl__[method];
2019-09-29 16:47:06 +02:00
component.__owl__[method] = function() {
current.call(component);
cb.call(component);
};
} else {
2019-10-08 11:12:02 +02:00
component.__owl__[method] = cb;
}
};
} else {
return function(cb) {
const component: Component<any, any> = Component.current!;
2019-10-08 11:12:02 +02:00
if (component.__owl__[method]) {
const current = component.__owl__[method];
2019-09-29 16:47:06 +02:00
component.__owl__[method] = function() {
cb.call(component);
current.call(component);
};
2019-10-08 11:12:02 +02:00
} else {
component.__owl__[method] = cb;
2019-09-29 16:47:06 +02:00
}
2019-10-08 11:12:02 +02:00
};
}
2019-09-29 16:47:06 +02:00
}
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);
2019-09-29 16:47:06 +02:00
export const onWillUnmount = makeLifecycleHook("willUnmountCB");
export const onWillPatch = makeLifecycleHook("willPatchCB");
export const onPatched = makeLifecycleHook("patchedCB", true);
export const onWillStart = makeAsyncHook("willStartCB");
export const onWillUpdateProps = makeAsyncHook("willUpdatePropsCB");
// -----------------------------------------------------------------------------
// useRef
// -----------------------------------------------------------------------------
2019-09-26 21:56:01 +02:00
/**
* 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__;
2019-09-26 21:56:01 +02:00
return {
get el(): HTMLElement | null {
const val = __owl__.refs && __owl__.refs[name];
if (val instanceof HTMLElement) {
return val;
} else if (val instanceof Component) {
return val.el;
}
return null;
2019-09-26 21:56:01 +02:00
},
get comp(): Component<any, any> | null {
const val = __owl__.refs && __owl__.refs[name];
return val instanceof Component ? val : null;
}
};
}
// -----------------------------------------------------------------------------
// useSubEnv
// -----------------------------------------------------------------------------
/**
* 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
* constructor method.
*/
export function useSubEnv(nextEnv) {
const component = Component.current!;
component.env = Object.assign(Object.create(component.env), nextEnv);
}