isActiveRoute - greydragon888/real-router GitHub Wiki

router.isActiveRoute

1. Overview

  • What it does: Checks if the specified route is active. Supports hierarchical checking (parent is considered active if child is active) and exact comparison. Takes route's defaultParams into account.
  • When to use:
    • For determining active state in navigation/menus
    • For conditional UI rendering based on current route
    • For checking if user is in a specific application section

2. Signature

router.isActiveRoute(
  name: string,
  params?: Params,
  strictEquality?: boolean,
  ignoreQueryParams?: boolean
): boolean

Usage Examples

// Check current route
router.isActiveRoute("home"); // true if home is active

// Check with parameters
router.isActiveRoute("users.view", { id: "123" });

// Hierarchical check (default)
// If users.view is active, users is also considered active
router.isActiveRoute("users"); // true

// Exact comparison (strictEquality=true)
router.isActiveRoute("users", {}, true); // false if users.view is active

// Query params consideration (ignoreQueryParams=false)
router.isActiveRoute("search", { q: "test" }, false, false);

3. Parameters

name (required)

  • Type: string
  • Purpose: Route name to check (dot-notation for nested)
  • Allowed values: Valid route name (validated by regex)
  • On error: TypeError for invalid names
Value Behavior
"home" ✅ Checks home route
"users.view" ✅ Checks nested route
"" ⚠️ Warning + returns false
null, 123 TypeError

params (optional)

  • Type: Params
  • Default value: {}
  • Purpose: Parameters to compare with active state
  • Validation: Must be plain object without functions, circular references, class instances
  • On error: TypeError for invalid structure

strictEquality (optional)

  • Type: boolean
  • Default value: false
  • Purpose: Require exact route name match
  • false: Hierarchical check (parent active if child is active)
  • true: Only exact name match
  • On error: TypeError for non-boolean values

ignoreQueryParams (optional)

  • Type: boolean
  • Default value: true
  • Purpose: Ignore query parameters during comparison
  • true: Only URL parameters (path params) are compared
  • false: All parameters including query are compared
  • On error: TypeError for non-boolean values

4. Return Value

  • Type: boolean
  • true: Route is active
  • false: Route is not active

Activity Logic

Situation strictEquality=false strictEquality=true
Exact name match true true
Child is active true false
Parent is active false false
Sibling is active false false
Router not started false false

5. Side Effects

  • Console warning: When called with empty string "" outputs console.warn

6. Possible Errors

Condition Error Message
name not a string TypeError Route name must be a string
params invalid structure TypeError [router.isActiveRoute] Invalid params structure
params contains function TypeError [router.isActiveRoute] Invalid params structure
params contains circular reference TypeError [router.isActiveRoute] Invalid params structure
params contains class instance TypeError [router.isActiveRoute] Invalid params structure
strictEquality not boolean TypeError [router.isActiveRoute] strictEquality must be a boolean, got {type}
ignoreQueryParams not boolean TypeError [router.isActiveRoute] ignoreQueryParams must be a boolean, got {type}
// Invalid route name
router.isActiveRoute(null); // TypeError: Route name must be a string

// Invalid params
router.isActiveRoute("home", "invalid"); // TypeError: Invalid params structure
router.isActiveRoute("home", { fn: () => {} }); // TypeError: Invalid params structure
router.isActiveRoute("home", { date: new Date() }); // TypeError: Invalid params structure

// Invalid boolean parameters
router.isActiveRoute("home", {}, 1); // TypeError: strictEquality must be a boolean
router.isActiveRoute("home", {}, false, "true"); // TypeError: ignoreQueryParams must be a boolean

7. Related Methods

Method When to use
getState() Get full current state
areStatesEqual() Low-level state comparison
navigate() Navigate to route

8. Behavior

Main Scenarios

  • Current route: true for active route
  • Inactive route: false for other routes
  • Router not started: Always false

Test Examples

// Current route is active
expect(router.isActiveRoute("home")).toBe(true);

// Inactive route
expect(router.isActiveRoute("sign-in")).toBe(false);

// strictEquality
router.navigate("sign-in");
expect(router.isActiveRoute("home", {}, true)).toBe(false);

// Router not started
router.stop();
expect(router.isActiveRoute("test", {})).toBe(false);

Hierarchical Check (strictEquality=false)

router.navigate("users.view", { id: "123" });

// Parent is active if child is active
expect(router.isActiveRoute("users")).toBe(true);

// With exact match — no
expect(router.isActiveRoute("users", {}, true)).toBe(false);

// Sibling is not active
expect(router.isActiveRoute("users.list")).toBe(false);

// Parameters must match
expect(router.isActiveRoute("users", { id: "123" })).toBe(true);
expect(router.isActiveRoute("users", { id: "456" })).toBe(false);

// Multi-level hierarchy
router.navigate("section.view", { section: "section1", id: "123" });
expect(router.isActiveRoute("section", { section: "section1" })).toBe(true);

Query Parameters

router.navigate("section.query", {
  section: "section1",
  param1: "value1",
  param2: "value2",
  param3: "value3",
});

// By default query params are ignored
expect(router.isActiveRoute("section.query", { section: "section1" })).toBe(
  true,
);

// With ignoreQueryParams=false — all parameters must match
expect(
  router.isActiveRoute("section.query", { section: "section1" }, false, false),
).toBe(false);

expect(
  router.isActiveRoute(
    "section.query",
    {
      section: "section1",
      param1: "value1",
      param2: "value2",
      param3: "value3",
    },
    false,
    false,
  ),
).toBe(true);

DefaultParams

// defaultParams are used in check
router.navigate("withDefaultParam");
expect(router.isActiveRoute("withDefaultParam")).toBe(true);
expect(router.isActiveRoute("withDefaultParam", {}, true)).toBe(true);

// In hierarchical check
getConfig(router).defaultParams.users = { filter: "active" };
router.navigate("users.view", { id: "123", filter: "active" });
expect(router.isActiveRoute("users")).toBe(true);

// Explicit params override defaultParams
expect(router.isActiveRoute("users", { filter: "inactive" })).toBe(false);

Strict Type Comparison

router.navigate("users.view", { id: "123" });

// Strict comparison: number !== string
expect(router.isActiveRoute("users.view", { id: 123 })).toBe(false);

// null !== string
expect(router.isActiveRoute("users.view", { id: null })).toBe(false);

// undefined !== string
expect(router.isActiveRoute("users.view", { id: undefined })).toBe(false);

Empty String (Root Node)

// Root node ("") always returns false with warning
expect(router.isActiveRoute("")).toBe(false);
// console.warn: 'isActiveRoute("") called with empty string...'

Edge Cases

  • Empty string "": Returns false with warning
  • Router not started: Always false
  • Invalid params: TypeError for functions, circular references, class instances
  • Object.create() params: Rejected due to non-standard prototype
  • Non-enumerable properties: Ignored in params
  • Type coercion: Not applied — 123 !== "123", null !== "123"
  • undefined in params: Means "value must be undefined", not "skip check"

Guarantees

  • Route name validation caching (optimization ~40ns per call)
  • Strict boolean parameter validation (prevents truthy/falsy bugs)
  • Strict equality when comparing parameters (===)
  • defaultParams taken into account automatically
  • Hierarchical check by default (convenient for menus/navigation)

Migration from router5

Version Comparison

Master Current
Method name isActive isActiveRoute
Signature (name, params?, strictEquality?, ignoreQueryParams?) => boolean Same
Name validation ❌ No validateRouteName() with regex
Params validation ❌ No validateParams() (plain object, no functions)
Boolean validation ❌ No (truthy/falsy coercion) ✅ Strict type checking
Warning for "" ❌ No console.warn
Caching ❌ No ✅ Validated names cache
TypeScript JavaScript (no types) ✅ Full typing

Breaking Changes

Severity What Changed Was Now Impact
🔴 CRITICAL Method name isActive isActiveRoute All calls need to be renamed
🟡 MEDIUM Name validation Silently accepted invalid TypeError Code with invalid names will break
🟡 MEDIUM Params validation Silently accepted any TypeError Code with functions/cycles in params breaks
🟡 MEDIUM Boolean validation Truthy/falsy coercion TypeError Code with 1/"true" instead of true breaks
🟢 LOW Empty string "" Returned false silently false + console.warn Warning in console

Examples

// ❌ Code that will break (method name)
router.isActive("home");

// ✅ Code after migration
router.isActiveRoute("home");

// ❌ Code that will break (truthy boolean)
router.isActive("users", {}, 1); // 1 was truthy → worked as true

// ✅ Code after migration
router.isActiveRoute("users", {}, true);

// ❌ Code that will break (function in params)
router.isActive("home", { onClick: () => {} });

// ✅ Code after migration
router.isActiveRoute("home", {}); // Remove functions from params

// ❌ Code that will break (invalid name)
router.isActive(null); // Returned false
router.isActive(123); // Returned false

// ✅ Code after migration
// Fix code — pass string
router.isActiveRoute("home");

Implementation Changes

Method Rename

Master Current
router.isActive() router.isActiveRoute()

Reason: More explicit naming, matching the isXxx pattern for route state predicates.

New Functionality

  • Name validation: Regex check of route name via validateRouteName()
  • Params validation: Check for plain object, rejection of functions, circular references, class instances
  • Boolean validation: Strict type checking for strictEquality and ignoreQueryParams
  • Validation caching: Set<string> to skip repeated regex validation (~40ns savings)
  • Warning for "": Informative console.warn when empty string is passed
  • TypeScript: Full typing of parameters and return value

Optimizations

Aspect Master Current Description
State creation makeState() always Minimal object { name, params, path: "" } Avoids expensive buildPath()
Fast path ✅ Route relationship check before expensive ops Early return false for unrelated routes
Route name cache validatedRouteNames Set ~40ns savings on repeated calls

Was (master)

Implementation issues in master branch:

  • No input validation
  • makeState() calls expensive buildPath() even when path is not needed
  • Truthy/falsy coercion for boolean parameters
  • No optimization for unrelated routes

Now (current)

Improvements:

  • Early validation of all parameters with clear messages
  • Minimal State object without buildPath()
  • Fast path: startsWith() check before expensive operations
  • Regex validation caching for names
  • Strict typing

Migration Guide

Checklist

  • Rename all isActive calls → isActiveRoute
  • Replace truthy values with true (1true, "true"true)
  • Replace falsy values with false (0false, ""false, nullfalse)
  • Remove functions from params
  • Remove class instances from params (Date, RegExp, etc.)
  • Check code passing invalid route names

Migration Examples

// ❌ Was (master) — method name
router.isActive("users");

// ✅ Now — renamed
router.isActiveRoute("users");

// ❌ Was (master) — truthy instead of boolean
router.isActive("users", {}, 1); // 1 as true
router.isActive("users", {}, "yes"); // "yes" as true

// ✅ Now — explicit boolean
router.isActiveRoute("users", {}, true);

// ❌ Was (master) — falsy instead of boolean
router.isActive("users", {}, 0); // 0 as false
router.isActive("users", {}, null); // null as false

// ✅ Now — explicit boolean
router.isActiveRoute("users", {}, false);

// ❌ Was (master) — function in params
const params = {
  id: "123",
  onClick: () => console.log("clicked"), // Worked
};
router.isActive("users", params);

// ✅ Now — only serializable values
const params = { id: "123" };
router.isActiveRoute("users", params);

// ❌ Was (master) — Date in params
router.isActive("events", { date: new Date() }); // Worked

// ✅ Now — string instead of Date
router.isActiveRoute("events", { date: "2024-01-01" });

Summary

Category Status
Breaking Changes 🔴 CRITICAL (method rename)
New validation ✅ name, params, boolean parameters
Optimizations ✅ Fast path, caching, minimal State
TypeScript ✅ Full typing
Warnings ✅ console.warn for ""

Maximum severity: 🔴 CRITICAL

Main change: Method renamed from isActive to isActiveRoute.

Global search and replace required: router.isActive(router.isActiveRoute(

Additional changes:

  • Strict validation of all parameters
  • TypeError instead of silent false for invalid input
  • Boolean parameters no longer accept truthy/falsy values

Positive changes:

  • Early error detection (fail-fast)
  • Performance optimization (fast path, caching)
  • TypeScript typing
  • Informative error messages
⚠️ **GitHub.com Fallback** ⚠️