Add 600ms debounce to search in usePaginatedList

- Typing updates the input immediately but delays the API call
- Table keeps showing current results until the search settles
- Resets to page 1 when search changes
- Applies to all list pages using the shared hook
This commit is contained in:
Adam Moussa 2026-04-16 19:16:04 -04:00
parent 9dec7cb448
commit 3a13d94b71

View file

@ -1,27 +1,38 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
export default function usePaginatedList(fetchFn, extraParams = {}) {
const [rows, setRows] = useState([]);
const [search, setSearch] = useState("");
const [debouncedSearch, setDebouncedSearch] = useState("");
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(12);
const [totalCount, setTotalCount] = useState(0);
const [loading, setLoading] = useState(true);
const [err, setErr] = useState("");
const debounceRef = useRef(null);
const extraKey = JSON.stringify(extraParams);
useEffect(() => {
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
setDebouncedSearch(search);
setPage(1);
}, 600);
return () => clearTimeout(debounceRef.current);
}, [search]);
const reload = useCallback(() => {
setLoading(true);
setErr("");
fetchFn({ search, page, pageSize, ...extraParams })
fetchFn({ search: debouncedSearch, page, pageSize, ...extraParams })
.then(({ rows, totalCount }) => {
setRows(rows);
setTotalCount(totalCount);
})
.catch((e) => setErr(e.message || "Failed to load"))
.finally(() => setLoading(false));
}, [fetchFn, search, page, pageSize, extraKey]);
}, [fetchFn, debouncedSearch, page, pageSize, extraKey]);
useEffect(() => { reload(); }, [reload]);