Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ $ npm install @ngxs/store@dev
- Refactor(store): Drop `ɵwrapObserverCalls` [#2371](https://github.com/ngxs/store/pull/2371)
- Fix(storage-plugin): Guard against `engine` may be falsy [#2367](https://github.com/ngxs/store/pull/2367) [#2368](https://github.com/ngxs/store/pull/2368)
- Peformance(storage-plugin): Replace closure-based action matcher with direct type comparison [#2369](https://github.com/ngxs/store/pull/2369)
- Fix(router-plugin): Avoid redundant NGXS state updates for identical router snapshots [#2372](https://github.com/ngxs/store/pull/2372)

### 20.1.0 2025-07-16

Expand Down
40 changes: 38 additions & 2 deletions packages/router-plugin/src/router.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ export class RouterState {
);
}

/**
* Handles all Angular Router actions (request, navigation, cancel, error, data resolved, navigated).
*
* Each time one of these actions is dispatched, the router state in the NGXS store
* is updated to reflect the latest router snapshot and navigation metadata.
*
* Specifically:
* - `trigger`: source of the navigation
* - `state`: current `RouterStateSnapshot` of the Angular Router
* - `navigationId`: unique ID of the router event
*
* This ensures the NGXS store always mirrors the latest Angular Router state.
*/
@Action([
RouterRequest<RouterStateSnapshot>,
RouterNavigation<RouterStateSnapshot>,
Expand All @@ -124,11 +137,34 @@ export class RouterState {
ctx: StateContext<RouterStateModel>,
action: RouterAction<RouterStateModel, RouterStateSnapshot>
): void {
ctx.setState({
const state = ctx.getState();
const newState = {
trigger: action.trigger,
state: action.routerState,
navigationId: action.event.id
});
};

// Skip updating NGXS state if nothing has changed.
// This avoids updating the internal NGXS state signal, which would otherwise
// trigger recomputation of `selectSignal` selectors and schedule unnecessary
// Angular change detection cycles.
if (state.trigger === newState.trigger && state.navigationId === newState.navigationId) {
let equal = false;
try {
equal =
JSON.stringify(this._serializer.serialize(state.state!)) ===
JSON.stringify(this._serializer.serialize(newState.state));
} catch {
// If serialization or stringify fails, assume not equal.
equal = false;
}

if (equal) {
return;
}
}

ctx.setState(newState);
}

private _setUpStoreListener(): void {
Expand Down
Loading