框架
版本

概述

Solid Query 是 TanStack Query 的官方 SolidJS 适配器,它可以轻松地在您的 Web 应用程序中实现获取、缓存、同步和更新服务器状态

动机

SolidJS 作为一种快速、响应式和声明式的前端库,在构建用户界面方面越来越受欢迎。它开箱即用地提供了许多功能。像 createSignalcreateStore 这样的原语非常适合管理客户端状态。而且,与其他 UI 库不同,SolidJS 在管理异步数据方面有其独特的理念。createResource API 是处理 SolidJS 应用中服务器状态的绝佳原语。一个 resource 是一种特殊的信号,可以在数据处于加载状态时触发 Suspense 边界。

tsx
import { createResource, ErrorBoundary, Suspense } from 'solid-js'
import { render } from 'solid-js/web'

function App() {
  const [repository] = createResource(async () => {
    const result = await fetch('https://api.github.com/repos/TanStack/query')
    if (!result.ok) throw new Error('Failed to fetch data')
    return result.json()
  })

  return (
    <div>
      <div>Static Content</div>
      {/* An error while fetching will be caught by the ErrorBoundary */}
      <ErrorBoundary fallback={<div>Something went wrong!</div>}>
        {/* Suspense will trigger a loading state while the data is being fetched */}
        <Suspense fallback={<div>Loading...</div>}>
          <div>{repository()?.updated_at}</div>
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

const root = document.getElementById('root')

render(() => <App />, root!)
import { createResource, ErrorBoundary, Suspense } from 'solid-js'
import { render } from 'solid-js/web'

function App() {
  const [repository] = createResource(async () => {
    const result = await fetch('https://api.github.com/repos/TanStack/query')
    if (!result.ok) throw new Error('Failed to fetch data')
    return result.json()
  })

  return (
    <div>
      <div>Static Content</div>
      {/* An error while fetching will be caught by the ErrorBoundary */}
      <ErrorBoundary fallback={<div>Something went wrong!</div>}>
        {/* Suspense will trigger a loading state while the data is being fetched */}
        <Suspense fallback={<div>Loading...</div>}>
          <div>{repository()?.updated_at}</div>
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

const root = document.getElementById('root')

render(() => <App />, root!)

这太棒了!只需几行代码,您就可以从 API 获取数据并处理加载和错误状态。但是,随着应用程序复杂度的增加,您将需要更多功能来有效地管理服务器状态。这是因为服务器状态与客户端状态完全不同。首先,服务器状态

  • 远程存储在您不控制或不拥有的位置
  • 需要异步 API 进行获取和更新
  • 意味着共享所有权,并且可以在您不知情的情况下被其他人更改
  • 如果您不小心,可能会在您的应用程序中变得“过时”

一旦您掌握了应用程序中服务器状态的本质,**甚至会出现更多挑战**,例如

  • 缓存……(可能是编程中最难的事情)
  • 将对相同数据的多个请求去重为单个请求
  • 在后台更新“过时”数据
  • 知道数据何时“过时”
  • 尽快反映数据的更新
  • 分页和懒加载数据等性能优化
  • 管理服务器状态的内存和垃圾回收
  • 使用结构共享记忆查询结果

这就是Solid Query发挥作用的地方。该库封装了 createResource,并提供了一组钩子和实用工具来有效地管理服务器状态。它开箱即用,无需配置,并且可以根据您的喜好进行自定义,以适应您不断增长的应用程序。

从更技术的角度来看,Solid Query 可能会

  • 帮助您从应用程序中删除许多复杂且难以理解的代码行,并用几行 Solid Query 逻辑取而代之。
  • 使您的应用程序更易于维护,更易于构建新功能,而无需担心连接新的服务器状态数据源
  • 通过让您的应用程序比以往任何时候都感觉更快、响应更灵敏,直接影响您的最终用户。
  • 可能有助于您节省带宽并提高内存性能

别再说了,快给我看代码!

在下面的示例中,您可以看到 Solid Query 以其最基本和最简单的形式被用来获取 TanStack Query GitHub 项目本身的 GitHub 统计信息

tsx
import { ErrorBoundary, Suspense } from 'solid-js'
import {
  useQuery,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/solid-query'

function App() {
  const repositoryQuery = useQuery(() => ({
    queryKey: ['TanStack Query'],
    queryFn: async () => {
      const result = await fetch('https://api.github.com/repos/TanStack/query')
      if (!result.ok) throw new Error('Failed to fetch data')
      return result.json()
    },
    staleTime: 1000 * 60 * 5, // 5 minutes
    throwOnError: true, // Throw an error if the query fails
  }))

  return (
    <div>
      <div>Static Content</div>
      {/* An error while fetching will be caught by the ErrorBoundary */}
      <ErrorBoundary fallback={<div>Something went wrong!</div>}>
        {/* Suspense will trigger a loading state while the data is being fetched */}
        <Suspense fallback={<div>Loading...</div>}>
          {/* 
            The `data` property on a query is a SolidJS resource  
            so it will work with Suspense and transitions out of the box! 
          */}
          <div>{repositoryQuery.data?.updated_at}</div>
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

const root = document.getElementById('root')
const client = new QueryClient()

render(
  () => (
    <QueryClientProvider client={client}>
      <App />
    </QueryClientProvider>
  ),
  root!,
)
import { ErrorBoundary, Suspense } from 'solid-js'
import {
  useQuery,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/solid-query'

function App() {
  const repositoryQuery = useQuery(() => ({
    queryKey: ['TanStack Query'],
    queryFn: async () => {
      const result = await fetch('https://api.github.com/repos/TanStack/query')
      if (!result.ok) throw new Error('Failed to fetch data')
      return result.json()
    },
    staleTime: 1000 * 60 * 5, // 5 minutes
    throwOnError: true, // Throw an error if the query fails
  }))

  return (
    <div>
      <div>Static Content</div>
      {/* An error while fetching will be caught by the ErrorBoundary */}
      <ErrorBoundary fallback={<div>Something went wrong!</div>}>
        {/* Suspense will trigger a loading state while the data is being fetched */}
        <Suspense fallback={<div>Loading...</div>}>
          {/* 
            The `data` property on a query is a SolidJS resource  
            so it will work with Suspense and transitions out of the box! 
          */}
          <div>{repositoryQuery.data?.updated_at}</div>
        </Suspense>
      </ErrorBoundary>
    </div>
  )
}

const root = document.getElementById('root')
const client = new QueryClient()

render(
  () => (
    <QueryClientProvider client={client}>
      <App />
    </QueryClientProvider>
  ),
  root!,
)

嗯,这样做似乎需要更多代码才能实现相同的功能?

是的!但是,这几行代码开启了全新的可能性。在上面的示例中,您的查询将缓存 5 分钟,这意味着如果在 5 分钟内,应用程序中的任何组件挂载并使用相同的查询,它将不会重新获取数据,而是使用缓存的数据。这仅仅是 Solid Query 开箱即用提供的众多功能之一。其他一些功能包括

  • 自动重新获取:当查询“陈旧”(根据 staleTime 选项过期)时,查询会在后台自动重新获取
  • 自动缓存:查询默认情况下会被缓存并在您的应用程序中共享
  • 请求去重:多个组件可以共享同一个查询并只发出一个请求
  • 自动垃圾回收:当查询不再需要时,它们会被垃圾回收
  • 窗口焦点重新获取:当应用程序重新获得焦点时,查询会自动重新获取
  • 分页:内置分页支持
  • 请求取消:自动取消过时或不需要的请求
  • 轮询/实时:通过简单的 refetchInterval 选项,可以轻松地为查询添加轮询或实时更新
  • SSR 支持:Solid Query 非常适合服务器端渲染
  • 乐观更新:使用乐观更新轻松更新您的缓存
  • 以及更多...