diff --git a/src/createEffectWhen/__tests__/createEffectWhen.test.ts b/src/createEffectWhen/__tests__/createEffectWhen.test.ts index ecf3ab6..8bbcf7f 100644 --- a/src/createEffectWhen/__tests__/createEffectWhen.test.ts +++ b/src/createEffectWhen/__tests__/createEffectWhen.test.ts @@ -1,7 +1,8 @@ import { renderHook } from "@testing-library/react"; import { describe, expect, expectTypeOf, it, vi } from "vitest"; -import { createEffectWhen } from ".."; import { predicates } from "../../useEffectWhen"; +import { matchPredicate } from "../../useEffectWhenMatch"; +import { createEffectWhen } from "../createEffectWhen"; import { tuple } from "./createEffectWhen.utils"; import type { ReadyDeps } from "../../useEffectWhen"; import type { DependencyList } from "react"; @@ -10,6 +11,11 @@ interface TestUser { id: string; } +type QueryState = + | { status: "idle" } + | { status: "success"; data: string } + | { status: "error"; error: Error }; + const truthy = (deps: DependencyList): boolean => predicates.truthy(deps); const readyPair = ( deps: [string | null, number | null] @@ -124,5 +130,26 @@ describe("createEffectWhen", () => { expect(effect).toHaveBeenCalledWith(["hello", 42]); }); + + it("should compose matchPredicate into a reusable narrowed hook", () => { + const useEffectWhenQuerySettled = createEffectWhen( + matchPredicate<"status", QueryState, "success" | "error">("status", ["success", "error"]) + ); + const effect = vi.fn(); + + renderHook(() => + useEffectWhenQuerySettled( + ([query]) => { + expectTypeOf(query).toEqualTypeOf< + Extract + >(); + effect(query.status); + }, + [{ status: "success", data: "result" }] + ) + ); + + expect(effect).toHaveBeenCalledWith("success"); + }); }); }); diff --git a/src/index.ts b/src/index.ts index 5e0f311..148d983 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ export { useEffectWhen, predicates } from "./useEffectWhen"; export { useEffectWhenReady } from "./useEffectWhenReady"; export { useEffectWhenTruthy } from "./useEffectWhenTruthy"; export { useEffectWhenChanged } from "./useEffectWhenChanged"; -export { useEffectWhenMatch } from "./useEffectWhenMatch"; +export { matchPredicate, useEffectWhenMatch } from "./useEffectWhenMatch"; export { createEffectWhen } from "./createEffectWhen"; export type { Falsy, diff --git a/src/useEffectWhenMatch/index.ts b/src/useEffectWhenMatch/index.ts index 6ca7e9c..15b4e3e 100644 --- a/src/useEffectWhenMatch/index.ts +++ b/src/useEffectWhenMatch/index.ts @@ -1,2 +1,3 @@ export { useEffectWhenMatch } from "./useEffectWhenMatch"; +export { matchPredicate } from "./useEffectWhenMatch.utils"; export type { Discriminant, MatchedDeps } from "./useEffectWhenMatch.types"; diff --git a/src/useEffectWhenMatch/useEffectWhenMatch.ts b/src/useEffectWhenMatch/useEffectWhenMatch.ts index d47de03..8b957a0 100644 --- a/src/useEffectWhenMatch/useEffectWhenMatch.ts +++ b/src/useEffectWhenMatch/useEffectWhenMatch.ts @@ -1,7 +1,7 @@ import { useEffectWhen } from "../useEffectWhen"; -import { isMatch } from "./useEffectWhenMatch.utils"; -import type { UseEffectWhenEffect, UseEffectWhenOptions } from "../useEffectWhen"; +import { matchPredicate } from "./useEffectWhenMatch.utils"; import type { Discriminant, MatchedDeps } from "./useEffectWhenMatch.types"; +import type { UseEffectWhenEffect, UseEffectWhenOptions } from "../useEffectWhen"; /** * Gates an effect on a discriminated union at `deps[0]` whose field at `key` @@ -23,5 +23,5 @@ export function useEffectWhenMatch< value: V | ReadonlyArray, options?: UseEffectWhenOptions ): void { - useEffectWhen(effect, deps, isMatch(key, value), options); + useEffectWhen(effect, deps, matchPredicate(key, value), options); } diff --git a/src/useEffectWhenMatch/useEffectWhenMatch.utils.ts b/src/useEffectWhenMatch/useEffectWhenMatch.utils.ts index d9985a7..37289a5 100644 --- a/src/useEffectWhenMatch/useEffectWhenMatch.utils.ts +++ b/src/useEffectWhenMatch/useEffectWhenMatch.utils.ts @@ -1,5 +1,5 @@ -import type { GuardPredicate } from "../useEffectWhen"; import type { Discriminant, MatchedDeps } from "./useEffectWhenMatch.types"; +import type { GuardPredicate } from "../useEffectWhen"; function isReadonlyArray(value: T | ReadonlyArray): value is ReadonlyArray { return Array.isArray(value); @@ -9,7 +9,7 @@ function includesValue(values: ReadonlyArray, candidate: T): boolean { return values.includes(candidate); } -export function isMatch, V extends Q[K]>( +export function matchPredicate, V extends Q[K]>( key: K, value: V | ReadonlyArray ): GuardPredicate> {