SuperAPI AI Directory

tanstack-query-intent-framework-shape-data-and-render-e-feb144a6

Data & analytics

View repository

Updated 10-16 00:02

About this skill

This skill builds on core/fetch-and-observe-queries and core/seed-placeholder-select-and-transform-data.

Setup

import { useQuery } from '@tanstack/react-query'

export function TodoCount() {
  const { data } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }, { id: 2 }],
    select: (todos) => todos.length,
  })
  return <p>{data ?? 0}</p>
}

Hooks and Components

Destructure only used result fields

import { useQuery } from '@tanstack/react-query'

export function Todos() {
  const { data, isFetching } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  return (
    <pre>
      {isFetching ? 'Refreshing ' : ''}
      {JSON.stringify(data ?? [])}
    </pre>
  )
}

Keep Vue query data immutable

import { ref } from 'vue'

export function editableTodo(todo: { id: number; title: string }) {
  return ref({ ...todo })
}

Use stable members in deps

import * as React from 'react'
import { useQuery } from '@tanstack/react-query'

export function TodosEffect() {
  const { data } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  React.useEffect(() => console.log(data?.length ?? 0), [data])
  return null
}

Common Mistakes

MEDIUM Rest destructuring

Wrong:

import { useQuery } from '@tanstack/react-query'

export function useTodos() {
  const { data, ...rest } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  return { data, rest }
}

Correct:

import { useQuery } from '@tanstack/react-query'

export function useTodos() {
  const { data, isFetching, error } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  return { data, isFetching, error }
}

Rest destructuring touches every tracked property and disables fine-grained render tracking.

Source: TanStack/query:docs/eslint/no-rest-destructuring.md

HIGH Query result in hook deps

Wrong:

import * as React from 'react'
import { useQuery } from '@tanstack/react-query'

export function Todos() {
  const query = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  React.useEffect(() => console.log(query.data), [query])
  return null
}

Correct:

import * as React from 'react'
import { useQuery } from '@tanstack/react-query'

export function Todos() {
  const { data } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => [{ id: 1 }],
  })
  React.useEffect(() => console.log(data), [data])
  return null
}

The top-level query result object is not referentially stable; destructured members are tracked.

Source: TanStack/query:docs/eslint/no-unstable-deps.md

HIGH Vue v-model mutates query result

Wrong:

<script setup lang="ts">
const todo = { id: 1, title: 'Ship' }
</script>
<template><input v-model="todo.title" /></template>

Correct:

<script setup lang="ts">
import { ref } from 'vue'
const editableTodo = ref({ id: 1, title: 'Ship' })
</script>
<template><input v-model="editableTodo.title" /></template>

Vue Query results are immutable; make a mutable copy for form state.

Source: TanStack/query:docs/framework/vue/reactivity.md

See also: compositions/enforce-query-best-practices-with-eslint for lint rules that protect render tracking.