React Router используется для управления навигацией в приложениях на React, а Redux — для централизованного хранения состояния. Совместное использование этих библиотек позволяет строить сложные интерфейсы с синхронизированным состоянием URL и состояния приложения.
Подключение React Router к Redux осуществляется
через библиотеку connected-react-router, которая
обеспечивает синхронизацию истории браузера с хранилищем Redux. Для
этого создается объект истории:
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
Далее при конфигурации Redux хранилища используется
routerMiddleware и connectRouter:
import { applyMiddleware, createStore, combineReducers } from 'redux';
import { routerMiddleware, connectRouter } from 'connected-react-router';
import { history } from './history';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(
combineReducers({
router: connectRouter(history),
...rootReducer
}),
applyMiddleware(
routerMiddleware(history),
thunk
)
);
export default store;
Этот подход позволяет следить за изменениями URL через Redux и управлять навигацией с помощью действий.
<Route> и <Switch> в связке с
ReduxКомпоненты React Router, такие как <Route> и
<Switch>, могут получать данные из Redux через
connect или useSelector. Например, условный
рендеринг страниц в зависимости от состояния:
import { useSelector } from 'react-redux';
import { Route, Switch, Redirect } from 'react-router-dom';
import HomePage from './HomePage';
import Dashboard from './Dashboard';
function AppRoutes() {
const isAuthenticated = useSelector(state => state.auth.isAuthenticated);
return (
<Switch>
<Route exact path="/" component={HomePage} />
<Route
path="/dashboard"
render={() =>
isAuthenticated ? <Dashboard /> : <Redirect to="/" />
}
/>
</Switch>
);
}
Ключевой момент: использование render
вместо component позволяет передавать данные из Redux
напрямую в маршрут.
Иногда нужно, чтобы навигация управлялась исключительно через Redux. Для этого создаются специальные действия:
import { push } from 'connected-react-router';
export const navigateTo = (path) => (dispatch) => {
dispatch(push(path));
};
Таким образом, любые изменения маршрута проходят через Redux, что упрощает логику тестирования и отслеживания переходов. Этот подход особенно полезен в сложных приложениях, где URL должен отражать состояние приложения, а состояние приложения должно влиять на URL.
Часто требуется сохранять в URL параметры формы или фильтров. Комбинация Redux и React Router позволяет это сделать следующим образом:
import { useSelector, useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { push } from 'connected-react-router';
function FilterComponent() {
const dispatch = useDispatch();
const location = useLocation();
const filters = useSelector(state => state.filters);
const updateFilter = (newFilter) => {
dispatch({ type: 'UPDATE_FILTER', payload: newFilter });
const searchParams = new URLSearchParams(location.search);
searchParams.set('filter', newFilter);
dispatch(push(`${location.pathname}?${searchParams.toString()}`));
};
return (
<input
value={filters.current}
onCha nge={(e) => updateFilter(e.target.value)}
/>
);
}
Выделение: URL и Redux-хранилище синхронизируются в реальном времени, что упрощает работу с закладками и историей браузера.
Для улучшения производительности большие страницы загружаются динамически. Redux и React Router совместно поддерживают динамическое подключение редьюсеров и маршрутов:
import { Suspense, lazy } from 'react';
import { Route, Switch } from 'react-router-dom';
const Dashboard = lazy(() => import('./Dashboard'));
const Reports = lazy(() => import('./Reports'));
function AppRoutes() {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/reports" component={Reports} />
</Switch>
</Suspense>
);
}
Если редьюсеры динамически подключаются при загрузке модуля, это позволяет разгружать основной store и хранить только актуальные данные.
Для крупных приложений рекомендуется разделять Redux-состояние по маршрутам. Каждый маршрут имеет собственный slice, который подключается только при активации страницы:
import { combineReducers } from 'redux';
import dashboardReducer from './dashboardReducer';
import reportsReducer from './reportsReducer';
export const createRootReducer = (injectedReducers = {}) =>
combineReducers({
...injectedReducers
});
При переходе на маршрут /dashboard подключается
dashboardReducer, при переходе на /reports —
reportsReducer. Это улучшает
масштабируемость и уменьшает размер глобального
состояния.
Middleware позволяют перехватывать действия Redux для реализации сложной логики навигации. Пример: редирект после успешного логина:
const authMiddleware = ({ dispatch }) => next => action => {
if (action.type === 'LOGIN_SUCCESS') {
dispatch(push('/dashboard'));
}
return next(action);
};
Особенность: этот подход централизует логику навигации, избавляя компоненты от необходимости напрямую управлять маршрутами.
connected-react-router для синхронизации
истории с Redux.render и
Redirect.push,
replace).Эти подходы создают единый поток управления состоянием и навигацией, упрощают тестирование и делают приложение предсказуемым и расширяемым.