diff --git a/doc/reference/content.md b/doc/reference/content.md index 8f272579..6e5cb4d1 100644 --- a/doc/reference/content.md +++ b/doc/reference/content.md @@ -29,6 +29,8 @@ hooks utils useContext useState useRef + useComponent + useEnv useSubEnv useStore useDispatch diff --git a/doc/reference/hooks.md b/doc/reference/hooks.md index 149c4296..b60600e1 100644 --- a/doc/reference/hooks.md +++ b/doc/reference/hooks.md @@ -21,6 +21,8 @@ - [`useStore`](#usestore) - [`useDispatch`](#usedispatch) - [`useGetters`](#usegetters) + - [`useComponent`](#usecomponent) + - [`useEnv`](#useenv) - [Making customized hooks](#making-customized-hooks) ## Overview @@ -381,6 +383,16 @@ The `useDispatch` hook is the way for components to get a reference to the store The `useGetters` hook is the way for components to get a reference to the store getters. See the [store documentation](store.md) for more information. +### `useComponent` + +The `useComponent` hook is useful as a building block for some customized hooks, +that may need a reference to the component calling them. + +### `useEnv` + +The `useEnv` hook is useful as a building block for some customized hooks, +that may need a reference to the env of the component calling them. + ### Making customized hooks Hooks are a wonderful way to organize the code of a complex component by feature @@ -435,13 +447,11 @@ not the solution to every problem. ```js function useRouter() { - return Component.current.env.router; + const env = useEnv(); + return 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. diff --git a/src/hooks.ts b/src/hooks.ts index 24d42a2d..3f8573e6 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,4 +1,4 @@ -import { Component } from "./component/component"; +import { Component, Env } from "./component/component"; import { Observer } from "./core/observer"; /** @@ -118,6 +118,26 @@ export function useRef(name: string): Ref { }; } +// ----------------------------------------------------------------------------- +// "Builder" hooks +// ----------------------------------------------------------------------------- + +/** + * This hook is useful as a building block for some customized hooks, that may + * need a reference to the component calling them. + */ +export function useComponent(): Component { + return Component.current as any; +} + +/** + * This hook is useful as a building block for some customized hooks, that may + * need a reference to the env of the component calling them. + */ +export function useEnv(): E { + return Component.current.env as any; +} + // ----------------------------------------------------------------------------- // useSubEnv // ----------------------------------------------------------------------------- diff --git a/tests/hooks.test.ts b/tests/hooks.test.ts index 2b594c27..da91f655 100644 --- a/tests/hooks.test.ts +++ b/tests/hooks.test.ts @@ -9,8 +9,10 @@ import { onWillPatch, onWillStart, onWillUpdateProps, + useEnv, useSubEnv, useExternalListener, + useComponent, } from "../src/hooks"; import { xml } from "../src/tags"; @@ -520,6 +522,19 @@ describe("hooks", () => { }); }); + test("can use useEnv", async () => { + expect.assertions(1); + class TestComponent extends Component { + static template = xml`
`; + constructor() { + super(); + expect(useEnv()).toBe(env); + } + } + const component = new TestComponent(); + await component.mount(fixture); + }); + test("can use sub env", async () => { class TestComponent extends Component { static template = xml`
`; @@ -535,6 +550,19 @@ describe("hooks", () => { expect(component.env).toHaveProperty("val"); }); + test("can use useComponent", async () => { + expect.assertions(1); + class TestComponent extends Component { + static template = xml`
`; + constructor() { + super(); + expect(useComponent()).toBe(this); + } + } + const component = new TestComponent(); + await component.mount(fixture); + }); + test("parent and child env", async () => { class Child extends Component { static template = xml`
`;