This document provides context for continuing the migration in future PRs: - Remaining work (Party component mutations, resource class removal) - Patterns and best practices for infinite queries, debounced search, mutations - Known issues and file locations - Reference implementations Co-Authored-By: Justin Edmund <justin@jedmund.com>
7.9 KiB
TanStack Query Migration - Continuation Guide
This document provides context for continuing the TanStack Query v6 migration in hensei-web.
Migration Status
Completed (PR #441 - merged)
- Query options factories:
party.queries.ts,job.queries.ts,user.queries.ts,search.queries.ts - Mutation configurations:
party.mutations.ts,grid.mutations.ts,job.mutations.ts - SSR utilities:
withInitialData,prefetchQuery,prefetchInfiniteQuery - Example components:
JobSelectionSidebar.svelte,teams/[id]/+page.svelte
Completed (PR #442 - pending merge)
JobSkillSelectionSidebar.svelte- Job skill search with infinite scrollSearchContent.svelte- Search modal for weapons/characters/summons[username]/+page.svelte- User profile page with teams/favorites tabsteams/explore/+page.svelte- Public teams listing
Remaining Work
Follow-Up Prompt 5: Party Component Mutations
Priority: High Complexity: Large
The Party.svelte component (1535 lines) needs to be migrated to use TanStack Query mutations instead of direct service calls.
Files to modify:
src/lib/components/party/Party.svelte
Current state: Uses PartyService, GridService, ConflictService, and direct partyAdapter calls.
Target state: Use mutation hooks from:
src/lib/api/mutations/party.mutations.ts-useUpdateParty,useDeleteParty,useRemixParty,useFavoriteParty,useUnfavoriteParty,useRegeneratePreviewsrc/lib/api/mutations/grid.mutations.ts-useCreateGridWeapon,useUpdateGridWeapon,useDeleteGridWeapon, etc.src/lib/api/mutations/job.mutations.ts-useUpdatePartyJob,useUpdatePartyJobSkills,useRemovePartyJobSkill,useUpdatePartyAccessory
Recommended sub-tasks:
- 5a: Party metadata mutations - name, description, visibility using
useUpdateParty - 5b: Grid weapon mutations - add/update/delete weapons using grid mutations
- 5c: Grid character mutations - add/update/delete characters using grid mutations
- 5d: Grid summon mutations - add/update/delete summons using grid mutations
- 5e: Job and skill mutations - job selection, skill management using job mutations
Key functions to migrate in Party.svelte:
updatePartyDetails()- replacepartyService.update()withuseUpdateParty().mutate()toggleFavorite()- replacepartyService.favorite()/unfavorite()withuseFavoriteParty()/useUnfavoriteParty()remixParty()- replacepartyService.remix()withuseRemixParty()deleteParty()- replacepartyService.delete()withuseDeleteParty()handleSelectJob()- replacepartyAdapter.updateJob()withuseUpdatePartyJob()handleSelectJobSkill()- replacepartyAdapter.updateJobSkills()withuseUpdatePartyJobSkills()- Drag-drop operations - replace
gridService.moveWeapon/Character/Summon()with appropriate mutations
Follow-Up Prompt 6: Remove Deprecated Resource Classes
Priority: Low Complexity: Small Prerequisite: All components migrated away from resource classes
Files to delete:
src/lib/api/adapters/resources/search.resource.svelte.tssrc/lib/api/adapters/resources/party.resource.svelte.tssrc/lib/api/adapters/resources/job.resource.svelte.tssrc/lib/api/adapters/resources/infiniteScroll.resource.svelte.ts
Steps:
- Search for any remaining imports:
grep -r "from.*resources/" src/ - Migrate any remaining usages
- Delete the resource files
- Update any barrel exports (index.ts files)
- Run build to verify no import errors
Current blockers: InfiniteScroll.svelte still imports InfiniteScrollResource type
Patterns and Best Practices
Infinite Query Pattern
import { createInfiniteQuery } from '@tanstack/svelte-query'
import { IsInViewport } from 'runed'
// Create the query with thunk for reactivity
const query = createInfiniteQuery(() => ({
...queryOptions.list(filters),
initialData: serverData ? {
pages: [{ results: serverData.items, page: 1, totalPages: serverData.totalPages }],
pageParams: [1]
} : undefined,
initialDataUpdatedAt: 0
}))
// Flatten and deduplicate results
const rawResults = $derived(query.data?.pages.flatMap((page) => page.results) ?? [])
const items = $derived(Array.from(new Map(rawResults.map((item) => [item.id, item])).values()))
// Infinite scroll with IsInViewport
let sentinelEl = $state<HTMLElement>()
const inViewport = new IsInViewport(() => sentinelEl, { rootMargin: '200px' })
$effect(() => {
if (inViewport.current && query.hasNextPage && !query.isFetchingNextPage && !query.isLoading) {
query.fetchNextPage()
}
})
Debounced Search Pattern
let searchQuery = $state('')
let debouncedSearchQuery = $state('')
let debounceTimer: ReturnType<typeof setTimeout> | undefined
$effect(() => {
const query = searchQuery
if (debounceTimer) clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => {
debouncedSearchQuery = query
}, 300)
return () => { if (debounceTimer) clearTimeout(debounceTimer) }
})
// Use debouncedSearchQuery in the query, not searchQuery
const query = createInfiniteQuery(() => queryOptions.search(debouncedSearchQuery))
Type Assertions for Conditional Queries
When a query can return different types based on conditions, use type assertions:
const query = createInfiniteQuery(() => {
if (condition) {
return queryOptionsA()
}
return queryOptionsB() as unknown as ReturnType<typeof queryOptionsA>
})
Mutation Pattern
import { useUpdateParty } from '$lib/api/mutations/party.mutations'
const updatePartyMutation = useUpdateParty()
function handleSave() {
updatePartyMutation.mutate(
{ partyId, updates },
{
onSuccess: () => { /* handle success */ },
onError: (error) => { /* handle error */ }
}
)
}
// Use mutation state for UI
{#if updatePartyMutation.isPending}
<span>Saving...</span>
{/if}
Known Issues
Pre-existing Build Errors
The build has pre-existing errors unrelated to TanStack Query migration:
Cannot find module '$lib/paraglide/server'inhooks.server.tsCannot find module '$lib/paraglide/runtime'inhooks.tsCannot find module '$lib/paraglide/messages'in various components
These are paraglide i18n setup issues and should be ignored when checking for migration-related errors.
Duplicate Key Error Fix
When using infinite queries, the API may return duplicate items across pages. Always deduplicate:
const rawResults = $derived(query.data?.pages.flatMap((page) => page.results) ?? [])
const items = $derived(Array.from(new Map(rawResults.map((item) => [item.id, item])).values()))
File Locations
Query Options Factories
src/lib/api/queries/party.queries.tssrc/lib/api/queries/job.queries.tssrc/lib/api/queries/user.queries.tssrc/lib/api/queries/search.queries.ts
Mutation Hooks
src/lib/api/mutations/party.mutations.tssrc/lib/api/mutations/grid.mutations.tssrc/lib/api/mutations/job.mutations.ts
SSR Utilities
src/lib/query/ssr.ts
Reference Implementations
src/lib/components/sidebar/JobSelectionSidebar.svelte- Simple infinite querysrc/lib/components/sidebar/JobSkillSelectionSidebar.svelte- Infinite query with searchsrc/lib/components/sidebar/SearchContent.svelte- Infinite query with filters and deduplicationsrc/routes/teams/[id]/+page.svelte- SSR with initialDatasrc/routes/[username]/+page.svelte- Conditional queries (teams vs favorites)src/routes/teams/explore/+page.svelte- Simple infinite scroll page
Commands
# Run TypeScript check
pnpm run check
# Run development server
pnpm run dev
# Check for resource class imports
grep -r "from.*resources/" src/
# Check for createInfiniteScrollResource usage
grep -r "createInfiniteScrollResource" src/
Branch Information
- Base branch:
svelte-main - PR #442 branch:
devin/1764405731-tanstack-query-migration-phase2