addEventListener - greydragon888/real-router GitHub Wiki
getPluginApi().addEventListener
1. Overview
- What it does: Registers a callback function to listen for router events. Returns an unsubscribe function to remove the listener. Available exclusively through the Plugin API.
- When to use:
- For subscribing to router lifecycle events (
ROUTER_START,ROUTER_STOP) - For subscribing to transition events (
TRANSITION_START,TRANSITION_LEAVE_APPROVE,TRANSITION_SUCCESS,TRANSITION_ERROR,TRANSITION_CANCEL) - In plugins for tracking router state
- For integration with logging and analytics systems
- For subscribing to router lifecycle events (
2. Signature
import { events } from "@real-router/core";
import { getPluginApi } from "@real-router/core/api";
import type { PluginApi } from "@real-router/core/api";
const pluginApi: PluginApi = getPluginApi(router);
const unsub = pluginApi.addEventListener(eventName, cb);
Full type signature:
addEventListener<E extends EventName>(
eventName: E,
cb: Plugin[EventMethodMap[E]]
): Unsubscribe
Event Types and Their Callback Signatures
| Event Constant | Event Name | Plugin Method | Callback Signature |
|---|---|---|---|
events.ROUTER_START |
"$start" |
onStart |
() => void |
events.ROUTER_STOP |
"$stop" |
onStop |
() => void |
events.TRANSITION_START |
"$$start" |
onTransitionStart |
(toState: State, fromState?: State) => void |
events.TRANSITION_LEAVE_APPROVE |
"$$leaveApprove" |
onTransitionLeaveApprove |
(toState: State, fromState?: State) => void |
events.TRANSITION_CANCEL |
"$$cancel" |
onTransitionCancel |
(toState: State, fromState?: State) => void |
events.TRANSITION_SUCCESS |
"$$success" |
onTransitionSuccess |
(toState: State, fromState: State | undefined, opts: NavigationOptions) => void |
events.TRANSITION_ERROR |
"$$error" |
onTransitionError |
(toState: State | undefined, fromState: State | undefined, err: RouterError) => void |
Usage Examples
import { events } from "@real-router/core";
import { getPluginApi } from "@real-router/core/api";
const pluginApi = getPluginApi(router);
// Subscribe to successful transitions
const unsub = pluginApi.addEventListener(
events.TRANSITION_SUCCESS,
(toState, fromState, opts) => {
analytics.trackPageView(toState.path);
},
);
// Later — unsubscribe
unsub();
// Subscribe to router start
const unsub = pluginApi.addEventListener(events.ROUTER_START, () => {
console.log("Router started");
});
// One callback for different events (allowed)
const transitionLogger = (toState, fromState) =>
console.log("Transition:", toState?.name);
pluginApi.addEventListener(events.TRANSITION_START, transitionLogger);
pluginApi.addEventListener(events.TRANSITION_SUCCESS, transitionLogger);
3. Parameters
eventName (required)
- Type:
EventName - Purpose: Event type to subscribe to
- Allowed values (use the
eventsconstant from@real-router/core):events.ROUTER_START— emitted whenrouter.start()succeedsevents.ROUTER_STOP— emitted whenrouter.stop()is calledevents.TRANSITION_START— emitted when navigation beginsevents.TRANSITION_LEAVE_APPROVE— emitted aftercanDeactivateguards pass (beforecanActivateguards run)events.TRANSITION_SUCCESS— emitted when navigation completes successfullyevents.TRANSITION_ERROR— emitted when navigation failsevents.TRANSITION_CANCEL— emitted when navigation is cancelled
- Error behavior:
Errorif the event name is not one of the values above
cb (required)
- Type:
Plugin[EventMethodMap[E]]— callback function with signature matching the event type - Purpose: Function called when the event occurs
- Error behavior:
TypeErrorif not a function
4. Return Value
- Type:
Unsubscribe(() => void) - Purpose: Function to remove the listener
- Behavior:
- Calling it removes the callback from the event
- Safe to call multiple times (subsequent calls are no-ops)
- After unsubscribe, the callback stops receiving events
const unsub = pluginApi.addEventListener(events.ROUTER_START, cb);
unsub(); // Removes the listener
unsub(); // Safe — nothing happens
unsub(); // Safe
5. Possible Errors
| Condition | Error Type | Message |
|---|---|---|
| Router is disposed | RouterError |
DISPOSED |
| Invalid event name | Error |
Invalid event name: {eventName} |
| Callback is not a function | TypeError |
Expected callback to be a function for event {eventName} |
Validation order: disposed check runs first, then event name, then callback type.
6. Related Methods
| Method | When to use |
|---|---|
subscribe() |
Simplified subscription to TRANSITION_SUCCESS only |
usePlugin() |
Register a plugin object with multiple event hooks |
7. Behavior
Registration and Unsubscription
import { events } from "@real-router/core";
import { getPluginApi } from "@real-router/core/api";
const pluginApi = getPluginApi(router);
// Register a listener
let called = false;
const unsub = pluginApi.addEventListener(events.TRANSITION_SUCCESS, () => {
called = true;
});
await router.navigate("users");
// called === true
// Unsubscribe
unsub();
Works Before router.start()
Listeners can be registered before starting the router:
const pluginApi = getPluginApi(router);
pluginApi.addEventListener(events.ROUTER_START, () => {
console.log("Router started");
});
await router.start("/home");
// Listener is called during start
Throws After dispose()
router.dispose();
const pluginApi = getPluginApi(router);
pluginApi.addEventListener(events.TRANSITION_SUCCESS, () => {});
// Throws RouterError with code DISPOSED
Listeners Are Called Synchronously in Registration Order
const calls = [];
pluginApi.addEventListener(events.ROUTER_START, () => calls.push(1));
pluginApi.addEventListener(events.ROUTER_START, () => calls.push(2));
pluginApi.addEventListener(events.ROUTER_START, () => calls.push(3));
await router.start("/home");
// calls === [1, 2, 3]
Error Isolation
An error in one callback does not interrupt execution of others:
pluginApi.addEventListener(events.ROUTER_START, () => {
throw new Error("Boom!");
});
pluginApi.addEventListener(events.ROUTER_START, cb2);
pluginApi.addEventListener(events.ROUTER_START, cb3);
await router.start("/home");
// console.error: "Error in listener..."
// cb2 and cb3 are still called
Guarantees
- Callback is called synchronously in registration order
- Error in one callback does not interrupt other listeners
- Listener list is cloned before iteration (safe against modification during dispatch)
- Unsubscribe function is safe for multiple calls
8. Migration
API Change
The addEventListener method has moved from the router instance to the Plugin API:
Before:
import { events } from "@real-router/core";
const unsub = router.addEventListener(events.TRANSITION_SUCCESS, (toState) => {
console.log(toState.name);
});
After:
import { events } from "@real-router/core";
import { getPluginApi } from "@real-router/core/api";
const pluginApi = getPluginApi(router);
const unsub = pluginApi.addEventListener(
events.TRANSITION_SUCCESS,
(toState) => {
console.log(toState.name);
},
);
Migration Checklist
- Replace all
router.addEventListener(...)calls withgetPluginApi(router).addEventListener(...) - Import
getPluginApifrom@real-router/core - Use event constants from
events(not string literals) - Handle
RouterError(codeDISPOSED) if the router may be disposed when registering listeners