Ломающие изменения

React Router — это библиотека для управления маршрутизацией в приложениях на React. С каждой крупной версией появляются изменения, которые могут сломать существующий код. Важно понимать, какие изменения являются ломающими, как они влияют на структуру маршрутов и поведение навигации.


Отмена или замена Switch на Routes

В версиях до React Router 6 использовался компонент Switch для рендеринга первого совпадающего маршрута. В React Router 6 Switch удалён и заменён на Routes.

Прежний подход:

import { Switch, Route } from 'react-router-dom';

<Switch>
  <Route path="/about" component={About} />
  <Route path="/contact" component={Contact} />
</Switch>

Новый подход:

import { Routes, Route } from 'react-router-dom';

<Routes>
  <Route path="/about" element={<About />} />
  <Route path="/contact" element={<Contact />} />
</Routes>
  • component заменён на element, что требует оборачивания компонента в JSX (<About /> вместо About).
  • Компонент Routes обрабатывает маршруты иначе: теперь он рендерит строго один совпавший маршрут без дополнительных проверок.

Изменение работы useHistory на useNavigate

В React Router 5 использовался хук useHistory для программной навигации:

import { useHistory } from 'react-router-dom';

const history = useHistory();
history.push('/home');

В React Router 6 useHistory удалён, вместо него используется useNavigate:

import { useNavigate } from 'react-router-dom';

const navigate = useNavigate();
navigate('/home');
  • Методы push и replace заменены единым вызовом navigate(path, { replace: true }).
  • Это изменение влияет на все компоненты, которые выполняют навигацию через историю, включая обработчики событий.

Удаление exact и изменение совпадения путей

Ранее для точного совпадения пути использовался атрибут exact:

<Route exact path="/home" component={Home} />

Теперь exact не нужен, так как маршруты в React Router 6 по умолчанию используют точное совпадение. Более длинные пути, включающие динамические сегменты, проверяются через “паттерны маршрута”.

<Route path="/home" element={<Home />} />
  • Это ломает приложения, где разработчики рассчитывали на частичное совпадение без явного exact.
  • Необходимо проверять маршруты с параметрами (:id) и вложенные маршруты.

Динамические маршруты и useParams

В React Router 5 динамические сегменты определялись как :id, и доступ к ним осуществлялся через match.params:

<Route path="/user/:id" component={User} />

const User = ({ match }) => {
  const { id } = match.params;
};

В React Router 6 используется хук useParams:

import { useParams } from 'react-router-dom';

const User = () => {
  const { id } = useParams();
};
  • Прямой доступ через match больше невозможен.
  • Любые функции и компоненты, которые полагались на props.match, требуют переписывания.

Вложенные маршруты и Outlet

В React Router 5 вложенные маршруты определялись через вложенные компоненты Route внутри родительского компонента, что иногда приводило к сложной структуре.

<Route path="/dashboard">
  <Route path="stats" component={Stats} />
</Route>

В React Router 6 используется <Outlet> для рендеринга дочерних маршрутов:

<Route path="/dashboard" element={<Dashboard />}>
  <Route path="stats" element={<Stats />} />
</Route>

const Dashboard = () => {
  return (
    <div>
      <h1>Dashboard</h1>
      <Outlet />
    </div>
  );
};
  • Без <Outlet> дочерние маршруты не будут рендериться.
  • Изменение структуры вложенности требует пересмотра всех родительских компонентов и их JSX.

Обновление редиректов: RedirectNavigate

В React Router 5 редирект выполнялся через <Redirect>:

<Redirect to="/login" />

В React Router 6 используется <Navigate>:

import { Navigate } from 'react-router-dom';

<Navigate to="/login" />
  • replace теперь передаётся как пропс: <Navigate to="/login" replace />.
  • Любой редирект, основанный на условных рендерах, требует переписывания компонентов.

Изменение контекста маршрутизатора

React Router 6 изменил внутренний контекст Router, что влияет на интеграции с библиотеками, использующими withRouter или доступ к context.router.

  • withRouter удалён.
  • Для доступа к навигации, параметрам и локации необходимо использовать хуки useNavigate, useParams и useLocation.
  • Существующие HOC-обёртки, зависящие от контекста маршрутизатора, ломаются и требуют переписывания под хуки.

Изменения в API навигации с состоянием

В React Router 5 можно было передавать объект состояния через history.push:

history.push('/home', { from: 'login' });

В React Router 6:

navigate('/home', { state: { from: 'login' } });
  • Доступ к состоянию теперь через useLocation:
import { useLocation } from 'react-router-dom';

const location = useLocation();
console.log(location.state.from);
  • Любой код, который напрямую работал с объектом истории, необходимо обновить.

Поддержка маршрутов с базовым путём (basename)

Если приложение использует базовый путь (<BrowserRouter basename="/app">), структура вложенных маршрутов и редиректов изменилась:

  • Раньше относительные пути автоматически учитывались.
  • Теперь важно использовать абсолютные пути или корректно строить относительные маршруты через <Route path> и <Link to>.

Эти ломающие изменения требуют внимательной проверки существующего кода при обновлении до React Router 6. Они охватывают все ключевые аспекты: рендеринг маршрутов, навигацию, доступ к параметрам, вложенные маршруты и редиректы. Игнорирование этих изменений может привести к ошибкам рендера, невозможности навигации и некорректной обработке состояния приложения.