React Router предоставляет мощные возможности для управления навигацией в приложении, однако для сложных SPA часто требуется синхронизация URL с глобальным состоянием приложения. Это позволяет сохранять состояние между перезагрузками, делиться ссылками с определённым контекстом и управлять состоянием через историю браузера.
useLocation и useNavigate для
синхронизацииuseLocation предоставляет объект
location, содержащий текущий путь, поисковую строку и hash.
Это позволяет наблюдать за изменениями URL и использовать их для
обновления глобального состояния.
Пример:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useStore } from './store';
function URLSync() {
const location = useLocation();
const { setFilter } = useStore();
useEffect(() => {
const params = new URLSearchParams(location.search);
const filter = params.get('filter') || '';
setFilter(filter);
}, [location.search, setFilter]);
return null;
}
Здесь любое изменение поисковой строки в URL автоматически обновляет глобальное состояние.
useNavigate позволяет программно
изменять URL, что полезно при обновлении состояния через действия
пользователя:
import { useNavigate } from 'react-router-dom';
import { useStore } from './store';
function FilterComponent() {
const navigate = useNavigate();
const { filter } = useStore();
const updateFilter = (newFilter) => {
navigate(`?filter=${encodeURIComponent(newFilter)}`, { replace: true });
};
return (
updateFilter(e.target.value)}
placeholder="Введите фильтр"
/>
);
}
Для сложных приложений рекомендуется использовать глобальное состояние. Это позволяет единожды определить логику синхронизации URL и состояния и переиспользовать её во всех компонентах.
Пример с Redux:
import { createSlice } from '@reduxjs/toolkit';
const filterSlice = createSlice({
name: 'filter',
initialState: { value: '' },
reducers: {
setFilter: (state, action) => {
state.value = action.payload;
},
},
});
export const { setFilter } = filterSlice.actions;
export default filterSlice.reducer;
Компонент синхронизации:
import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { setFilter } from './filterSlice';
function URLSync() {
const location = useLocation();
const navigate = useNavigate();
const dispatch = useDispatch();
const filter = useSelector((state) => state.filter.value);
useEffect(() => {
const params = new URLSearchParams(location.search);
const urlFilter = params.get('filter') || '';
if (urlFilter !== filter) {
dispatch(setFilter(urlFilter));
}
}, [location.search, dispatch, filter]);
useEffect(() => {
const params = new URLSearchParams(location.search);
if (filter !== params.get('filter')) {
params.set('filter', filter);
navigate(`?${params.toString()}`, { replace: true });
}
}, [filter, location.search, navigate]);
return null;
}
Такой подход обеспечивает двустороннюю синхронизацию, предотвращая рассинхронизацию состояния и URL.
searchParams для более чистого кодаReact Router v6 предоставляет хук useSearchParams,
который упрощает работу с параметрами запроса:
import { useSearchParams } from 'react-router-dom';
import { useStore } from './store';
function FilterSearch() {
const [searchParams, setSearchParams] = useSearchParams();
const { filter, setFilter } = useStore();
useEffect(() => {
const param = searchParams.get('filter') || '';
if (param !== filter) setFilter(param);
}, [searchParams, filter, setFilter]);
const handleChange = (value) => {
setFilter(value);
setSearchParams({ filter: value });
};
return handleChange(e.target.value)} />;
}
Преимущество useSearchParams в том, что нет
необходимости вручную формировать URL, React Router автоматически
поддерживает актуальность строки запроса.
replace при обновлении
URL: это предотвращает засорение истории браузера
промежуточными состояниями.encodeURIComponent.setSearchParams({ data: encodeURIComponent(JSON.stringify(state)) });
Использование useEffect с зависимостями от URL и
состояния обеспечивает реактивную синхронизацию, когда
изменения в одном источнике автоматически отражаются в другом. Это ключ
к поддержке консистентного состояния приложения,
особенно при совместном использовании с Redux или Zustand.
useEffect могут привести к непредсказуемым
обновлениям.URLSearchParams, query-string или встроенные
методы React Router упрощают чтение и запись параметров.