UNPKG

@gawryco/use-shareable-state

Version:

The tiny, typed React hook for URL query string state. Transform your components into shareable, bookmarkable experiences with zero boilerplate.

298 lines (296 loc) 12.3 kB
/** * Setter signature returned by the hook methods. Mirrors React's `setState` API. * * Accepts either a direct value or an updater function of the previous value. * * @template T Value type * @param value Either the next value or a function that derives it from the previous value */ type Updater<T> = (value: T | ((prev: T) => T)) => void; /** * Replaces the current URL's query string (without a navigation) so the page * stays in place and the history stack receives a new state. * * Uses `history.replaceState` to avoid pushing a new entry unless the consumer * opts into push behavior in a custom implementation. * * @param params Query parameters to apply to the current URL */ type HistoryAction = 'replace' | 'push'; interface NumberBuilder { /** * Creates a non-nullable number state bound to a query param. * * @param defaultValue Required default value when the param is missing/invalid * @param opts Optional constraints and history action * @returns A tuple `[value, setValue]` where value is never null */ (defaultValue: number, opts?: { min?: number; max?: number; step?: number; action?: HistoryAction; }): [number, Updater<number>]; /** * Creates a nullable number state bound to a query param. * * @param defaultValue Optional default value (defaults to null) * @param opts Optional constraints and history action * @returns A tuple `[value, setValue]` where value can be null */ optional(defaultValue?: number | null, opts?: { min?: number; max?: number; step?: number; action?: HistoryAction; }): [number | null, Updater<number | null>]; } interface StringBuilder { /** * Creates a non-nullable string state bound to a query param. * * @param defaultValue Required default value when the param is missing * @param opts Optional length constraints and history action * @returns A tuple `[value, setValue]` where value is never null */ (defaultValue: string, opts?: { minLength?: number; maxLength?: number; action?: HistoryAction; }): [string, Updater<string>]; /** * Creates a nullable string state bound to a query param. * * @param defaultValue Optional default value (defaults to null) * @param opts Optional length constraints and history action * @returns A tuple `[value, setValue]` where value can be null */ optional(defaultValue?: string | null, opts?: { minLength?: number; maxLength?: number; action?: HistoryAction; }): [string | null, Updater<string | null>]; } interface BooleanBuilder { /** * Creates a non-nullable boolean state bound to a query param. * * @param defaultValue Required default value when the param is missing * @param opts Optional history action * @returns A tuple `[value, setValue]` where value is never null */ (defaultValue: boolean, opts?: { action?: HistoryAction; }): [boolean, Updater<boolean>]; /** * Creates a nullable boolean state bound to a query param. * * @param defaultValue Optional default value (defaults to null) * @param opts Optional history action * @returns A tuple `[value, setValue]` where value can be null */ optional(defaultValue?: boolean | null, opts?: { action?: HistoryAction; }): [boolean | null, Updater<boolean | null>]; } interface DateBuilder { /** * Creates a non-nullable Date state bound to a query param. * * @param defaultValue Required default value when the param is missing/invalid * @param opts Optional min/max clamping and history action * @returns A tuple `[value, setValue]` where value is never null */ (defaultValue: Date, opts?: { min?: Date; max?: Date; action?: HistoryAction; }): [Date, Updater<Date>]; /** * Creates a nullable Date state bound to a query param. * * @param defaultValue Optional default value (defaults to null) * @param opts Optional min/max clamping and history action * @returns A tuple `[value, setValue]` where value can be null */ optional(defaultValue?: Date | null, opts?: { min?: Date; max?: Date; action?: HistoryAction; }): [Date | null, Updater<Date | null>]; } interface EnumBuilder<U extends string> { /** * Creates a non-nullable enum state bound to a query param. * * @param allowed Array of allowed string values * @param defaultValue Required default value when the param is not in allowed list * @param opts Optional history action * @returns A tuple `[value, setValue]` where value is never null */ (allowed: readonly U[], defaultValue: U, opts?: { action?: HistoryAction; }): [U, Updater<U>]; /** * Creates a nullable enum state bound to a query param. * * @param allowed Array of allowed string values * @param defaultValue Optional default value (defaults to null) * @param opts Optional history action * @returns A tuple `[value, setValue]` where value can be null */ optional(allowed: readonly U[], defaultValue?: U | null, opts?: { action?: HistoryAction; }): [U | null, Updater<U | null>]; } interface CustomBuilder<T> { /** * Creates a non-nullable custom state bound to a query param. * * @param defaultValue Required default value when parsing fails * @param parse Function parsing the raw string into T (return null on failure) * @param format Function formatting T into a string * @param opts Optional history action * @returns A tuple `[value, setValue]` where value is never null */ (defaultValue: T, parse: (raw: string) => T | null, format: (value: T) => string, opts?: { action?: HistoryAction; }): [T, Updater<T>]; /** * Creates a nullable custom state bound to a query param. * * @param defaultValue Optional default value (defaults to null) * @param parse Function parsing the raw string into T (return null on failure) * @param format Function formatting T into a string (null values result in empty string) * @param opts Optional history action * @returns A tuple `[value, setValue]` where value can be null */ optional(defaultValue: T | null, parse: (raw: string) => T | null, format: (value: T | null) => string, opts?: { action?: HistoryAction; }): [T | null, Updater<T | null>]; } interface JsonBuilder<T> { /** * Creates a non-nullable JSON state bound to a query param. * * @param defaultValue Required default value when parsing fails * @param opts Optional validation, omitEmpty, custom serializers, and history action * @returns A tuple `[value, setValue]` where value is never null */ (defaultValue: T, opts?: { validate?: (value: unknown) => value is T; omitEmpty?: (value: T) => boolean; stringify?: (value: T) => string; parse?: (raw: string) => unknown; action?: HistoryAction; }): [T, Updater<T>]; /** * Creates a nullable JSON state bound to a query param. * * @param defaultValue Optional default value (defaults to null) * @param opts Optional validation, omitEmpty, custom serializers, and history action * @returns A tuple `[value, setValue]` where value can be null */ optional(defaultValue?: T | null, opts?: { validate?: (value: unknown) => value is T; omitEmpty?: (value: T) => boolean; stringify?: (value: T) => string; parse?: (raw: string) => unknown; action?: HistoryAction; }): [T | null, Updater<T | null>]; } /** * Public API: returns builder methods for creating typed query‑state pairs. * * Pattern: * const [value, setValue] = useShareableState('key').number(123); // non-nullable * const [value, setValue] = useShareableState('key').number().optional(); // nullable * * Available builders: * - number(defaultValue): number (non-nullable) | number().optional(): number | null * - string(defaultValue): string (non-nullable) | string().optional(): string | null * - boolean(defaultValue): boolean (non-nullable) | boolean().optional(): boolean | null * - date(defaultValue): Date (non-nullable) | date().optional(): Date | null * - enum<U>(allowed, defaultValue): U (non-nullable) | enum<U>().optional(allowed): U | null * - custom<T>(defaultValue, parse, format): T (non-nullable) | custom<T>().optional(): T | null * - json<T>(defaultValue): T (non-nullable) | json<T>().optional(): T | null */ declare function useShareableState(key: string): { /** * Number state builder. Use .number(defaultValue) for non-nullable or .number().optional() for nullable. * * @example * const [count, setCount] = useShareableState('count').number(0); // non-nullable * const [optional, setOptional] = useShareableState('opt').number().optional(); // nullable */ readonly number: NumberBuilder; /** * String state builder. Use .string(defaultValue) for non-nullable or .string().optional() for nullable. * * @example * const [name, setName] = useShareableState('name').string(''); // non-nullable * const [optional, setOptional] = useShareableState('opt').string().optional(); // nullable */ readonly string: (defaultValue?: string, opts?: { minLength?: number; maxLength?: number; action?: HistoryAction; }) => [string, Updater<string>] | StringBuilder; /** * Boolean state builder. Use .boolean(defaultValue) for non-nullable or .boolean().optional() for nullable. * * @example * const [active, setActive] = useShareableState('active').boolean(false); // non-nullable * const [optional, setOptional] = useShareableState('opt').boolean().optional(); // nullable */ readonly boolean: BooleanBuilder; /** * Date state builder. Use .date(defaultValue) for non-nullable or .date().optional() for nullable. * * @example * const [start, setStart] = useShareableState('start').date(new Date()); // non-nullable * const [optional, setOptional] = useShareableState('opt').date().optional(); // nullable */ readonly date: (defaultValue?: Date, opts?: { min?: Date; max?: Date; action?: HistoryAction; }) => [Date, Updater<Date>] | DateBuilder; /** * Enum state builder. Binds a string literal union (enum-like) to a query param. * * @template U extends string * @example * type Theme = 'light' | 'dark'; * const [theme, setTheme] = useShareableState('theme').enum<Theme>(['light','dark'], 'light'); // non-nullable * const [optional, setOptional] = useShareableState('opt').enum<Theme>().optional(['light','dark']); // nullable */ readonly enum: <U extends string>(allowed?: readonly U[], defaultValue?: U, opts?: { action?: HistoryAction; }) => [U, Updater<U>] | EnumBuilder<U>; /** * Custom state builder. Provide your own parse/format functions. * * @template T * @example * const [ids, setIds] = useShareableState('ids').custom<number[]>([], parse, format); // non-nullable * const [optional, setOptional] = useShareableState('opt').custom<number[]>().optional(null, parse, format); // nullable */ readonly custom: <T>() => CustomBuilder<T>; /** * JSON state builder. Binds a JSON-serializable value to a query param. * * @template T * @example * const [data, setData] = useShareableState('data').json<{q: string}>({q: ''}); // non-nullable * const [optional, setOptional] = useShareableState('opt').json<{q: string}>().optional(); // nullable */ readonly json: <T>(defaultValue?: T, opts?: { validate?: (value: unknown) => value is T; omitEmpty?: (value: T) => boolean; stringify?: (value: T) => string; parse?: (raw: string) => unknown; action?: HistoryAction; }) => [T, Updater<T>] | JsonBuilder<T>; }; export { useShareableState };