@real-router/lifecycle-plugin
v0.4.2
Published
Route-level lifecycle hooks: onEnter, onStay, onLeave
Maintainers
Readme
@real-router/lifecycle-plugin
Route-level lifecycle hooks for Real-Router. Add
onNavigate,onEnter,onStay,onLeavecallbacks directly to route definitions.
// Without plugin — scattered subscribe() calls with route checks:
router.subscribe(({ route, previousRoute }) => {
if (route.name === "catalog") loadServices(route.params);
if (previousRoute?.name === "editor") saveEditorState();
});
// With plugin — declarative, per-route:
{ name: "catalog", path: "/catalog?q&sort", onNavigate: () => (s) => loadServices(s.params) }
{ name: "editor", path: "/editor", onLeave: () => () => saveEditorState() }Installation
npm install @real-router/lifecycle-pluginPeer dependency: @real-router/core
Quick Start
import { createRouter } from "@real-router/core";
import { lifecyclePluginFactory } from "@real-router/lifecycle-plugin";
const routes = [
{
name: "services.catalog",
path: "/catalog?q&sort&dir",
// Fires on entry AND on param-change — recommended default
onNavigate: () => (toState) => {
loadServices(toState.params);
},
},
{
name: "chat",
path: "/chat/:roomId",
// Orthogonal: onEnter covers entry-only setup, onNavigate covers
// every navigation (including entry). Both fire on entry.
onEnter: () => (toState) => {
chatSocket.connect(toState.params.roomId);
},
onNavigate: () => (toState) => {
loadMessages(toState.params.roomId);
},
onLeave: () => () => {
chatSocket.disconnect();
},
},
];
const router = createRouter(routes);
router.usePlugin(lifecyclePluginFactory());
await router.start("/");Start with
onNavigate. It covers the most common case — running the same logic whenever the route is the navigation target (data loading, analytics, UI reset). AddonEnteroronStayfor extra case-specific logic.
Hook Reference
| Hook | Fires when | Typical use case |
| ------------ | --------------------------------------- | ------------------------------------------- |
| onNavigate | Any successful navigation to the route | Data loading, analytics, UI reset (default) |
| onEnter | Route is entered | Entry-only setup (open socket, scroll top) |
| onStay | Same route, params changed | Stay-only logic (incremental updates) |
| onLeave | Route is left | Cleanup timers, save state |
Orthogonal dispatch: onEnter / onStay / onNavigate fire independently based on their own conditions. On entry, onEnter and onNavigate fire. On param-change, onStay and onNavigate fire. Each hook is composable — declaring one never silences another.
Each hook field is a factory function (router, getDependency) => (toState, fromState?) => void. The factory runs once per route; the returned callback is cached and invoked on each matching transition. When you don't need DI, omit the factory params:
// Without DI — ignore factory params:
onEnter: () => (toState) => { console.log("entered", toState.name); }
// With DI — access router and dependencies:
onEnter: (router, getDependency) => (toState) => {
const analytics = getDependency("analytics");
analytics.track("page_viewed", { route: toState.name });
}Execution order
onLeave fires first (at leave-approve phase), then onEnter or onStay (at transition success).
Use Cases
Data loading (onNavigate — recommended default)
{
name: "services.catalog",
path: "/catalog?q&sort&dir",
onNavigate: () => (toState) => {
// Fires on entry from another route AND on filter/sort param changes
loadServices(toState.params);
},
}Analytics tracking
{
name: "product",
path: "/products/:id",
onEnter: () => (toState) => {
analytics.track("product_viewed", { productId: toState.params.id });
},
}Cleanup on leave
{
name: "editor",
path: "/editor/:docId",
onLeave: () => () => {
autosaveTimer.clear();
webSocket.disconnect();
},
}React to param changes
{
name: "search",
path: "/search?q",
onStay: () => (toState) => {
searchStore.setQuery(toState.params.q);
},
}Documentation
- ARCHITECTURE.md — Design decisions and data flow
- Plugin Architecture — How plugins integrate with the router
Related Packages
| Package | Description | | ---------------------------------------------------------------------------------------- | -------------------------------------- | | @real-router/core | Core router (required peer dependency) | | @real-router/browser-plugin | Browser History API integration | | @real-router/logger-plugin | Development logging |
