В Page.js объект ctx (контекст) является центральным
механизмом передачи данных между маршрутами и промежуточными функциями
(middlewares). Одним из самых удобных способов хранения данных, которые
должны быть доступны на протяжении цепочки вызовов, является
использование свойства ctx.state.
ctx.state представляет собой обычный объект JavaScript,
в который можно помещать любые значения, доступные для текущего маршрута
и всех промежуточных обработчиков, следующих за ним.
ctx.stateПри первом обращении к ctx.state можно присвоить ему
объект или отдельные свойства. Например:
page('*', (ctx, next) => {
ctx.state = {}; // инициализация объекта state
ctx.state.user = null;
next();
});
ctx.state.user будет доступен для всех последующих
обработчиков на этом маршруте.ctx.state, можно добавлять
свойства напрямую: ctx.state.token = '12345';.Важно понимать, что ctx.state — это отдельный объект для
каждого запроса (в случае SPA — для каждой навигации), поэтому данные не
пересекаются между разными переходами страницы.
Использование ctx.state особенно удобно для передачи
данных между несколькими промежуточными функциями:
page('/dashboard', loadUser, loadSettings, renderDashboard);
function loadUser(ctx, next) {
// имитация запроса к серверу
ctx.state.user = { id: 1, name: 'Алексей' };
next();
}
function loadSettings(ctx, next) {
// использование данных из предыдущего middleware
ctx.state.settings = { theme: 'dark', notifications: true };
next();
}
function renderDashboard(ctx) {
console.log(ctx.state.user.name); // 'Алексей'
console.log(ctx.state.settings.theme); // 'dark'
}
Каждый middleware может как создавать, так и
модифицировать данные в ctx.state, а
финальная функция обработки маршрута использует уже готовый объект с
полным набором данных.
При работе с асинхронными операциями важно корректно использовать
await внутри middleware, чтобы данные в
ctx.state были готовы к следующей функции:
page('/profile', async (ctx, next) => {
ctx.state.user = await fetchUserFromAPI();
await next();
}, renderProfile);
async function renderProfile(ctx) {
console.log(ctx.state.user); // данные уже загружены
}
await next().ctx.stateДля больших приложений рекомендуется использовать четкую
структуру ctx.state, чтобы избежать конфликтов и
пересечений имен:
ctx.state = {
auth: {
user: null,
token: null
},
ui: {
theme: 'light',
language: 'ru'
},
data: {
posts: [],
comments: []
}
};
Такой подход облегчает поддержку и понимание кода, особенно когда цепочка middleware длинная.
ctx.state
выполняется напрямую:ctx.state.auth.user = { id: 2, name: 'Мария' };
ctx.state.data.posts = [];
ctx.state.auth.token = null;
В SPA часто требуется сброс ctx.state при выходе
пользователя или смене маршрута.
ctx.state для UI-логикиctx.state может содержать не только данные с сервера, но
и локальное состояние интерфейса, например:
ctx.state.ui.isSidebarOpen = true;
ctx.state.ui.activeTab = 'settings';
Это позволяет использовать единый источник состояния, доступный на протяжении всей навигации, и интегрировать его с библиотеками рендеринга, такими как lit-html или React.
ctx.state — объект для хранения данных текущего
маршрута.ctx.state изолированы для каждого перехода в
SPA.Использование ctx.state делает архитектуру маршрутов
более прозрачной, позволяет централизованно управлять данными и упрощает
взаимодействие между middleware.