Recorddetourdistillate18 October 2026open
The fix for a stale search box wiped what the reader was typing
Resyncing a search box from the URL treated the URL's own lag as an outside change and reset the box mid-typing.
- Problem
- Keeping a text input that writes to the URL in step with the URL when the URL also changes from outside, such as back navigation or a link.
- Approach
- First reset the box whenever the URL's q differed from the last value written, which wiped text while router.replace lagged behind typing; now the component remembers the values it wrote and resets only for a value it never wrote.
- Outcome
- solved
- Conditions
- Next.js 15 App Router client component using useSearchParams and router.replace, where the URL updates after the input's own state.
- Evidence
- distillate PR #5; components/records/RecordSearch.tsx and its tests
- Implication
- A component that writes to the URL must tell its own pending writes apart from outside navigation; test it with a URL that arrives one keystroke behind.
The records search box keeps its text in React state and writes it to the URL as q. Review
found that the box kept "highlight" after the reader followed a link to /records, because
the state was read from the URL only on mount.
The first fix compared the URL's q on every render with the last value the component had
written, and reset the box when they differed. Writing the regression test showed what that
did while typing: router.replace lands after the input's own re-render, so for a moment the
URL still holds the previous text, which differs from what was just written, and the box was
reset to it. A fast typist would have watched letters vanish.
The fix that held keeps a short list of values the component wrote. When the URL's q
changes to one of them, it is the component's own write arriving late and is ignored. Any
other value came from outside, so the box takes it. The check only reads the list and clears
it, so running it twice in one render changes nothing.
Evidence: components/records/RecordSearch.tsx; the
tests "keeps the latest typed text when the URL arrives one keystroke behind" and "clears the
search box when the URL loses its query after mount" in
components/records/RecordSearch.test.tsx.
Implications: two-way sync between an input and the URL needs to know which changes are its own. Test it with the URL lagging, not only with the URL updating at once.