Back

Руководство по Middleware в React Router для начинающих

Руководство по Middleware в React Router для начинающих

Если вам когда-либо требовалось проверять аутентификацию перед каждым защищённым маршрутом, логировать запросы во всём приложении или передавать данные между родительскими и дочерними маршрутами в React Router, вы, скорее всего, писали один и тот же код множество раз. Middleware в React Router решает эту проблему дублирования, позволяя перехватывать и обрабатывать запросы до того, как они достигнут ваших loader’ов и action’ов.

Это руководство охватывает всё необходимое для реализации middleware в React Router версии 7.9+, от включения флага v8_middleware до создания вашего первого middleware для аутентификации с использованием нового API middleware.

Ключевые выводы

  • React Router 7 представляет middleware для обработки сквозных задач, таких как аутентификация и логирование
  • Middleware выполняется последовательно во вложенной цепочке, обеспечивая эффективный обмен данными между маршрутами
  • Новый context API обеспечивает типобезопасный обмен данными без конфликтов имён
  • Поддерживаются как серверные, так и клиентские middleware с различным поведением выполнения

Что такое Middleware в React Router?

Middleware в React Router 7 — это функция, которая выполняет код до и после выполнения обработчиков маршрутов. Представьте это как конвейер обработки, где каждый middleware может проверять запросы, добавлять данные в контекст или даже прерывать запрос с помощью редиректа.

В отличие от параллельного выполнения loader’ов в React Router 6, middleware выполняется последовательно во вложенной цепочке. Родительский middleware выполняется перед дочерним при движении вниз, а затем в обратном порядке при движении вверх после генерации ответа.

Parent middleware → Child middleware → Route handler → Child middleware → Parent middleware

Такое последовательное выполнение позволяет реализовать паттерны, которые ранее были невозможны, например, передачу данных аутентифицированного пользователя от родительского маршрута всем дочерним маршрутам без избыточных обращений к базе данных.

Включение Middleware в вашем проекте

Чтобы начать использовать middleware, сначала убедитесь, что вы используете React Router версии 7.9.0 или новее (или 7.3.0+ с включённым флагом middleware). Затем включите флаг функции в вашей конфигурации:

// react-router.config.ts
import type { Config } from "@react-router/dev/config";

export default {
  future: {
    v8_middleware: true,
  },
} satisfies Config;

Этот флаг включает стабильную поддержку middleware и новый context API. Параметр context в ваших loader’ах и action’ах теперь предоставляет доступ к данным, установленным middleware, через типобезопасный API.

Понимание Context API

Новый context API в React Router 7 предоставляет типобезопасный интерфейс, похожий на Map, для обмена данными между middleware и обработчиками маршрутов. Вместо прямого добавления свойств к объекту контекста, вы теперь используете context.set() и context.get() с типизированными ключами контекста:

import { createContext } from "react-router";

// Создание типизированного ключа контекста
const userContext = createContext<User>();

// В middleware
context.set(userContext, user);

// В loader/action
const user = context.get(userContext); // Типобезопасный объект User

Такой подход исключает необходимость в утверждениях типов TypeScript и предотвращает конфликты имён между различными middleware.

Создание вашего первого Middleware

Давайте создадим middleware для аутентификации, который защищает маршруты и передаёт данные пользователя:

// app/middleware/auth.ts
import { redirect, createContext } from "react-router";
import type { Route } from "./+types/root";

export const userContext = createContext<User | null>();

export const authMiddleware: Route.Middleware = async ({ 
  request, 
  context,
  next
}) => {
  const user = await getUserFromSession(request);
  
  if (!user) {
    throw redirect("/login");
  }
  
  context.set(userContext, user);
  return next();
};

Примените этот middleware к защищённым маршрутам:

// app/routes/dashboard.tsx
import { authMiddleware, userContext } from "~/middleware/auth";

export  const  middleware: Route.MiddlewareFunction[] = [authMiddleware];

export async function loader({ context }: Route.LoaderArgs) {
  const user = context.get(userContext); // Гарантированно существует
  const profile = await getProfile(user.id);
  return { profile };
}

Middleware выполняется перед loader’ом, обеспечивая аутентифицированный доступ и предоставляя объект пользователя без дополнительных запросов к базе данных.

Серверный Middleware vs Клиентский Middleware

React Router поддерживает как серверный, так и клиентский middleware с немного различным поведением:

Серверный middleware выполняется на сервере и должен возвращать Response:

export const middleware: Route.MiddlewareFunction[] = [
  async ({ request }, next) => {
    console.log(`Server: ${request.method} ${request.url}`);
    const response = await next();
    response.headers.set("X-Custom-Header", "value");
    return response; // Обязательно
  };
];

Клиентский middleware выполняется в браузере во время клиентской навигации:

export const middleware: Route.ClientMiddlewareFunction[] = [
  async ({ next }) => {
    const start = performance.now();
    await next();
    console.log(`Navigation took ${performance.now() - start}ms`);
  },
];

Распространённые паттерны Middleware

Middleware для логирования

export const loggingMiddleware: Route.Middleware = async ({ request, next }) => {
  const requestId = crypto.randomUUID();
  console.log(`[${requestId}] ${request.method} ${request.url}`);
  
  const response = await next();
  
  console.log(`[${requestId}] Response ${response.status}`);
  return response;
};

Композиция нескольких Middleware

Вы можете объединять несколько функций middleware в цепочку:

// app/routes/admin.tsx
export const middleware = [
  loggingMiddleware,
  authMiddleware,
  adminRoleMiddleware
];

Каждый middleware выполняется по порядку, и любой из них может прервать цепочку, выбросив редирект или ответ с ошибкой.

Заключение

Middleware в React Router преобразует способ обработки сквозных задач в вашем приложении. Включив флаг v8_middleware и приняв новый context API, вы можете устранить дублирование кода, эффективно обмениваться данными между маршрутами и реализовывать аутентификацию, логирование и другие паттерны чистым и переиспользуемым способом.

Начните с простых middleware, таких как аутентификация или логирование, а затем исследуйте продвинутые паттерны по мере роста вашего приложения. Модель последовательного выполнения и типобезопасный context API делают middleware мощным дополнением к вашему инструментарию React Router.

Часто задаваемые вопросы

Нет, middleware — это новая функция, представленная за флагом в React Router 7.3 и стабилизированная в версии 7.9 через `future.v8_middleware`. В более ранних версиях требуется реализация аналогичной функциональности через компоненты-обёртки или пользовательскую логику loader'ов.

Middleware может улучшить производительность за счёт сокращения избыточных операций. Поскольку middleware выполняется последовательно и может обмениваться данными через контекст, вы избегаете дублирующих запросов к базе данных или API-вызовов, которые могут возникать при параллельных loader'ах.

Когда middleware выбрасывает ошибку, React Router обрабатывает её так же, как любую другую ошибку маршрута. Ошибка всплывает до ближайшей границы ошибок, и последующие middleware в цепочке не будут выполнены.

Middleware не может напрямую изменить объект запроса, но может добавить данные в контекст, к которым могут получить доступ loader'ы и action'ы. Для модификации запроса создайте новый объект Request и передайте его следующему middleware.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay