Стан URL у React без помилок синхронізації
Зробіть фільтри, сортування й пагінацію доступними за посиланням: зберігайте їх в URL і визначте правила читання та оновлення.
Користувач фільтрує велику таблицю, відкриває запис, а потім натискає «Назад». Замість попереднього фільтра застосунок виходить зі сторінки — або показує URL, який уже не відповідає видимим результатам. Зазвичай це трапляється, коли той самий стан зберігається в кількох місцях: у стані компонента, кеші запитів і параметрах URL, причому кожне оновлюється трохи по-різному.
Для стану, який має переживати перезавантаження, передаватися посиланням або реагувати на кнопки «Назад» і «Вперед», URL — не просто деталь навігації. Це стійкий інтерфейс між користувачем, браузером і застосунком. Важливо визначити, який саме стан належить зберігати там, і встановити передбачуваний спосіб його читання та зміни.
Зберігайте тривалий стан подання в URL
Пошуковий запит, фільтри, порядок сортування, пагінація й вибрана вкладка часто впливають на вміст сторінки. Користувачі цілком очікують, що зможуть скопіювати посилання й пізніше побачити те саме подання. Такі значення добре підходять для параметрів запиту. Тимчасові деталі інтерфейсу — відкрита підказка, поле у фокусі чи незбережена чернетка — зазвичай туди не належать.
- Стан URL: параметри подання, якими можна поділитися та які зберігаються після перезавантаження, наприклад `?status=open&page=2`.
- Локальний стан інтерфейсу: тимчасові деталі взаємодії, які мають зникати після завершення роботи компонента чи дії.
- Стан сервера: дані, отримані від API; їхню актуальність і кешування потрібно контролювати окремо.
Розділення цих категорій допомагає не перетворити URL на другу копію всього стану застосунку. Воно також добре поєднується з явним моделюванням інтерфейсу: машини станів у React допомагають описати допустимі стани інтерфейсу, а URL може задавати сталі параметри, які визначають вибране подання.
Перетворюйте параметри на перевірену модель подання
Параметри запиту — це вхідні дані, яким не можна довіряти. Посилання може містити невідомий ключ сортування, від’ємний номер сторінки або повторені значення там, де застосунок очікує одне. Зчитуйте параметри на межі маршруту, задавайте значення за замовчуванням і нормалізуйте некоректні значення до того, як вони потраплять у рендеринг чи запит до API.
type Sort = "name" | "created";
type TableView = {
page: number;
status: "all" | "open" | "closed";
sort: Sort;
};
function parseTableView(search: string): TableView {
const params = new URLSearchParams(search);
const page = Number(params.get("page"));
const status = params.get("status");
const sort = params.get("sort");
return {
page: Number.isInteger(page) && page > 0 ? page : 1,
status: status === "open" || status === "closed" ? status : "all",
sort: sort === "created" ? "created" : "name",
};
}Так решта сторінки отримує невелику типізовану модель замість повторних перевірок рядків. Тримайте розбір і серіалізацію поруч та перевіряйте значення за замовчуванням, некоректні дані й повторені параметри. У Next.js або іншому фреймворку маршрутизації, де це доречно, використовуйте його API навігації. Головний принцип той самий: адаптер фреймворку має зберігати цю межу, а не розносити розбір URL по компонентах.
Оновлюйте параметр, не стираючи решту
Поширена помилка — щоразу повністю перебудовувати рядок запиту, коли змінюється один елемент керування. Оновлення фільтра за статусом випадково стирає сортування, а зміна сторінки прибирає не пов’язаний із нею параметр кампанії. Починайте з поточних параметрів, змінюйте лише ключі, за які відповідає функція, і вилучайте значення за замовчуванням, якщо це дає коротший канонічний URL.
function updateQuery(updates: Record<string, string | null>) {
const url = new URL(window.location.href);
for (const [key, value] of Object.entries(updates)) {
if (value === null) url.searchParams.delete(key);
else url.searchParams.set(key, value);
}
window.history.pushState(null, "", url);
window.dispatchEvent(new PopStateEvent("popstate"));
}Цей приклад показує правило оновлення, але у виробничому коді потрібно дотримуватися контракту маршрутизатора. Фреймворку може знадобитися власний метод навігації, щоб запустити повторний рендеринг, завантаження даних або потрібну поведінку прокручування. Не змішуйте прямі виклики History API з навігацією через маршрутизатор для одного стану, якщо така взаємодія не задумана й не перевірена.
Узгоджуйте історію браузера з наміром користувача
Не кожна зміна URL має створювати новий запис в історії. Якщо введення пошукового запиту додає запис на кожен символ, кнопкою «Назад» стане неможливо користуватися. Для пошуку з відкладеним оновленням можна замінювати поточний запис, поки користувач вводить текст, а після явного підтвердження створити окрему контрольну точку навігації. Зміни фільтрів і сторінок часто варто додавати в історію: користувач може захотіти повернутися до попереднього вибору.
- Використовуйте push, коли зміна є важливим кроком, який користувач може захотіти скасувати кнопкою «Назад».
- Використовуйте replace для проміжних або нормалізованих значень, які не мають створювати зайвих записів історії.
- Оновлюючи URL, зберігайте не пов’язані з функцією параметри й не скидайте прокручування, якщо цього не потребує взаємодія.
Правильна політика залежить від екрана, а не від універсального правила. Перевірте реальну послідовність дій: встановити фільтр, перейти на іншу сторінку, відкрити деталі й повернутися. Історія браузера має відновлювати узгоджене подання, а не лише рядок.
Узгодьте URL, елементи керування та запит до API
Коли URL змінюється, елементи керування й результати мають походити з тієї самої розібраної моделі подання. Не зберігайте окрему копію фільтрів у стані компонента, якщо на це немає чіткої причини — наприклад, незафіксованої чернетки форми. Для вже застосованих фільтрів використовуйте розібрану модель URL і для відображення елементів керування, і для формування запиту. Так менша ймовірність, що запит піде з новими фільтрами, а елементи керування показуватимуть старі.
URL не замінює кеш даних сервера. Бібліотека запитів однаково потребує ключа кешу з усіма параметрами, що впливають на відповідь, наприклад статусом, сортуванням і сторінкою. Так різні подання не використовуватимуть чужий результат. Після мутацій використовуйте цільове оновлення кешу або інвалідацію, а не запускайте широке повторне завантаження через кожну зміну URL. Цей принцип докладніше розглянуто в матеріалі про інвалідацію кешу React Query.
Практичний список для перевірки
- Визначте, які параметри подання мають зберігатися після перезавантаження, передавання посилання й навігації браузером.
- Задайте допустимі значення та значення за замовчуванням для кожного параметра запиту.
- Розбирайте й перевіряйте параметри один раз — на межі маршруту.
- Оновлюйте лише ті параметри, за які відповідає функція, зберігаючи решту.
- Виберіть push або replace відповідно до того, чи має кнопка «Назад» повертати попереднє значення.
- Переконайтеся, що елементи керування, запити й ключі кешу використовують ту саму нормалізовану модель подання.
Перевірте прямий перехід за URL із фільтром, перезавантаження, некоректні параметри, кнопки «Назад» і «Вперед», а також посилання, скопійовані зі сторінки. Перевірте й порожні результати та зміну розміру сторінки: якщо поточний номер сторінки стає недійсним, вирішіть, чи потрібно обмежити його, чи повернутися на першу сторінку, а тоді узгоджено оновіть URL і результати.
Коли URL не підходить для зберігання стану
Не додавайте в адресний рядок кожну взаємодію. Видимість пароля, стан наведення курсора чи незавершена багатокрокова форма можуть зробити URL захаращеним або розкрити зайві дані, не покращивши досвід. Стан URL доречний, коли подання має відтворюватися, підтримувати навігацію або ним потрібно ділитися. Для іншого стану використовуйте власника з найменшою областю видимості, яка відповідає тривалості цього стану.
У складних застосунках така невелика межа полегшує роботу з глибокими посиланнями, навігацією браузера та завантаженням даних у міру розвитку функцій. WebMriya розробляє й модернізує подібні інтерфейси на React у межах послуги зі створення складних вебзастосунків.
Поширені запитання
Чи варто зберігати кожен фільтр в URL?
Ні. Додавайте фільтри в URL, якщо користувачам потрібно перезавантажувати сторінку, ділитися посиланням або повертатися до того самого подання. Тимчасові чи ще не застосовані значення залишайте локально.
Для зміни фільтрів використовувати pushState чи replaceState?
Використовуйте push, якщо кнопка «Назад» має повертати до важливого вибору, наприклад застосованого фільтра чи зміни сторінки. Використовуйте replace для проміжних редагувань, наприклад кожного символу в полі пошуку.
Чи замінює стан URL кеш React Query?
Ні. URL визначає параметри подання, а кеш зберігає відповіді сервера. Якщо параметри, отримані з URL, впливають на відповідь, додайте їх нормалізовані значення до ключа запиту.