Стан каталогу (фільтри, пошук, сортування, сторінка) у useState зникає при оновленні сторінки, не працює з кнопкою «Назад» і не передається посиланням. URL - природне місце для такого стану.
Що дає стан в URL:
- посилання відтворює екран: «ось вакансії PHP у Києві, віддалено» - одне посилання;
- «Назад» і «Вперед» повертають попередні фільтри;
- оновлення сторінки нічого не губить;
- серверний рендер і SEO: сервер бачить параметри й рендерить правильний вміст;
- аналітика бачить, як саме користуються фільтрами.
З React Router:
import { useSearchParams } from 'react-router';
function Vacancies() {
const [searchParams, setSearchParams] = useSearchParams();
const city = searchParams.get('city') ?? 'all';
const page = Number(searchParams.get('page') ?? 1);
function changeCity(nextCity) {
setSearchParams((params) => {
params.set('city', nextCity);
params.delete('page'); // новий фільтр - перша сторінка
return params;
});
}
// ...
}
Значення читаються з URL під час рендеру - окремий useState для них не потрібен. URL і є джерелом правди.
Без роутера - URLSearchParams + history.replaceState/pushState і підписка на popstate (через useSyncExternalStore).
Що варто врахувати:
pushчиreplace: зміна фільтра - новий запис історії (користувач повертається «Назад» до попередніх фільтрів); введення в поле пошуку -replace, щоб не засмічувати історію кожною літерою;- debounce для полів введення: не змінювати URL на кожне натискання;
- валідація: параметри з URL - дані від користувача. Невідоме значення сортування чи від'ємна сторінка мають оброблятися без падіння;
- типи: усе в URL - рядки; числа й булеві значення треба перетворювати (бібліотеки на кшталт
nuqsроблять це з типами); - короткі й стабільні назви параметрів - вони стають частиною публічних посилань.
Що НЕ варто тримати в URL: тимчасовий стан інтерфейсу (відкрите меню, наведення), персональні дані, великі обсяги даних.
Для Laravel-бекенду формат на кшталт tags[]=php&tags[]=vue зручний: сервер отримає масив.