Why TanStack Query
Server state is async, can go stale, needs caching. TanStack Query handles all of this.
Setup + useQuery
const [qc] = useState(() => new QueryClient({ defaultOptions: { queries: { staleTime: 60_000 } } }))
function usePost(id) {
return useQuery({
queryKey: ['posts', id],
queryFn: () => fetch('/api/posts/' + id).then(r => r.json()),
enabled: id > 0,
})
}
useMutation + Optimistic Update
const mutation = useMutation({
mutationFn: (data) => fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) }).then(r => r.json()),
onMutate: async (newPost) => {
await queryClient.cancelQueries({ queryKey: ['posts'] })
const prev = queryClient.getQueryData(['posts'])
queryClient.setQueryData(['posts'], old => [...(old || []), { ...newPost, id: 'temp' }])
return { prev }
},
onError: (_, __, ctx) => queryClient.setQueryData(['posts'], ctx?.prev),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
})
Infinite Scroll
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 1 }) => fetch('/api/posts?page=' + pageParam).then(r => r.json()),
getNextPageParam: last => last.nextPage,
initialPageParam: 1,
})
const posts = data?.pages.flatMap(p => p.posts) ?? []
-> Debug API responses with the JSON Viewer.