mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] component: add setup lifecycle hook
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
- [Methods](#methods)
|
- [Methods](#methods)
|
||||||
- [Lifecycle](#lifecycle)
|
- [Lifecycle](#lifecycle)
|
||||||
- [`constructor(parent, props)`](#constructorparent-props)
|
- [`constructor(parent, props)`](#constructorparent-props)
|
||||||
|
- [`setup()`](#setup)
|
||||||
- [`willStart()`](#willstart)
|
- [`willStart()`](#willstart)
|
||||||
- [`mounted()`](#mounted)
|
- [`mounted()`](#mounted)
|
||||||
- [`willUpdateProps(nextProps)`](#willupdatepropsnextprops)
|
- [`willUpdateProps(nextProps)`](#willupdatepropsnextprops)
|
||||||
@@ -324,7 +325,7 @@ a owl component:
|
|||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
| ------------------------------------------------ | ----------------------------------------------------------- |
|
| ------------------------------------------------ | ----------------------------------------------------------- |
|
||||||
| **[constructor](#constructorparent-props)** | constructor |
|
| **[setup](#setup)** | setup |
|
||||||
| **[willStart](#willstart)** | async, before first rendering |
|
| **[willStart](#willstart)** | async, before first rendering |
|
||||||
| **[mounted](#mounted)** | just after component is rendered and added to the DOM |
|
| **[mounted](#mounted)** | just after component is rendered and added to the DOM |
|
||||||
| **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update |
|
| **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update |
|
||||||
@@ -369,6 +370,23 @@ class ClickCounter extends owl.Component {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Hook functions can be called in the constructor.
|
||||||
|
|
||||||
|
#### `setup()`
|
||||||
|
|
||||||
|
_setup_ is run just after the component is constructed. It is a lifecycle method,
|
||||||
|
very similar to the _constructor_, except that it does not receive any argument.
|
||||||
|
|
||||||
|
It is a valid method to call hook functions. Note that one of the main reason to
|
||||||
|
have the `setup` hook in the component lifecycle is to make it possible to
|
||||||
|
monkey patch it. It is a common need in the Odoo ecosystem.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
setup() {
|
||||||
|
useSetupAutofocus();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### `willStart()`
|
#### `willStart()`
|
||||||
|
|
||||||
willStart is an asynchronous hook that can be implemented to
|
willStart is an asynchronous hook that can be implemented to
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class SomeComponent extends Component {
|
|||||||
### One rule
|
### One rule
|
||||||
|
|
||||||
There is only one rule: every hook for a component has to be called in the
|
There is only one rule: every hook for a component has to be called in the
|
||||||
constructor (or in class fields):
|
constructor, in the _setup_ method, or in class fields:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// ok
|
// ok
|
||||||
@@ -147,6 +147,13 @@ class SomeComponent extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// also ok
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ value: 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// not ok: this is executed after the constructor is called
|
// not ok: this is executed after the constructor is called
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
async willStart() {
|
async willStart() {
|
||||||
|
|||||||
@@ -209,8 +209,20 @@ export class Component<Props extends {} = any, T extends Env = Env> {
|
|||||||
if (constr.style) {
|
if (constr.style) {
|
||||||
this.__applyStyles(constr);
|
this.__applyStyles(constr);
|
||||||
}
|
}
|
||||||
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setup is run just after the component is constructed. This is the standard
|
||||||
|
* location where the component can setup its hooks. It has some advantages
|
||||||
|
* over the constructor:
|
||||||
|
* - it can be patched (useful in odoo ecosystem)
|
||||||
|
* - it does not need to propagate the arguments to the super call
|
||||||
|
*
|
||||||
|
* Note: this method should not be called manually.
|
||||||
|
*/
|
||||||
|
setup() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* willStart is an asynchronous hook that can be implemented to perform some
|
* willStart is an asynchronous hook that can be implemented to perform some
|
||||||
* action before the initial rendering of a component.
|
* action before the initial rendering of a component.
|
||||||
|
|||||||
@@ -661,9 +661,8 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
class ChildWidget extends Component {
|
class ChildWidget extends Component {
|
||||||
static template = xml`<div/>`;
|
static template = xml`<div/>`;
|
||||||
constructor(parent) {
|
setup() {
|
||||||
super(parent);
|
steps.push("setup");
|
||||||
steps.push("init");
|
|
||||||
}
|
}
|
||||||
async willStart() {
|
async willStart() {
|
||||||
steps.push("willstart");
|
steps.push("willstart");
|
||||||
@@ -687,10 +686,10 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
const widget = new ParentWidget();
|
const widget = new ParentWidget();
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
expect(steps).toEqual(["init", "willstart", "mounted"]);
|
expect(steps).toEqual(["setup", "willstart", "mounted"]);
|
||||||
widget.state.ok = false;
|
widget.state.ok = false;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(steps).toEqual(["init", "willstart", "mounted", "willunmount"]);
|
expect(steps).toEqual(["setup", "willstart", "mounted", "willunmount"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("components are unmounted and destroyed if no longer in DOM, even after updateprops", async () => {
|
test("components are unmounted and destroyed if no longer in DOM, even after updateprops", async () => {
|
||||||
@@ -738,8 +737,7 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
class ChildWidget extends Component {
|
class ChildWidget extends Component {
|
||||||
static template = xml`<div/>`;
|
static template = xml`<div/>`;
|
||||||
constructor(parent) {
|
setup() {
|
||||||
super(parent);
|
|
||||||
steps.push("c init");
|
steps.push("c init");
|
||||||
}
|
}
|
||||||
async willStart() {
|
async willStart() {
|
||||||
@@ -755,8 +753,7 @@ describe("lifecycle hooks", () => {
|
|||||||
class ParentWidget extends Component {
|
class ParentWidget extends Component {
|
||||||
static template = xml`<div><t t-component="child"/></div>`;
|
static template = xml`<div><t t-component="child"/></div>`;
|
||||||
static components = { child: ChildWidget };
|
static components = { child: ChildWidget };
|
||||||
constructor(parent?) {
|
setup() {
|
||||||
super(parent);
|
|
||||||
steps.push("p init");
|
steps.push("p init");
|
||||||
}
|
}
|
||||||
async willStart() {
|
async willStart() {
|
||||||
|
|||||||
+20
-39
@@ -2,8 +2,7 @@ const COMPONENTS = `// In this example, we show how components can be defined an
|
|||||||
const { Component, useState, mount } = owl;
|
const { Component, useState, mount } = owl;
|
||||||
|
|
||||||
class Greeter extends Component {
|
class Greeter extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ word: 'Hello' });
|
this.state = useState({ word: 'Hello' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,8 +13,7 @@ class Greeter extends Component {
|
|||||||
|
|
||||||
// Main root component
|
// Main root component
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ name: 'World'});
|
this.state = useState({ name: 'World'});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,8 +50,7 @@ const ANIMATION = `// The goal of this component is to see how the t-transition
|
|||||||
const { Component, useState, mount } = owl;
|
const { Component, useState, mount } = owl;
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ value: 0 });
|
this.state = useState({ value: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +60,7 @@ class Counter extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ flag: false, componentFlag: false, numbers: [] });
|
this.state = useState({ flag: false, componentFlag: false, numbers: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,10 +190,9 @@ const LIFECYCLE_DEMO = `// This example shows all the possible lifecycle hooks
|
|||||||
const { Component, useState, mount } = owl;
|
const { Component, useState, mount } = owl;
|
||||||
|
|
||||||
class DemoComponent extends Component {
|
class DemoComponent extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ n: 0 });
|
this.state = useState({ n: 0 });
|
||||||
console.log("constructor");
|
console.log("setup");
|
||||||
}
|
}
|
||||||
async willStart() {
|
async willStart() {
|
||||||
console.log("willstart");
|
console.log("willstart");
|
||||||
@@ -223,8 +218,7 @@ class DemoComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ n: 0, flag: true });
|
this.state = useState({ n: 0, flag: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,8 +289,7 @@ function useMouse() {
|
|||||||
|
|
||||||
// Main root component
|
// Main root component
|
||||||
class App extends owl.Component {
|
class App extends owl.Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
// simple state hook (reactive object)
|
// simple state hook (reactive object)
|
||||||
this.counter = useState({ value: 0 });
|
this.counter = useState({ value: 0 });
|
||||||
|
|
||||||
@@ -333,8 +326,7 @@ const { Component, Context, mount } = owl;
|
|||||||
const { useContext } = owl.hooks;
|
const { useContext } = owl.hooks;
|
||||||
|
|
||||||
class ToolbarButton extends Component {
|
class ToolbarButton extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.theme = useContext(this.env.themeContext);
|
this.theme = useContext(this.env.themeContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,8 +460,7 @@ const actions = {
|
|||||||
// TodoItem
|
// TodoItem
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
class TodoItem extends Component {
|
class TodoItem extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
useAutofocus("input");
|
useAutofocus("input");
|
||||||
this.state = useState({ isEditing: false });
|
this.state = useState({ isEditing: false });
|
||||||
this.dispatch = useDispatch();
|
this.dispatch = useDispatch();
|
||||||
@@ -499,8 +490,7 @@ class TodoItem extends Component {
|
|||||||
// TodoApp
|
// TodoApp
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
class TodoApp extends Component {
|
class TodoApp extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ filter: "all" });
|
this.state = useState({ filter: "all" });
|
||||||
this.todos = useStore(state => state.todos);
|
this.todos = useStore(state => state.todos);
|
||||||
this.dispatch = useDispatch();
|
this.dispatch = useDispatch();
|
||||||
@@ -1026,8 +1016,7 @@ class FormView extends owl.Component {}
|
|||||||
FormView.components = { AdvancedComponent };
|
FormView.components = { AdvancedComponent };
|
||||||
|
|
||||||
class Chatter extends owl.Component {
|
class Chatter extends owl.Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.messages = Array.from(Array(100).keys());
|
this.messages = Array.from(Array(100).keys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1170,8 +1159,7 @@ const SLOTS = `// We show here how slots can be used to create generic component
|
|||||||
const { Component, useState, mount } = owl;
|
const { Component, useState, mount } = owl;
|
||||||
|
|
||||||
class Card extends Component {
|
class Card extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ showContent: true });
|
this.state = useState({ showContent: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1181,8 +1169,7 @@ class Card extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({val: 1});
|
this.state = useState({val: 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1193,8 +1180,7 @@ class Counter extends Component {
|
|||||||
|
|
||||||
// Main root component
|
// Main root component
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({a: 1, b: 3});
|
this.state = useState({a: 1, b: 3});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1306,8 +1292,7 @@ class SlowComponent extends Component {
|
|||||||
class NotificationList extends Component {}
|
class NotificationList extends Component {}
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ value: 0, notifs: [] });
|
this.state = useState({ value: 0, notifs: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1381,8 +1366,7 @@ const FORM = `// This example illustrate how the t-model directive can be used t
|
|||||||
const { Component, useState, mount } = owl;
|
const { Component, useState, mount } = owl;
|
||||||
|
|
||||||
class Form extends Component {
|
class Form extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({
|
this.state = useState({
|
||||||
text: "",
|
text: "",
|
||||||
othertext: "",
|
othertext: "",
|
||||||
@@ -1556,8 +1540,7 @@ const { useRef } = owl.hooks;
|
|||||||
class HelloWorld extends Component {}
|
class HelloWorld extends Component {}
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.state = useState({ value: 0 });
|
this.state = useState({ value: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1610,8 +1593,7 @@ class Window extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WindowManager extends Component {
|
class WindowManager extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.windows = [];
|
this.windows = [];
|
||||||
this.nextId = 1;
|
this.nextId = 1;
|
||||||
this.currentZindex = 1;
|
this.currentZindex = 1;
|
||||||
@@ -1661,8 +1643,7 @@ class WindowManager extends Component {
|
|||||||
WindowManager.components = { Window };
|
WindowManager.components = { Window };
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
setup() {
|
||||||
super(...arguments);
|
|
||||||
this.wmRef = useRef("wm");
|
this.wmRef = useRef("wm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user