Compare commits

..

1 Commits

Author SHA1 Message Date
Steve Van Essche fe176f13a5 Update hooks.md
- Remove a duplicate "the"
- Capitalize "HTML" as initialism should be
- Add "s" to plural functions
2025-08-06 15:55:31 +02:00
4 changed files with 4 additions and 131 deletions
+3 -3
View File
@@ -107,7 +107,7 @@ is necessary, since Owl needs to react to a change in state.
### `useRef` ### `useRef`
The `useRef` hook is useful when we need a way to interact with some inside part The `useRef` hook is useful when we need a way to interact with some inside part
of a component, rendered by Owl. It only work on a html element tagged by the of a component, rendered by Owl. It only work on a HTML element tagged by the
`t-ref` directive: `t-ref` directive:
```xml ```xml
@@ -227,12 +227,12 @@ function useSomething() {
This hook will run a callback when a component is mounted and patched, and This hook will run a callback when a component is mounted and patched, and
will run a cleanup function before patching and before unmounting the will run a cleanup function before patching and before unmounting the
the component (only if some dependencies have changed). component (only if some dependencies have changed).
It has almost the same API as the React `useEffect` hook, except that the dependencies It has almost the same API as the React `useEffect` hook, except that the dependencies
are defined by a function instead of just the dependencies. are defined by a function instead of just the dependencies.
The `useEffect` hook takes two function: the effect function and the dependency The `useEffect` hook takes two functions: the effect function and the dependency
function. The effect function perform some task and return (optionally) a cleanup function. The effect function perform some task and return (optionally) a cleanup
function. The dependency function returns a list of dependencies, these dependencies function. The dependency function returns a list of dependencies, these dependencies
are passed as parameters in the effect function . If any of these are passed as parameters in the effect function . If any of these
-22
View File
@@ -320,28 +320,6 @@ class ComponentB extends owl.Component {
Note: the props validation code is done by using the [validate utility function](utils.md#validate). Note: the props validation code is done by using the [validate utility function](utils.md#validate).
### `slots` prop
If a component that uses [slots](slots.md) also lists or validates its props, then
you will have to explicitely allow the `slots` prop (with an `Object` type), or
allow extra props using the `*` notation mentioned above. This is because slots
are provided to a component [as props](slots.md#slots-and-props).
For example:
```js
class MyComponent extends Component {
static props = [someProp, slots?];
}
class MyComponentWithValidation extends Component {
static props = {
someProp: {type: Number, optional: true},
slots : {type: Object, optional: true},
}
}
```
## Good Practices ## Good Practices
A `props` object is a collection of values that come from the parent. As such, A `props` object is a collection of values that come from the parent. As such,
-43
View File
@@ -1,5 +1,4 @@
import { OwlError } from "../common/owl_error"; import { OwlError } from "../common/owl_error";
import { ComponentNode, getCurrent } from "./component_node";
export type Callback = () => void; export type Callback = () => void;
/** /**
@@ -82,52 +81,10 @@ export function validateTarget(target: HTMLElement | ShadowRoot) {
} }
export class EventBus extends EventTarget { export class EventBus extends EventTarget {
constructor(events?: string[]) {
if (events) {
let node: ComponentNode | null = null;
try {
node = getCurrent();
} catch {}
if (node?.app?.dev) {
return new DebugEventBus(events);
}
}
super();
}
trigger(name: string, payload?: any) { trigger(name: string, payload?: any) {
this.dispatchEvent(new CustomEvent(name, { detail: payload })); this.dispatchEvent(new CustomEvent(name, { detail: payload }));
} }
} }
class DebugEventBus extends EventBus {
private events: Set<string>;
constructor(events: string[]) {
super();
this.events = new Set(events);
}
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions
): void {
if (!this.events.has(type)) {
throw new OwlError(`EventBus: subscribing to unknown event '${type}'`);
}
super.addEventListener(type, listener, options);
}
trigger(name: string, payload?: any) {
if (!this.events.has(name)) {
throw new OwlError(`EventBus: triggering unknown event '${name}'`);
}
super.trigger(name, payload);
}
dispatchEvent(event: Event): boolean {
if (!this.events.has(event.type)) {
throw new OwlError(`EventBus: dispatching unknown event '${event.type}'`);
}
return super.dispatchEvent(event);
}
}
export function whenReady(fn?: any): Promise<void> { export function whenReady(fn?: any): Promise<void> {
return new Promise(function (resolve) { return new Promise(function (resolve) {
+1 -63
View File
@@ -1,7 +1,5 @@
import { batched, EventBus, htmlEscape, markup } from "../src/runtime/utils"; import { batched, EventBus, htmlEscape, markup } from "../src/runtime/utils";
import { makeTestFixture, nextMicroTick } from "./helpers"; import { nextMicroTick } from "./helpers";
import { getCurrent } from "../src/runtime/component_node";
import { Component, mount, xml } from "../src";
describe("event bus behaviour", () => { describe("event bus behaviour", () => {
test("can subscribe and be notified", () => { test("can subscribe and be notified", () => {
@@ -35,66 +33,6 @@ describe("event bus behaviour", () => {
bus.addEventListener("event", (ev: any) => expect(ev.detail).toBe("hello world")); bus.addEventListener("event", (ev: any) => expect(ev.detail).toBe("hello world"));
bus.trigger("event", "hello world"); bus.trigger("event", "hello world");
}); });
test("events are not validated if the bus is created outside of dev mode", async () => {
let bus_empty: EventBus | null = null;
class Root extends Component {
static template = xml`<div/>`;
setup() {
getCurrent(); // checks that we're in a component context
bus_empty = new EventBus([]);
}
}
await mount(Root, makeTestFixture());
bus_empty!.addEventListener("a", () => {});
bus_empty!.trigger("a");
bus_empty!.dispatchEvent(new CustomEvent("a"));
});
test("events are validated if the bus is created in dev mode & events are provided", async () => {
let bus: EventBus | null = null;
let bus_empty: EventBus | null = null;
let bbus_no_validation: EventBus | null = null;
class Root extends Component {
static template = xml`<div/>`;
setup() {
getCurrent(); // checks that we're in a component context
bus = new EventBus(["a", "b"]);
bus_empty = new EventBus([]);
bbus_no_validation = new EventBus();
}
}
await mount(Root, makeTestFixture(), { test: true });
bbus_no_validation!.addEventListener("c", () => {});
bbus_no_validation!.trigger("c");
bbus_no_validation!.dispatchEvent(new CustomEvent("c"));
bus!.addEventListener("a", () => {});
bus!.trigger("a");
bus!.dispatchEvent(new CustomEvent("a"));
expect(() => bus!.addEventListener("c", () => {})).toThrow(
"EventBus: subscribing to unknown event 'c'"
);
expect(() => bus!.trigger("c")).toThrow("EventBus: triggering unknown event 'c'");
expect(() => bus!.dispatchEvent(new CustomEvent("c"))).toThrow(
"EventBus: dispatching unknown event 'c'"
);
expect(() => bus_empty!.addEventListener("a", () => {})).toThrow(
"EventBus: subscribing to unknown event 'a'"
);
expect(() => bus_empty!.trigger("a")).toThrow("EventBus: triggering unknown event 'a'");
expect(() => bus_empty!.dispatchEvent(new CustomEvent("a"))).toThrow(
"EventBus: dispatching unknown event 'a'"
);
});
}); });
describe("batched", () => { describe("batched", () => {