Skip to content

fix(lib): stop the url sync from restoring a removed filter tag - #862

Merged
antontranelis merged 1 commit into
mainfrom
fix/filter-tags-url-sync
Aug 29, 2026
Merged

fix(lib): stop the url sync from restoring a removed filter tag#862
antontranelis merged 1 commit into
mainfrom
fix/filter-tags-url-sync

Conversation

@antontranelis

Copy link
Copy Markdown
Member

Symptom

Filtering the map by hashtags worked, but the filters could not be removed again: clicking ✕ on a tag chip left the chip in place and the tag in the url.

Cause

removeFilterTag drops the tag from the filter state and rewrites the address bar in one go. The router location, however, still carries the old tags value for one render. A render log of the click makes it visible:

--- click ✕ ---
RENDER router.search="?tags=garten"  window.search=""             state=[]
RENDER router.search="?tags=garten"  window.search="?tags=garten"  state=[garten]

The sync effect ran on that stale location, saw a filter state that no longer matched the url, and restored the tag it had just removed — writing it back into the url as well. Because the effect listed filterTags among its dependencies, every state change triggered exactly this reconcile.

Fix

The sync now follows the url alone. It remembers the tags value it last applied and does nothing while that value is unchanged, so a state update can no longer trigger a reconcile against a stale location. It also waits for the available tags before recording anything, which keeps deep links such as /?tags=garten working when the tag list arrives late.

The effect moved out of UtopiaMapInner into useSyncFilterTagsWithUrl so it can be covered by tests.

Tests

Four cases in useSyncFilterTagsWithUrl.spec.tsx: reading one tag from the url, reading several, removing a tag, and removing one of two. The two removal tests fail without the fix.

The suite needs a real BrowserRouter. MemoryRouter cannot reproduce the regression because useFilter reads window.location, which MemoryRouter never touches — worth knowing before anyone rewrites these tests.

vitest run (52 passed), eslint --max-warnings 0 and tsc --noEmit all pass in lib.

Note

That useFilter reads the global window.location while the sync uses the router location is the underlying weakness that made this possible. This PR does not change it, to keep the fix small — it looks like a worthwhile follow-up.

🤖 Generated with Claude Code

Removing a hashtag filter had no effect: the chip reappeared immediately and
the tag stayed in the url.

`removeFilterTag` drops the tag from the filter state and rewrites the address
bar in one go, but the router location still carries the old `tags` value for
one render. The sync effect ran on that stale location, saw a filter state
that no longer matched the url, and faithfully restored the tag it had just
removed — including writing it back into the url.

The sync now follows the url alone: it remembers which `tags` value it last
applied and does nothing while that value is unchanged, so a state update can
no longer trigger a reconcile against a stale location. It also waits for the
available tags before recording, which keeps deep links working.

The effect moved out of UtopiaMapInner into `useSyncFilterTagsWithUrl` so it
can be covered by tests. The suite reproduces the regression with a real
BrowserRouter — MemoryRouter cannot, because `useFilter` reads
`window.location`, which MemoryRouter never touches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antontranelis
antontranelis merged commit cfeb752 into main Aug 29, 2026
28 checks passed
@antontranelis
antontranelis deleted the fix/filter-tags-url-sync branch August 29, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant