[IMP] hooks: add onWillPatch and onPatched

This commit also changes the behaviour of willPatch and patched: they no
longer transfer data from one to the other, because it can be done
cleanly by hooks and a closure.

closes #307
This commit is contained in:
Géry Debongnie
2019-09-29 16:47:06 +02:00
parent e82ed7dc8c
commit 367725d194
6 changed files with 269 additions and 51 deletions
+8 -13
View File
@@ -329,9 +329,6 @@ before an actual DOM patch, and is only intended to be used to save some local
DOM state. Also, it will not be called if the component is not in the DOM (this can
happen with components with `t-keepalive`).
The return value of this method will be given as the first argument of the
corresponding `patched` call.
#### `patched(snapshot)`
This hook is called whenever a component did actually update its DOM (most
@@ -342,8 +339,6 @@ with the DOM (for example, through an external library) whenever the
component was patched. Note that this hook will not be called if the compoent is
not in the DOM (this can happen with components with `t-keepalive`).
The `snapshot` parameter is the result of the previous `willPatch` call.
Updating the compoent state in this hook is possible, but not encouraged.
One need to be careful, because updates here will cause rerender, which in
turn will cause other calls to patched. So, we need to be particularly
@@ -1219,7 +1214,7 @@ class Node extends Component {
</t>
</g>
`;
static components = { Node };
static components = { Node };
}
class RootNode extends Component {
@@ -1230,12 +1225,12 @@ class RootNode extends Component {
`;
static components = { Node };
graph = {
label: "a",
children: [
{label: "b"},
{label: "c", children: [{label: "d"}, {label: "e"}]},
{label: "f", children: [{label: "g"}]},
]
label: "a",
children: [
{ label: "b" },
{ label: "c", children: [{ label: "d" }, { label: "e" }] },
{ label: "f", children: [{ label: "g" }] }
]
};
}
```
@@ -1247,4 +1242,4 @@ here: the `Node` component uses itself as a subcomponent.
Note that since SVG needs to be handled in a specific way (its namespace needs
to be properly set), there is a small constraint for Owl components: if an owl
component is supposed to be a part of an svg graph, then its root node needs to
be a `g` tag, so Owl can properly set the namespace.
be a `g` tag, so Owl can properly set the namespace.
+52 -8
View File
@@ -3,7 +3,8 @@
## Content
- [Overview](#overview)
- [Example](#example)
- [Example: Mouse Position](#example-mouse-position)
- [Example: Autofocus](#example-autofocus)
- [Reference](#reference)
- [One Rule](#one-rule)
- [`useState`](#usestate)
@@ -28,7 +29,7 @@ Hooks works beautifully with Owl components: they solve the problems mentioned
above, and in particular, they are the perfect way to make your component
reactive.
## Example
## Example: mouse position
Here is the classical example of a non trivial hook to track the mouse position.
@@ -57,9 +58,9 @@ function useMouse() {
// Main root component
class App extends owl.Component {
static template = xml`
<div t-name="App">
<div>Mouse: <t t-esc="mouse.x"/>, <t t-esc="mouse.y"/></div>
</div>`;
<div t-name="App">
<div>Mouse: <t t-esc="mouse.x"/>, <t t-esc="mouse.y"/></div>
</div>`;
// this hooks is bound to the 'mouse' property.
mouse = useMouse();
@@ -69,6 +70,49 @@ class App extends owl.Component {
Note that we use the prefix `use` for hooks, just like in React. This is just
a convention.
## Example: autofocus
Hooks can be combined to create the desired effect. For example, the following
hook combines the `useRef` hook with the `onPatched` and `onMounted` functions
to create an easy way to focus an input whenever it appears in the DOM:
```js
function useAutofocus(name) {
let ref = useRef(name);
let isInDom = false;
function updateFocus() {
if (!isInDom && ref.el) {
isInDom = true;
ref.el.focus();
} else if (isInDom && !ref.el) {
isInDom = false;
}
}
onPatched(updateFocus);
onMounted(updateFocus);
}
```
This hook takes the name of a valid `t-ref` directive, which should be present
in the template. It then checks whenever the component is mounted or patched if
the reference is not valid, and in this case, it will focus the node element.
This hook can be used like this:
```js
class SomeComponent extends Component {
static template = xml`
<div>
<input />
<input t-ref="myinput"/>
</div>`;
constructor(...args) {
super(...args);
useAutofocus("myinput");
}
}
```
## Reference
### One rule
@@ -79,21 +123,21 @@ constructor (or in class fields):
```js
// ok
class SomeComponent extends Component {
state = useState({value: 0});
state = useState({ value: 0 });
}
// also ok
class SomeComponent extends Component {
constructor(...args) {
super(...args);
this.state = useState({value: 0});
this.state = useState({ value: 0 });
}
}
// not ok: this is executed after the constructor is called
class SomeComponent extends Component {
async willStart() {
this.state = useState({value: 0});
this.state = useState({ value: 0 });
}
}
```