Skip to content

trackQuery.entered re-fires when an unrelated query key changes #94

Description

@sergeysova

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions