- Add POST /admin/users endpoint with password hashing and role support - Add user creation dialog to admin users page - Migrate web from Next.js to Vite + TanStack Router - Fix Dialog component positioning for Tailwind CSS v4 - Add @source directive for @sase/ui package scanning - Add pl24 integration parsers and vehicle decode flow - Backup old Next.js app to apps/web-nj Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
2.9 KiB
Markdown
127 lines
2.9 KiB
Markdown
---
|
|
paths: "**/*.tsx", "**/*.ts", "**/*query*.ts", "**/*hook*.ts"
|
|
---
|
|
|
|
# TanStack Query v5 Corrections
|
|
|
|
Claude's training may reference TanStack Query v4 patterns. This project uses **v5**.
|
|
|
|
## Object Syntax Required
|
|
|
|
```typescript
|
|
/* ❌ v4 array syntax (removed in v5) */
|
|
useQuery(['todos'], fetchTodos, { staleTime: 5000 })
|
|
|
|
/* ✅ v5 object syntax only */
|
|
useQuery({
|
|
queryKey: ['todos'],
|
|
queryFn: fetchTodos,
|
|
staleTime: 5000
|
|
})
|
|
```
|
|
|
|
## Query Callbacks Removed
|
|
|
|
```typescript
|
|
/* ❌ v5 removed onSuccess/onError/onSettled from queries */
|
|
useQuery({
|
|
queryKey: ['todos'],
|
|
queryFn: fetchTodos,
|
|
onSuccess: (data) => console.log(data), // Removed!
|
|
})
|
|
|
|
/* ✅ Use useEffect instead */
|
|
const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
|
|
useEffect(() => {
|
|
if (data) console.log(data)
|
|
}, [data])
|
|
|
|
/* Note: Mutations still support callbacks */
|
|
useMutation({
|
|
mutationFn: addTodo,
|
|
onSuccess: () => {}, // Still works!
|
|
})
|
|
```
|
|
|
|
## isPending vs isLoading
|
|
|
|
```typescript
|
|
/* ❌ isLoading meaning changed in v5 */
|
|
if (isLoading) return <Loading />
|
|
|
|
/* ✅ Use isPending for initial load */
|
|
const { data, isPending } = useQuery(...)
|
|
if (isPending) return <Loading />
|
|
// isPending = no data yet
|
|
// isLoading = isPending && isFetching
|
|
```
|
|
|
|
## cacheTime → gcTime
|
|
|
|
```typescript
|
|
/* ❌ Renamed in v5 */
|
|
useQuery({
|
|
queryKey: ['todos'],
|
|
queryFn: fetchTodos,
|
|
cacheTime: 1000 * 60 * 60, // Error!
|
|
})
|
|
|
|
/* ✅ Use gcTime */
|
|
useQuery({
|
|
queryKey: ['todos'],
|
|
queryFn: fetchTodos,
|
|
gcTime: 1000 * 60 * 60,
|
|
})
|
|
```
|
|
|
|
## keepPreviousData → placeholderData
|
|
|
|
```typescript
|
|
/* ❌ Removed in v5 */
|
|
useQuery({
|
|
queryKey: ['todos', page],
|
|
queryFn: () => fetchTodos(page),
|
|
keepPreviousData: true, // Error!
|
|
})
|
|
|
|
/* ✅ Use placeholderData helper */
|
|
import { keepPreviousData } from '@tanstack/react-query'
|
|
|
|
useQuery({
|
|
queryKey: ['todos', page],
|
|
queryFn: () => fetchTodos(page),
|
|
placeholderData: keepPreviousData,
|
|
})
|
|
```
|
|
|
|
## Infinite Queries: initialPageParam Required
|
|
|
|
```typescript
|
|
/* ❌ v4 used undefined as first pageParam */
|
|
useInfiniteQuery({
|
|
queryKey: ['projects'],
|
|
queryFn: ({ pageParam = 0 }) => fetchProjects(pageParam),
|
|
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
})
|
|
|
|
/* ✅ v5 requires explicit initialPageParam */
|
|
useInfiniteQuery({
|
|
queryKey: ['projects'],
|
|
queryFn: ({ pageParam }) => fetchProjects(pageParam),
|
|
initialPageParam: 0, // Required!
|
|
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
})
|
|
```
|
|
|
|
## Quick Fixes
|
|
|
|
| If Claude suggests... | Use instead... |
|
|
|----------------------|----------------|
|
|
| `useQuery(['key'], fn, opts)` | `useQuery({ queryKey, queryFn, ...opts })` |
|
|
| `onSuccess` in useQuery | `useEffect` watching data |
|
|
| `cacheTime` | `gcTime` |
|
|
| `isLoading` for initial load | `isPending` |
|
|
| `keepPreviousData: true` | `placeholderData: keepPreviousData` |
|
|
| Missing `initialPageParam` | Add `initialPageParam: 0` (or appropriate value) |
|
|
| `useErrorBoundary` | `throwOnError` |
|