Package
@effector/router (core)
What happened?
trackQuery's entered event (and $state) re-fires on every $query update while the tracker is eligible (its routes match the active route) — even when none of the schema-owned keys actually changed. A chain sampled off tracker.entered therefore re-runs whenever an unrelated query parameter changes.
Root cause
In packages/core/lib/track-query.ts:
const $result = combine($eligible, $query, (eligible, query) =>
eligible ? parameters.safeParse(query) : null,
);
safeParse returns a new object reference on every call, so combine treats $result (and the derived $evaluation) as changed on any $query update, regardless of whether the schema-relevant fields differ from the previous parse. The downstream sample that fires entered has no equality guard, so it re-emits with logically identical parsed params.
Reproduction
const home = createRoute({ path: '/' });
const controls = createRouterControls();
const router = createRouter({ routes: [home], controls });
const scope = fork();
await allSettled(router.setHistory, { scope, params: historyAdapter(createMemoryHistory({ initialEntries: ['/'] })) });
const tracker = trackQuery({
controls,
routes: [home],
parameters: z.object({ id: z.string() }),
});
const enteredCalls = watchCalls(tracker.entered, scope);
await allSettled(router.navigate, { scope, params: { path: '/', query: { id: '1' } } });
// enteredCalls: 1
await allSettled(router.navigate, { scope, params: { path: '/', query: { id: '1', tab: 'details' } } });
// enteredCalls: 2 (unchanged id, unrelated key added)
await allSettled(router.navigate, { scope, params: { path: '/', query: { id: '1', tab: 'other' } } });
// enteredCalls: 3 (unrelated key changed again)
Expected: enteredCalls stays at 1 across both extra navigations, since id never changes.
Actual: fires again on every unrelated query mutation.
Suggested direction
Add an equality guard on the parsed result (e.g. updateFilter/deep-equal on result.data, or compare only schema-owned keys) before deriving entered/$state, so unrelated query keys don't cause spurious re-entry.
Traceability
Package
@effector/router (core)
What happened?
trackQuery'senteredevent (and$state) re-fires on every$queryupdate while the tracker is eligible (itsroutesmatch the active route) — even when none of the schema-owned keys actually changed. A chain sampled offtracker.enteredtherefore re-runs whenever an unrelated query parameter changes.Root cause
In
packages/core/lib/track-query.ts:safeParsereturns a new object reference on every call, socombinetreats $result (and the derived $evaluation) as changed on any $query update, regardless of whether the schema-relevant fields differ from the previous parse. The downstream sample that firesenteredhas no equality guard, so it re-emits with logically identical parsed params.Reproduction
Expected:
enteredCallsstays at 1 across both extra navigations, sinceidnever changes.Actual: fires again on every unrelated query mutation.
Suggested direction
Add an equality guard on the parsed result (e.g.
updateFilter/deep-equal onresult.data, or compare only schema-owned keys) before derivingentered/$state, so unrelated query keys don't cause spurious re-entry.Traceability
trackQueryAPI.routesworks correctly (seepackages/core/tests/track-query.test.ts— "for routes"), but query-key-level scoping does not — this is the real remaining case.