框架
版本

ESLint 插件查询

TanStack Query 自带 ESLint 插件。此插件用于强制执行最佳实践,并帮助您避免常见错误。

安装

该插件是一个独立的包,您需要安装它:

bash
npm i -D @tanstack/eslint-plugin-query
npm i -D @tanstack/eslint-plugin-query

或者

bash
pnpm add -D @tanstack/eslint-plugin-query
pnpm add -D @tanstack/eslint-plugin-query

或者

bash
yarn add -D @tanstack/eslint-plugin-query
yarn add -D @tanstack/eslint-plugin-query

或者

bash
bun add -D @tanstack/eslint-plugin-query
bun add -D @tanstack/eslint-plugin-query

Flat Config (eslint.config.js)

推荐设置

要为我们的插件启用所有推荐的规则,请添加以下配置:

js
import pluginQuery from '@tanstack/eslint-plugin-query'

export default [
  ...pluginQuery.configs['flat/recommended'],
  // 任何其他配置...
]
import pluginQuery from '@tanstack/eslint-plugin-query'

export default [
  ...pluginQuery.configs['flat/recommended'],
  // 任何其他配置...
]

自定义设置

或者,您可以加载插件并仅配置您想要使用的规则:

js
import pluginQuery from '@tanstack/eslint-plugin-query'

export default [
  {
    plugins: {
      '@tanstack/query': pluginQuery,
    },
    rules: {
      '@tanstack/query/exhaustive-deps': 'error',
    },
  },
  // 任何其他配置...
]
import pluginQuery from '@tanstack/eslint-plugin-query'

export default [
  {
    plugins: {
      '@tanstack/query': pluginQuery,
    },
    rules: {
      '@tanstack/query/exhaustive-deps': 'error',
    },
  },
  // 任何其他配置...
]

Legacy Config (.eslintrc)

推荐设置

要为我们的插件启用所有推荐的规则,请在 extends 中添加 plugin:@tanstack/query/recommended

json
{
  "extends": ["plugin:@tanstack/query/recommended"]
}
{
  "extends": ["plugin:@tanstack/query/recommended"]
}

自定义设置

或者,将 @tanstack/query 添加到 plugins 部分,并配置您想要使用的规则:

json
{
  "plugins": ["@tanstack/query"],
  "rules": {
    "@tanstack/query/exhaustive-deps": "error"
  }
}
{
  "plugins": ["@tanstack/query"],
  "rules": {
    "@tanstack/query/exhaustive-deps": "error"
  }
}

规则


id: exhaustive-deps title: 查询键的详���依赖项

查询键应被视为查询函数的依赖数组:查询函数内部使用的每个变量都应添加到查询键中。 这确保了查询被独立缓存,并且当变量更改时查询会自动重新获取。

规则详情

此规则的不正确代码示例:

tsx
/* eslint "@tanstack/query/exhaustive-deps": "error" */

useQuery({
  queryKey: ['todo'],
  queryFn: () => api.getTodo(todoId),
})

const todoQueries = {
  detail: (id) => ({ queryKey: ['todo'], queryFn: () => api.getTodo(id) }),
}
/* eslint "@tanstack/query/exhaustive-deps": "error" */

useQuery({
  queryKey: ['todo'],
  queryFn: () => api.getTodo(todoId),
})

const todoQueries = {
  detail: (id) => ({ queryKey: ['todo'], queryFn: () => api.getTodo(id) }),
}

此规则的正确代码示例:

tsx
useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => api.getTodo(todoId),
})

const todoQueries = {
  detail: (id) => ({ queryKey: ['todo', id], queryFn: () => api.getTodo(id) }),
}
useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => api.getTodo(todoId),
})

const todoQueries = {
  detail: (id) => ({ queryKey: ['todo', id], queryFn: () => api.getTodo(id) }),
}

何时不使用

如果您不关心查询键的规则,则不需要此规则。

属性

  • ✅ 推荐
  • 🔧 可修复

id: infinite-query-property-order title: 确保无限查询中对推断敏感的属性的正确顺序

对于以下函数,由于类型推断,传递的对象中的属性顺序很重要:

  • useInfiniteQuery
  • useSuspenseInfiniteQuery
  • infiniteQueryOptions

正确的属性顺序如下:

  • queryFn
  • getPreviousPageParam
  • getNextPageParam

所有其他属性对顺序不敏感,因为它们不依赖于类型推断。

规则详情

此规则的不正确代码示例:

tsx
/* eslint "@tanstack/query/infinite-query-property-order": "warn" */
import { useInfiniteQuery } from '@tanstack/react-query'

const query = useInfiniteQuery({
  queryKey: ['projects'],
  getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
  queryFn: async ({ pageParam }) => {
    const response = await fetch(`/api/projects?cursor=${pageParam}`)
    return await response.json()
  },
  initialPageParam: 0,
  getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
  maxPages: 3,
})
/* eslint "@tanstack/query/infinite-query-property-order": "warn" */
import { useInfiniteQuery } from '@tanstack/react-query'

const query = useInfiniteQuery({
  queryKey: ['projects'],
  getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
  queryFn: async ({ pageParam }) => {
    const response = await fetch(`/api/projects?cursor=${pageParam}`)
    return await response.json()
  },
  initialPageParam: 0,
  getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
  maxPages: 3,
})

此规则的正确代码示例:

tsx
/* eslint "@tanstack/query/infinite-query-property-order": "warn" */
import { useInfiniteQuery } from '@tanstack/react-query'

const query = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: async ({ pageParam }) => {
    const response = await fetch(`/api/projects?cursor=${pageParam}`)
    return await response.json()
  },
  initialPageParam: 0,
  getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
  getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
  maxPages: 3,
})
/* eslint "@tanstack/query/infinite-query-property-order": "warn" */
import { useInfiniteQuery } from '@tanstack/react-query'

const query = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: async ({ pageParam }) => {
    const response = await fetch(`/api/projects?cursor=${pageParam}`)
    return await response.json()
  },
  initialPageParam: 0,
  getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
  getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
  maxPages: 3,
})

属性

  • ✅ 推荐
  • 🔧 可修复

id: no-rest-destructuring title: 禁止对查询结果使用对象剩余解构

在查询结果上使用对象剩余解构会自动订阅查询结果的每个字段,这可能会导致不必要的重新渲染。 这确保您只订阅实际需要的字段。

规则详情

此规则的不正确代码示例:

tsx
/* eslint "@tanstack/query/no-rest-destructuring": "warn" */

const useTodos = () => {
  const { data: todos, ...rest } = useQuery({
    queryKey: ['todos'],
    queryFn: () => api.getTodos(),
  })
  return { todos, ...rest }
}
/* eslint "@tanstack/query/no-rest-destructuring": "warn" */

const useTodos = () => {
  const { data: todos, ...rest } = useQuery({
    queryKey: ['todos'],
    queryFn: () => api.getTodos(),
  })
  return { todos, ...rest }
}

此规则的正确代码示例:

tsx
const todosQuery = useQuery({
  queryKey: ['todos'],
  queryFn: () => api.getTodos(),
})

// 普通的对象解构是可以的
const { data: todos } = todosQuery
const todosQuery = useQuery({
  queryKey: ['todos'],
  queryFn: () => api.getTodos(),
})

// 普通的对象解构是可以的
const { data: todos } = todosQuery

何时不使用

如果您手动设置 notifyOnChangeProps 选项,则可以禁用此规则。 由于您未使用跟踪查询,因此您有责任指定哪些道具应触发重新渲染。

属性

  • ✅ 推荐
  • 🔧 可修复

id: no-unstable-deps title: 禁止将查询钩子的结果直接放入 React 钩子依赖数组中

从以下查询钩子返回的对象在引用上不稳定

  • useQuery
  • useSuspenseQuery
  • useQueries
  • useSuspenseQueries
  • useInfiniteQuery
  • useSuspenseInfiniteQuery
  • useMutation

从这些钩子返回的对象不应直接放入 React 钩子(例如 useEffectuseMemouseCallback)的依赖数组中。 相反,应解构查询钩子的返回值,并将解构后的值传递到 React 钩子的依赖数组中。

规则详情

此规则的不正确代码示例:

tsx
/* eslint "@tanstack/query/no-unstable-deps": "warn" */
import { useCallback } from 'React'
import { useMutation } from '@tanstack/react-query'

function Component() {
  const mutation = useMutation({ mutationFn: (value: string) => value })
  const callback = useCallback(() => {
    mutation.mutate('hello')
  }, [mutation])
  return null
}
/* eslint "@tanstack/query/no-unstable-deps": "warn" */
import { useCallback } from 'React'
import { useMutation } from '@tanstack/react-query'

function Component() {
  const mutation = useMutation({ mutationFn: (value: string) => value })
  const callback = useCallback(() => {
    mutation.mutate('hello')
  }, [mutation])
  return null
}

此规则的正确代码示例:

tsx
/* eslint "@tanstack/query/no-unstable-deps": "warn" */
import { useCallback } from 'React'
import { useMutation } from '@tanstack/react-query'

function Component() {
  const { mutate } = useMutation({ mutationFn: (value: string) => value })
  const callback = useCallback(() => {
    mutate('hello')
  }, [mutate])
  return null
}
/* eslint "@tanstack/query/no-unstable-deps": "warn" */
import { useCallback } from 'React'
import { useMutation } from '@tanstack/react-query'

function Component() {
  const { mutate } = useMutation({ mutationFn: (value: string) => value })
  const callback = useCallback(() => {
    mutate('hello')
  }, [mutate])
  return null
}

属性

  • ✅ 推荐
  • 🔧 可修复

id: no-void-query-fn title: 禁止从查询函数返回 void

查询函数必须返回一个将被 TanStack Query 缓存的值。不返回值的函数(void 函数)可能导致意外行为,并可能表明实现中存在错误。

规则详情

此规则的不正确代码示例:

tsx
/* eslint "@tanstack/query/no-void-query-fn": "error" */

useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    await api.todos.fetch() // 函数未返回获取的数据
  },
})
/* eslint "@tanstack/query/no-void-query-fn": "error" */

useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    await api.todos.fetch() // 函数未返回获取的数据
  },
})

此规则的正确代码示例:

tsx
/* eslint "@tanstack/query/no-void-query-fn": "error" */
useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    const todos = await api.todos.fetch()
    return todos
  },
})
/* eslint "@tanstack/query/no-void-query-fn": "error" */
useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    const todos = await api.todos.fetch()
    return todos
  },
})

属性

  • ✅ 推荐
  • 🔧 可修复

id: stable-query-client title: 稳定的查询客户端

QueryClient 包含 QueryCache,因此您只想为应用程序的生命周期创建一个 QueryClient 实例 - _而不是_在每次渲染时创建一个新实例。

例外:允许在异步服务器组件内部创建一个新的 QueryClient,因为异步函数仅在服务器上调用一次。

规则详情

此规则的不正确代码示例:

tsx
/* eslint "@tanstack/query/stable-query-client": "error" */

function App() {
  const queryClient = new QueryClient()
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
/* eslint "@tanstack/query/stable-query-client": "error" */

function App() {
  const queryClient = new QueryClient()
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}

此规则的正确代码示例:

tsx
function App() {
  const [queryClient] = useState(() => new QueryClient())
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
function App() {
  const [queryClient] = useState(() => new QueryClient())
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
tsx
const queryClient = new QueryClient()
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
const queryClient = new QueryClient()
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
tsx
async function App() {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery(options)
}
async function App() {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery(options)
}

属性

  • ✅ 推荐
  • 🔧 可修复