Files
fredy/ui/src/hooks/useSearchParamState.js

70 lines
2.1 KiB
JavaScript
Raw Normal View History

2026-03-31 11:46:22 +02:00
/*
* Copyright (c) 2026 by Christian Kellner.
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
*/
import { useCallback } from 'react';
// Preset parsers for common types
export const parseString = {
parse: (v) => v,
stringify: (v) => v,
};
export const parseNumber = {
parse: (v) => Number(v),
stringify: (v) => String(v),
};
export const parseBoolean = {
parse: (v) => v === 'true',
stringify: (v) => String(v),
};
// For state that is null | true | false
export const parseNullableBoolean = {
parse: (v) => (v === 'true' ? true : v === 'false' ? false : null),
stringify: (v) => (v === null ? null : String(v)),
};
/**
* Drop-in replacement for useState that syncs with URL search params.
* Uses replace: true so filter changes don't add browser history entries.
*
* Requires a shared [searchParams, setSearchParams] pair from a single
* useSearchParams() call in the component. This ensures multiple hooks
* in the same component don't overwrite each other's params.
*
* @param {[URLSearchParams, Function]} searchParamsPair - from useSearchParams()
* @param {string} key - URL search param key
* @param {*} defaultValue - value when param is absent
* @param {{ parse: (s: string) => *, stringify: (v: *) => string|null }} [options]
*/
export function useSearchParamState([searchParams, setSearchParams], key, defaultValue, options = {}) {
const { parse = (v) => v, stringify = (v) => String(v) } = options;
const rawValue = searchParams.get(key);
const value = rawValue !== null ? parse(rawValue) : defaultValue;
const setValue = useCallback(
(newValue) => {
setSearchParams(
(prev) => {
const next = new URLSearchParams(prev);
const serialized = stringify(newValue);
if (newValue === defaultValue || newValue === null || newValue === undefined || serialized === null) {
next.delete(key);
} else {
next.set(key, serialized);
}
return next;
},
{ replace: true },
);
},
[key, defaultValue, stringify],
);
return [value, setValue];
}