Problem
The Form component (and the useQuery hook it uses) unmounts when navigating between Next.js pages. This causes:
- The
useEffect cleanup in useQuery aborts in-flight fetch requests
- The dispatch callbacks (
onSuccess, onError, onSettled) wired in the Form are lost
- The user navigates to the results page with no results because the fetch was cancelled
Solution
Move the fetch lifecycle into a QueryProvider that sits under ChatProvider in the consuming app's layout, so it persists across page navigation.
Current flow (broken)
ChatProvider (persists in layout)
└── Assistant (unmounts on navigation)
├── useAdapter() → useQuery(url)
└── Form (wires dispatch callbacks)
Target flow (fixed)
ChatProvider (persists in layout)
└── QueryProvider (persists in layout, owns fetch + dispatch wiring)
└── Assistant (can unmount safely)
└── Form (thin wrapper, calls provider's onSubmit)
Changes
- New:
QueryProvider in state/query/ — owns AbortController, dispatch wiring, and fetchResponse call
- New:
useQuerySubmit hook — consumer hook to access onSubmit from provider context
- New:
useFocusOnSettled hook — focuses input when loading transitions to false
- Modify:
Form — becomes thin wrapper using useQuerySubmit
- Modify:
Assistant — remove useAdapter() call, simplify Form props
- Delete:
query/useQuery.ts and adapter/useAdapter.ts — logic absorbed into QueryProvider
- Update: Tests rewritten for
QueryProvider
Consuming app migration
<ChatProvider initialArgs={prompt}>
<QueryProvider>
{children}
</QueryProvider>
</ChatProvider>
Problem
The
Formcomponent (and theuseQueryhook it uses) unmounts when navigating between Next.js pages. This causes:useEffectcleanup inuseQueryaborts in-flight fetch requestsonSuccess,onError,onSettled) wired in theFormare lostSolution
Move the fetch lifecycle into a
QueryProviderthat sits underChatProviderin the consuming app's layout, so it persists across page navigation.Current flow (broken)
Target flow (fixed)
Changes
QueryProviderinstate/query/— owns AbortController, dispatch wiring, andfetchResponsecalluseQuerySubmithook — consumer hook to accessonSubmitfrom provider contextuseFocusOnSettledhook — focuses input when loading transitions to falseForm— becomes thin wrapper usinguseQuerySubmitAssistant— removeuseAdapter()call, simplify Form propsquery/useQuery.tsandadapter/useAdapter.ts— logic absorbed intoQueryProviderQueryProviderConsuming app migration