mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
@@ -162,6 +162,11 @@ constructor.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There is another static property defined on the `Component` class: `current`.
|
||||||
|
This property is set to the currently being defined component (in the constructor).
|
||||||
|
This is the way [hooks](hooks.md) are able to get a reference to the target
|
||||||
|
component.
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
We explain here all the public methods of the `Component` class.
|
We explain here all the public methods of the `Component` class.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
- [`useContext`](#usecontext)
|
- [`useContext`](#usecontext)
|
||||||
- [`useRef`](#useref)
|
- [`useRef`](#useref)
|
||||||
- [`useSubEnv`](#usesubenv)
|
- [`useSubEnv`](#usesubenv)
|
||||||
|
- [Making customized hooks](#making-customized-hooks)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -146,6 +147,10 @@ class SomeComponent extends Component {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Hooks can get a reference to the currently being defined component with the
|
||||||
|
`Component.current` static property. This is why they need to be called in the
|
||||||
|
constructor.
|
||||||
|
|
||||||
### `useState`
|
### `useState`
|
||||||
|
|
||||||
The `useState` hook is certainly the most important hooks for Owl components:
|
The `useState` hook is certainly the most important hooks for Owl components:
|
||||||
@@ -277,3 +282,68 @@ The `useSubEnv` takes one argument: an object which contains some key/value that
|
|||||||
will be added to the parent environment. Note that it will extend, not replace
|
will be added to the parent environment. Note that it will extend, not replace
|
||||||
the parent environment. And of course, the parent environment will not be
|
the parent environment. And of course, the parent environment will not be
|
||||||
affected.
|
affected.
|
||||||
|
|
||||||
|
### Making customized hooks
|
||||||
|
|
||||||
|
Hooks are a wonderful way to organize the code of a complex component by feature
|
||||||
|
instead of by lifecycle methods. They are like mixins, except that they can be
|
||||||
|
easily composed together.
|
||||||
|
|
||||||
|
But, like every good things in life, hooks should be used with moderation. They are
|
||||||
|
not the solution to every problem.
|
||||||
|
|
||||||
|
- they may be overkill: if your component needs to perform some action specific
|
||||||
|
to him (so, the specific code does not need to be shared), there is nothing
|
||||||
|
wrong with a simple class method:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// maybe overkill
|
||||||
|
class A extends Component {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
useMySpecificHook()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok
|
||||||
|
class B extends Component {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
this.performSpecificTask()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the second solution is easier to extend in sub components.
|
||||||
|
|
||||||
|
- they may be harder to test: if a customized hook inject some external side
|
||||||
|
effect dependency, then it is harder to test without doing some non obvious
|
||||||
|
manipulation. For example, assume that we want to give a reference to a
|
||||||
|
router in a `useRouter` hook. We could do this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const router = new Router(...);
|
||||||
|
|
||||||
|
function useRouter() {
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, this does not *hook* into the internal of the component. It
|
||||||
|
simply returns a global object, which is difficult to mock.
|
||||||
|
|
||||||
|
A better way would be to do something like this: get the reference from the
|
||||||
|
environment.
|
||||||
|
|
||||||
|
```js
|
||||||
|
function useRouter() {
|
||||||
|
return Component.current.env.router;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This means that we give control to the application developer to create the
|
||||||
|
router, which is good, so they can set it up, subclass it, ... And then, to
|
||||||
|
test our components, we can just add a mock router in the environment.
|
||||||
|
|
||||||
|
Note: the code above makes use of the `Component.current` property. This is the
|
||||||
|
way hooks are able to get a reference to the component currently being created.
|
||||||
+1
-1
@@ -59,7 +59,7 @@ export class Context extends EventBus {
|
|||||||
* to context state changes. The `useContext` method returns the context state
|
* to context state changes. The `useContext` method returns the context state
|
||||||
*/
|
*/
|
||||||
export function useContext(ctx: Context): any {
|
export function useContext(ctx: Context): any {
|
||||||
const component: Component<any, any> = Component._current;
|
const component: Component<any, any> = Component.current!;
|
||||||
const __owl__ = component.__owl__;
|
const __owl__ = component.__owl__;
|
||||||
const id = __owl__.id;
|
const id = __owl__.id;
|
||||||
const mapping = ctx.mapping;
|
const mapping = ctx.mapping;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
readonly __owl__: Internal<Env, Props>;
|
readonly __owl__: Internal<Env, Props>;
|
||||||
static template?: string | null = null;
|
static template?: string | null = null;
|
||||||
static _template?: string | null = null;
|
static _template?: string | null = null;
|
||||||
static _current?: any | null = null;
|
static current: Component<any,any> | null = null;
|
||||||
static components = {};
|
static components = {};
|
||||||
static props?: any;
|
static props?: any;
|
||||||
static defaultProps?: any;
|
static defaultProps?: any;
|
||||||
@@ -142,7 +142,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
*/
|
*/
|
||||||
constructor(parent: Component<T, any> | T, props?: Props) {
|
constructor(parent: Component<T, any> | T, props?: Props) {
|
||||||
const defaultProps = (<any>this.constructor).defaultProps;
|
const defaultProps = (<any>this.constructor).defaultProps;
|
||||||
Component._current = this;
|
Component.current = this;
|
||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
props = this.__applyDefaultProps(props, defaultProps);
|
props = this.__applyDefaultProps(props, defaultProps);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -22,7 +22,7 @@ import { Observer } from "./core/observer";
|
|||||||
* trigger a rerendering of the current component.
|
* trigger a rerendering of the current component.
|
||||||
*/
|
*/
|
||||||
export function useState<T>(state: T): T {
|
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__;
|
const __owl__ = component.__owl__;
|
||||||
if (!__owl__.observer) {
|
if (!__owl__.observer) {
|
||||||
__owl__.observer = new Observer();
|
__owl__.observer = new Observer();
|
||||||
@@ -37,7 +37,7 @@ export function useState<T>(state: T): T {
|
|||||||
function makeLifecycleHook(method: string, reverse: boolean = false) {
|
function makeLifecycleHook(method: string, reverse: boolean = false) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
return function(cb) {
|
return function(cb) {
|
||||||
const component: Component<any, any> = Component._current;
|
const component: Component<any, any> = Component.current!;
|
||||||
if (component.__owl__[method]) {
|
if (component.__owl__[method]) {
|
||||||
const current = component.__owl__[method];
|
const current = component.__owl__[method];
|
||||||
component.__owl__[method] = function() {
|
component.__owl__[method] = function() {
|
||||||
@@ -50,7 +50,7 @@ function makeLifecycleHook(method: string, reverse: boolean = false) {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return function(cb) {
|
return function(cb) {
|
||||||
const component: Component<any, any> = Component._current;
|
const component: Component<any, any> = Component.current!;
|
||||||
if (component.__owl__[method]) {
|
if (component.__owl__[method]) {
|
||||||
const current = component.__owl__[method];
|
const current = component.__owl__[method];
|
||||||
component.__owl__[method] = function() {
|
component.__owl__[method] = function() {
|
||||||
@@ -66,7 +66,7 @@ function makeLifecycleHook(method: string, reverse: boolean = false) {
|
|||||||
|
|
||||||
function makeAsyncHook(method: string) {
|
function makeAsyncHook(method: string) {
|
||||||
return function(cb) {
|
return function(cb) {
|
||||||
const component: Component<any, any> = Component._current;
|
const component: Component<any, any> = Component.current!;
|
||||||
if (component.__owl__[method]) {
|
if (component.__owl__[method]) {
|
||||||
const current = component.__owl__[method];
|
const current = component.__owl__[method];
|
||||||
component.__owl__[method] = function(...args) {
|
component.__owl__[method] = function(...args) {
|
||||||
@@ -100,7 +100,7 @@ interface Ref {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useRef(name: string): Ref {
|
export function useRef(name: string): Ref {
|
||||||
const __owl__ = Component._current.__owl__;
|
const __owl__ = Component.current!.__owl__;
|
||||||
return {
|
return {
|
||||||
get el(): HTMLElement | null {
|
get el(): HTMLElement | null {
|
||||||
const val = __owl__.refs && __owl__.refs[name];
|
const val = __owl__.refs && __owl__.refs[name];
|
||||||
@@ -123,6 +123,6 @@ export function useRef(name: string): Ref {
|
|||||||
* constructor method.
|
* constructor method.
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ function deleteLocalSample() {
|
|||||||
|
|
||||||
function useSamples() {
|
function useSamples() {
|
||||||
const samples = loadSamples();
|
const samples = loadSamples();
|
||||||
const component = owl.Component._current;
|
const component = owl.Component.current;
|
||||||
let interval;
|
let interval;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user