Передача контекста в action

Библиотека Universal Router построена вокруг концепции маршрутов, каждый из которых может иметь собственное действие (action). Одним из ключевых механизмов является возможность передачи контекста, который может содержать любые данные, необходимые для обработки запроса или рендеринга страницы.

Структура контекста

Контекст — это объект, который передаётся в router.resolve(context). Он может содержать:

  • Параметры запроса (params)
  • Query-параметры (query)
  • Состояние приложения или пользовательские данные (user, auth, store)
  • Ссылки на вспомогательные функции (fetchData, redirect, navigate)

Пример структуры контекста:

const context = {
  pathname: '/profile/123',
  params: {},
  query: { ref: 'dashboard' },
  user: { id: 42, name: 'Alice' },
  fetchData: async (url) => await fetch(url).then(res => res.json())
};

Передача контекста в маршруты

Каждый маршрут в Universal Router описывается объектом с полями path, action и, при необходимости, children. Контекст передаётся в action автоматически при вызове router.resolve(context).

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/profile/:id',
    action: async (context) => {
      const userId = context.params.id;
      const userData = await context.fetchData(`/api/users/${userId}`);
      return { page: 'Profile', data: userData };
    }
  }
];

const router = new UniversalRouter(routes);

const context = {
  pathname: '/profile/123',
  params: {},
  fetchData: async (url) => await fetch(url).then(res => res.json())
};

router.resolve(context).then(result => {
  console.log(result);
});

В этом примере context передаёт функцию fetchData, которая используется в action для загрузки данных пользователя. Параметры маршрута автоматически подставляются в context.params.

Динамическое добавление данных в контекст

Контекст можно модифицировать на лету перед вызовом router.resolve(). Это позволяет использовать один и тот же маршрут с разными данными или функциями:

const contextBase = {
  pathname: '/profile/123',
  fetchData: async (url) => await fetch(url).then(res => res.json())
};

const contextWithAuth = {
  ...contextBase,
  user: { id: 42, name: 'Alice', role: 'admin' }
};

router.resolve(contextWithAuth);

Такой подход позволяет делать маршруты универсальными и легко расширяемыми.

Вложенные маршруты и наследование контекста

Universal Router поддерживает вложенные маршруты (children). Контекст автоматически передаётся вниз по иерархии, что позволяет дочерним маршрутам получать доступ ко всем данным, переданным из родителя.

const routes = [
  {
    path: '/dashboard',
    action: (context) => ({ page: 'Dashboard' }),
    children: [
      {
        path: 'stats',
        action: (context) => {
          console.log(context.user); // доступ к user из родительского контекста
          return { page: 'Stats' };
        }
      }
    ]
  }
];

Асинхронные действия и контекст

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

const routes = [
  {
    path: '/posts/:id',
    action: async ({ params, fetchData }) => {
      const post = await fetchData(`/api/posts/${params.id}`);
      return { page: 'Post', data: post };
    }
  }
];

Контекст для редиректов и навигации

Контекст можно расширять функциями redirect или navigate, которые будут использоваться внутри action для управления маршрутизацией:

const context = {
  pathname: '/login',
  user: null,
  redirect: (url) => { window.location.href = url; }
};

const routes = [
  {
    path: '/profile',
    action: ({ user, redirect }) => {
      if (!user) redirect('/login');
      return { page: 'Profile' };
    }
  }
];

Этот подход позволяет управлять поведением маршрутов без нарушения чистоты action.

Резюме по использованию контекста

  • Контекст — это единый объект для передачи данных, параметров, функций и состояния.
  • Он передаётся в action каждого маршрута и доступен на всех уровнях вложенности.
  • Контекст позволяет избегать глобальных переменных и делать маршруты модульными и тестируемыми.
  • Асинхронные действия и редиректы удобно реализовывать через контекст, обеспечивая единый интерфейс для маршрутов.

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