Передача state при навигации

Page.js — это лёгкая клиентская библиотека для организации маршрутизации в одностраничных приложениях (SPA). Одной из ключевых возможностей является передача состояния между маршрутами, что позволяет хранить и передавать данные без использования глобальных переменных или URL-параметров. Рассмотрим детально, как это реализуется и какие есть нюансы.


Основы передачи state

При использовании Page.js маршруты определяются через функцию page(path, callback), где callback принимает объект контекста ctx и функцию next. Контекст содержит важные данные о текущем маршруте. Для передачи state при навигации можно использовать объект ctx.state, который создаётся и доступен в каждом обработчике маршрута.

page('/profile', (ctx) => {
  console.log(ctx.state.user); // Доступ к переданным данным
});

Чтобы передать state при переходе на маршрут, используется метод page.show(path, state):

const user = { id: 1, name: 'Alice' };
page.show('/profile', { user });

В этом примере объект { user } автоматически попадает в ctx.state при обработке маршрута /profile.

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

  • ctx.state создаётся автоматически при вызове page.show.
  • Объект state может содержать любые данные: примитивы, массивы, объекты.
  • Переданный state не сохраняется в URL, что позволяет скрывать внутренние данные приложения.

Механизм работы ctx.state

Каждый вызов page.show создаёт новый объект ctx. Если передан state, он присваивается ctx.state. Это значит, что:

  1. State локален для конкретного перехода. Он не сохраняется между последующими навигациями.
  2. Для передачи данных между разными маршрутами нужно явно передавать state при каждом переходе.
  3. State можно использовать совместно с ctx.params для более сложной маршрутизации.
page('/post/:id', (ctx) => {
  console.log(ctx.params.id); // параметр из URL
  console.log(ctx.state.comments); // данные из предыдущего маршрута
});

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


Передача state при программной навигации

Чаще всего state передаётся при программной навигации через кнопки, ссылки или события:

document.querySelector('#btnProfile').addEventListener('click', () => {
  const userData = { id: 42, name: 'Bob' };
  page.show('/profile', { user: userData });
});

Если пользователь кликает по ссылке <a href="/profile">, данные state не передаются автоматически. В этом случае есть два решения:

  1. Использовать onclick обработчик и вызвать page.show с state.
  2. Хранить данные в глобальном хранилище или через localStorage/sessionStorage.

Прямое использование <a> работает только для URL-перехода, state здесь не поддерживается.


Состояние и middleware

Page.js позволяет подключать промежуточные обработчики (middleware) через page(fn) или цепочку вызовов маршрутов. Передаваемый state доступен на всех уровнях цепочки.

page('*', (ctx, next) => {
  console.log('Global middleware', ctx.state);
  next();
});

page('/dashboard', (ctx) => {
  console.log('Dashboard', ctx.state.user);
});

page.show('/dashboard', { user: { name: 'Alice' } });

Особенности:

  • Middleware вызывается в порядке регистрации.
  • ctx.state одинаковый для всех middleware и конечного обработчика маршрута.
  • Изменение ctx.state в middleware отражается в последующих обработчиках.

Копирование и модификация state

Если нужно сохранить оригинальный state и добавить новые данные, рекомендуется клонировать объект перед модификацией:

page('/settings', (ctx) => {
  const newState = { ...ctx.state, theme: 'dark' };
  page.show('/profile', newState);
});

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


Ограничения и рекомендации

  1. State не хранится в истории браузера, поэтому при обновлении страницы все данные будут потеряны. Для долговременного хранения нужно использовать localStorage или сервер.
  2. Не следует помещать в state большие объёмы данных или объекты с циклическими ссылками — это может вызвать проблемы при копировании и дебаге.
  3. Для передачи конфиденциальной информации лучше использовать state, а не query-параметры URL.

Пример комплексного использования

// Middleware для логина
page('*', (ctx, next) => {
  ctx.state.user = { id: 123, name: 'Alice' };
  next();
});

// Маршрут профиля
page('/profile', (ctx) => {
  const { user } = ctx.state;
  document.body.innerHTML = `<h1>Профиль: ${user.name}</h1>`;
});

// Переход с кнопки
document.querySelector('#btnProfile').addEventListener('click', () => {
  page.show('/profile', { extra: 'Дополнительные данные' });
});

В этом примере:

  • State из middleware доступен на маршруте.
  • Дополнительные данные можно передавать при page.show.
  • Объект ctx.state объединяет данные middleware и переданные вручную.

Использование ctx.state делает маршрутизацию в Page.js гибкой и безопасной для передачи внутренних данных приложения без зависимости от URL, облегчая работу с динамическим интерфейсом SPA.