Рефакторинг render props

Render props — это паттерн, позволяющий передавать функцию как дочерний элемент компонента, предоставляя полный контроль над тем, что будет отрендерено в зависимости от состояния маршрутизации. В контексте React Router render props традиционно использовались для условного рендеринга компонентов на основе текущего маршрута, параметров URL или состояния навигации.

Пример базового использования render props с компонентом <Route>:

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

<Route path="/profile" render={({ match, location, history }) => (
  <Profile match={match} location={location} history={history} />
)} />

Ключевые моменты:

  • match содержит информацию о совпадении маршрута, включая параметры URL.
  • location хранит текущий адрес, search-параметры и hash.
  • history позволяет программно управлять навигацией (push, replace, goBack и т.д.).

Преимущества render props

  1. Гибкость рендеринга: позволяет создавать динамические компоненты в зависимости от состояния маршрута.
  2. Изоляция логики маршрутизации: можно передавать match, location и history непосредственно в компоненты, не завязываясь на глобальный контекст.
  3. Удобство для условного рендеринга: можно комбинировать с логикой авторизации, проверками прав и другими условиями до отрисовки компонента.

Ограничения render props

  • Увеличение вложенности JSX при использовании нескольких <Route> с render props.
  • Возможность повторного рендера при изменении props или состояния, что может повлиять на производительность при больших приложениях.
  • Сложность композиции нескольких render props внутри одного маршрута.

Переход к современному синтаксису: компонент <Routes> и хук useNavigate

Начиная с React Router v6, render props устарели, а основной подход изменился на декларативный. Вместо render и component используется <Routes> с вложенными <Route> и хуки для доступа к истории и параметрам маршрута.

Пример рефакторинга старого render props кода:

Старый синтаксис:

<Route path="/profile/:id" render={({ match }) => (
  <Profile id={match.params.id} />
)} />

Новый синтаксис:

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

function ProfileWrapper() {
  const { id } = useParams();
  return <Profile id={id} />;
}

<Routes>
  <Route path="/profile/:id" element={<ProfileWrapper />} />
</Routes>

Преимущества нового подхода:

  • Прозрачность и декларативность: <Routes> и <Route> позволяют легко читать структуру маршрутов.
  • Упрощение передачи параметров: хуки useParams, useLocation, useNavigate позволяют избавиться от render props и явного пропс-дриллинга.
  • Меньше лишних рендеров: каждый компонент получает только нужные данные через хук, что повышает производительность.

Использование хуков для замены render props

  1. useParams() — получение динамических параметров маршрута.
  2. useNavigate() — программная навигация без необходимости обращаться к history через props.
  3. useLocation() — доступ к текущему URL, search-параметрам и hash.
  4. useMatch(pattern) — проверка совпадения маршрута с конкретным паттерном.

Пример замены render props с несколькими условиями:

Старый вариант:

<Route path="/dashboard" render={({ history }) => (
  isAuthenticated ? <Dashboard history={history} /> : <Redirect to="/login" />
)} />

Новый вариант:

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

function DashboardWrapper({ isAuthenticated }) {
  const navigate = useNavigate();
  if (!isAuthenticated) return <Navigate to="/login" />;
  return <Dashboard navigate={navigate} />;
}

<Routes>
  <Route path="/dashboard" element={<DashboardWrapper isAuthenticated={isAuthenticated} />} />
</Routes>

Стратегии рефакторинга существующего кода

  1. Определить компоненты, использующие render props: найти все <Route> с атрибутом render.
  2. Создать обертки (Wrapper Components): переместить логику из функции render props в отдельный компонент.
  3. Заменить render props на element и хуки: использовать <Routes> и <Route> с новым API.
  4. Удалить повторяющийся код: избавиться от передачи match, location, history через props.
  5. Тестирование маршрутов: убедиться, что поведение и переходы работают корректно после рефакторинга.

Практические советы

  • Для больших проектов лучше сразу перейти на <Routes> с хуками, чтобы упростить поддержку.
  • Render props можно временно использовать для сложных условий, но желательно планировать миграцию к современному синтаксису.
  • Комбинировать Navigate и useNavigate для обработки защищенных маршрутов и редиректов.
  • Разделять маршруты на отдельные модули для упрощения управления сложной структурой навигации.

Рефакторинг render props делает код более читаемым, современным и поддерживаемым, снижает вероятность ошибок при передаче параметров и повышает производительность приложения.