OWL教程9 其他的钩子
1 useComponent
返回对当前组件的引用(用于创建派生钩子)
function useSomething() {
const component = useComponent();
// now, component is bound to the instance of the current component
}
2 useEffect
当组件被挂载和打补丁时,这个钩子会运行一个回调函数,并在打补丁和卸载组件之前运行一个清理函数(只有当一些依赖项发生了变化时)。
它与React的useEffect钩子有着几乎相同的API,除了依赖关系是由函数定义的,而不仅仅是依赖关系。
useEffect钩子有两个函数:效果函数和依赖函数。效果函数执行一些任务并返回(可选的)清理函数。依赖函数返回一个依赖列表,这些依赖作为参数传递给效果函数。如果这些依赖项中的任何一个发生了变化,那么当前效果将被清理并重新执行。
下面是一个没有任何依赖的例子:
useEffect(
() => {
window.addEventListener("mousemove", someHandler);
return () => window.removeEventListener("mousemove", someHandler);
},
() => []
);
在上面的示例中,依赖项列表为空,因此只有在卸载组件时才会清除该效果。
如果跳过依赖函数,那么效果将被清理并在每个patch上重新运行。
下面是另一个例子,如何用useEffect钩子实现一个useAutofocus钩子:
function useAutofocus(name) {
let ref = useRef(name);
useEffect(
(el) => el && el.focus(),
() => [ref.el]
);
}
该钩子采用一个有效的t-ref指令的名称,该指令应该出现在模板中。然后,如果引用无效,它将检查何时安装组件或修补组件,在这种情况下,它将关注节点元素。这个钩子可以这样使用:
class SomeComponent extends Component {
static template = xml`
<div>
<input />
<input t-ref="myinput"/>
</div>`;
setup() {
useAutofocus("myinput");
}
}
下面是一个典型的跟踪鼠标位置的钩子的例子。
const { useState, onWillDestroy, Component } = owl;
// We define here a custom behaviour: this hook tracks the state of the mouse
// position
function useMouse() {
const position = useState({ x: 0, y: 0 });
function update(e) {
position.x = e.clientX;
position.y = e.clientY;
}
window.addEventListener("mousemove", update);
onWillDestroy(() => {
window.removeEventListener("mousemove", update);
});
return position;
}
// Main root component
class Root extends Component {
static template = xml`<div>Mouse: <t t-esc="mouse.x"/>, <t t-esc="mouse.y"/></div>`;
// this hooks is bound to the 'mouse' property.
mouse = useMouse();
}
注意,我们使用了钩子的前缀use,就像在React中一样。这只是惯例。
3 useEnv
useEnv钩子作为一些定制钩子的构建块很有用,这些定制钩子可能需要调用它们的组件的env的引用。
function useSomething() {
const env = useEnv();
// now, env is bound to the env of the current component
}
4 useExternalListener
useExternalListener钩子有助于解决一个非常常见的问题:每当挂载/卸载组件时,在某个目标上添加和删除侦听器。例如,一个下拉菜单(或它的父菜单)可能需要监听一个点击事件的窗口被关闭:
useExternalListener(window, "click", this.closeMenu);
5 useRef
当我们需要一种与组件内部元素交互的方式时,useRef钩子是很有用的,它是由Ow渲染的。它只对带有t-ref指令的html元素有效:
<div>
<input t-ref="someInput"/>
<span>hello</span>
</div>
在这个例子中,组件将能够通过useRef钩子访问输入框:
class Parent extends Component {
inputRef = useRef("someInput");
someMethod() {
// here, if component is mounted, refs are active:
// - this.inputRef.el is the input HTMLElement
}
}
如上面的例子所示,实际的HTMLElement实例是用el键访问的。
t-ref指令也接受带有字符串插值的动态值(类似于t-attf和t-component指令)。例如
<div t-ref="div_{{someCondition ? '1' : '2'}}"/>
在这里,引用需要这样设置:
this.ref1 = useRef("div_1");
this.ref2 = useRef("div_2");
只有在父组件被挂载时,引用才保证是活动的。如果不是这种情况,在它上面访问el将返回null。
6 useSubEnv
and useChildSubEnv
环境有时对于在所有组件之间共享一些公共信息很有用。但有时,我们想要将这些知识扩展到一个子树。
例如,如果我们有一个表单视图组件,也许我们想让一些模型对象对所有子组件可用,但不是对整个应用程序可用。这就是useChildSubEnv钩子可能有用的地方:它允许组件以一种只有其子组件才能访问的方式向环境添加一些信息:
class FormComponent extends Component {
setup() {
const model = makeModel();
// model will be available on this.env for this component and all children
useSubEnv({ model });
// someKey will be available on this.env for all children
useChildSubEnv({ someKey: "value" });
}
}
useSubEnv和useChildSubEnv钩子接受一个参数:一个对象,它包含一些键/值,将被添加到当前环境中。这些钩子将用新的信息创建一个新的env对象:
useSubEnv将把这个新环境分配给它自己和所有子组件
useChildSubEnv只会将这个新环境分配给所有子组件。
在Owl中,使用这两个钩子创建的环境通常是冻结的,以防止不必要的修改。
请注意,这两个钩子都可以被调用任意次数。然后,环境将相应地更新。