UNPKG

@kilpi/react-client

Version:

Kilpi React Client Components · Kilpi is the authorization framework for full-stack TypeScript applications, designed for flexible, powerful, agnostic, intuitive and developer friendly authorization.

103 lines (94 loc) 2.98 kB
import type { KilpiClient } from "@kilpi/client"; import type { AnyKilpiCore, GetPolicyByKey, InferPolicyInputs, PolicysetKeys } from "@kilpi/core"; import { useEffect, useState } from "react"; import { useCacheClearSignal } from "./useCacheClearSignal"; /** * Value returned by useIsAuthorized */ type UseIsAuthorizedValue = | { status: "idle"; isAuthorized: null; error: null; } | { status: "loading"; isAuthorized: null; error: null; } | { status: "error"; isAuthorized: null; error: unknown; } | { status: "success"; isAuthorized: boolean; error: null; }; /** * Create the `useIsAuthorized` react client hook. */ export function createUseIsAuthorized<T extends AnyKilpiCore>(KilpiClient: KilpiClient<T>) { /** * React client hook for accessing the current subject. */ return function useIsAuthorized<TKey extends PolicysetKeys<T["$$infer"]["policies"]>>( key: TKey, ...inputs: InferPolicyInputs<GetPolicyByKey<T["$$infer"]["policies"], TKey>> ) { /** * Hold current state of useIsAuthorized. */ const [value, setValue] = useState<UseIsAuthorizedValue>({ status: "idle", isAuthorized: null, error: null, }); /** * Reset state and force refetch whenever client cache is cleared. */ const cacheSignal = useCacheClearSignal(KilpiClient, () => { setValue({ status: "idle", isAuthorized: null, error: null }); }); /** * Fetch the current subject. It is OK to fire this fetch function in multiple components, * as the subject is cached and only fetched once. */ useEffect(() => { // Cleanup utilities: When unmounted, cancel fetch and prevent further state updates. let isMounted = true; const abortController = new AbortController(); // Define data fetching logic with respect to cleanup utilities. async function fetchIsAuthorized() { if (isMounted) setValue({ status: "loading", error: null, isAuthorized: null }); try { const isAuthorized = await KilpiClient.fetchIsAuthorized({ key, resource: inputs[0], queryOptions: { signal: abortController.signal }, }); if (isMounted) setValue({ status: "success", error: null, isAuthorized }); } catch (error) { if (isMounted) setValue({ status: "error", error, isAuthorized: null }); } } // Initiate fetch fetchIsAuthorized(); // Trigger cleanup utilities return () => { isMounted = false; abortController.abort(); }; }, [setValue, cacheSignal]); /** * Return value with computed values. */ return Object.assign(value, { isLoading: value.status === "loading", isError: value.status === "error", isSuccess: value.status === "success", isIdle: value.status === "idle", }); }; }