Files
bknd/app/src/ui/hooks/use-search.ts
dswbx e6ff5c3f0b fix pagination if endpoint's total is not available
when using a connection that has softscans disabled (e.g. D1) pagination failed. Fixing it by overfetching and slicing
2025-10-11 20:37:14 +02:00

56 lines
2.0 KiB
TypeScript

import { decodeSearch, encodeSearch, mergeObject, type s, parse } from "bknd/utils";
import { isEqual, transform } from "lodash-es";
import { useLocation, useSearch as useWouterSearch } from "wouter";
import { useEffect, useMemo, useState } from "react";
export type UseSearchOptions<Schema extends s.Schema = s.Schema> = {
defaultValue?: Partial<s.StaticCoerced<Schema>>;
beforeEncode?: (search: Partial<s.StaticCoerced<Schema>>) => object;
};
export function useSearch<Schema extends s.Schema = s.Schema>(
schema: Schema,
options?: UseSearchOptions<Schema>,
) {
const searchString = useWouterSearch();
const [location, navigate] = useLocation();
const defaults = useMemo(() => {
return mergeObject(
// @ts-ignore
schema.template({ withOptional: true }),
options?.defaultValue ?? {},
);
}, [JSON.stringify({ schema, dflt: options?.defaultValue })]);
const [value, setValue] = useState<s.StaticCoerced<Schema>>(defaults);
useEffect(() => {
const initial =
searchString.length > 0 ? decodeSearch(searchString) : (options?.defaultValue ?? {});
const v = parse(schema, Object.assign({}, defaults, initial)) as any;
setValue(v);
}, [searchString, JSON.stringify(options?.defaultValue), location]);
function set<Update extends Partial<s.StaticCoerced<Schema>>>(update: Update): void {
const search = getWithoutDefaults(Object.assign({}, value, update), defaults);
const prepared = options?.beforeEncode?.(search) ?? search;
const encoded = encodeSearch(prepared, { encode: false });
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
}
return {
value: value as Required<s.StaticCoerced<Schema>>,
set,
};
}
function getWithoutDefaults(value: object, defaultValue: object) {
return transform(
value as any,
(result, value, key) => {
if (defaultValue && isEqual(value, defaultValue[key])) return;
result[key] = value;
},
{} as object,
);
}