Skip to content

Discard stale relationship-search responses after the dialog closes or query changes #172

Description

@atomantic

Problem

LinkRelationshipDialog debounces person searches but neither aborts nor invalidates requests when the dialog closes, when the query falls below two characters, or when a later search begins. A slower older response can overwrite current results and clear the loading state for a newer request.

Evidence (quoted code + context)

Opening resets UI state, but the open effect only executes cleanup when the component unmounts: "useEffect(() => { if (open) { ... setResults([]); ... } }, [open, defaultType]);" (lines 42-55), while the timer cleanup is only "return () => { if (searchTimerRef.current) clearTimeout(searchTimerRef.current); };" in a separate unmount-only effect (lines 57-60). doSearch directly awaits "const result = await api.search(dbId, { q: q.trim(), limit: 10 });" and always commits "setResults(...)" and "setSearching(false)" (lines 62-72). The newer RelationshipModal demonstrates the needed contract with a monotonic request id and checks before it commits results (lines 42-104).

Impact

Typing quickly, closing/reopening, or changing the dialog context can display candidates for a previous query or database. A user can select a stale person and create the wrong relationship, while the spinner may falsely indicate the current request is complete.

Implementation plan

Give the dialog a monotonically increasing request generation (and an AbortController passed through api.search/fetchJson if supported). Increment it and clear the debounce timer on close and on every invalidating input change. Only the latest live request may update results or loading state; handle rejected/aborted searches so the UI does not retain a spinner or unhandled promise.

Acceptance criteria

Results always correspond to the currently visible query and database; deleting to fewer than two characters leaves no results; closing invalidates pending work; and an older response cannot change results or loading after a newer search starts.

Verification

Add a fake-timer React Testing Library test that resolves two deferred api.search calls out of order, plus close/reopen and short-query cases. Run npm test --prefix client -- --run for the dialog tests and npm run build --prefix client.

Dependencies and related work

No PostgreSQL migration work. Align with the existing stale-response protection in RelationshipModal rather than creating a competing search convention.

Scope (Complexity, files, non-goals)

Low; LinkRelationshipDialog, API signal typing only if necessary, and focused tests. Non-goals: changing relationship business rules or search ranking.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions