From dc95e1ec60e4a65b7858535654e797e728be5cb4 Mon Sep 17 00:00:00 2001 From: abearxiong Date: Mon, 2 Mar 2026 12:52:22 +0800 Subject: [PATCH] feat(query): integrate @tanstack/react-query for improved data fetching and state management --- AGENTS.md | 23 +++++++++++++-- package.json | 5 ++-- pnpm-lock.yaml | 66 +++++++++++++++++++++++++++---------------- src/modules/query.ts | 6 +++- src/routes/__root.tsx | 34 ++++++++++++---------- 5 files changed, 89 insertions(+), 45 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c878589..6b64f68 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,19 +25,36 @@ src/ ``` pages/page-app/ ├── components/ # 模块专属组件 -├── store/ # 模块状态管理 -└── module/ # 模块功能函数 +├── hooks/ # 模块 React Query hooks(API 查询封装) +├── modules/ # 模块功能函数(UI 组件、工具函数等) +└── store/ # 模块状态管理(Zustand) ``` +### hooks/ 文件夹说明 + +每个模块的 `hooks/` 文件夹用于封装与该模块相关的 React Query hooks: + +- **use-api-query.ts**: 使用 `@tanstack/react-query` 的 `useQuery` 封装 API 调用 + - 定义 `queryKeys` 常量用于缓存标识 + - 封装 `useQuery` hooks 用于数据获取(GET 请求) + - 封装 `useMutation` hooks 用于数据修改(POST/PUT/DELETE 请求) + - 支持预取(prefetch)和无限滚动(infinite query) +- **index.ts**: 导出模块所有 hooks,便于统一导入使用 + ### 状态和数据获取 +- **@tanstack/react-query** 用于数据获取、缓存和状态管理 + - 在模块的 `hooks/` 文件夹中封装 API 调用 + - QueryClient 实例位于 `src/modules/query.ts` + - 在 `src/routes/__root.tsx` 中通过 `QueryClientProvider` 提供 - **Zustand** 用于全局状态管理 -- **@kevisual/query** 用于数据获取(QueryClient 实例位于 `src/modules/query.ts`) +- **@kevisual/query** 用于底层 API 请求封装 - **React Hook Form** 用于表单管理 ## 核心依赖 - **@base-ui/react**: Headless UI 基础组件 +- **@tanstack/react-query**: 数据获取、缓存和状态管理(配合 hooks/ 使用) - **@tanstack/react-router**: 基于 TanStack Router 插件的文件路由 - **class-variance-authority**: 基于变体的样式系统 - **clsx + tailwind-merge**: 通过 `cn()` 提供 className 工具函数 diff --git a/package.json b/package.json index 2784dbc..1213b29 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@kevisual/api": "^0.0.60", "@kevisual/context": "^0.0.8", "@kevisual/router": "0.0.84", + "@tanstack/react-query": "^5.90.21", "@tanstack/react-router": "^1.163.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -46,8 +47,8 @@ "@kevisual/types": "^0.0.12", "@tailwindcss/vite": "^4.2.1", "@tanstack/react-router-devtools": "^1.163.3", - "@tanstack/router-plugin": "^1.163.3", - "@types/node": "^25.3.2", + "@tanstack/router-plugin": "^1.164.0", + "@types/node": "^25.3.3", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd62a89..e28e6cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@kevisual/router': specifier: 0.0.84 version: 0.0.84 + '@tanstack/react-query': + specifier: ^5.90.21 + version: 5.90.21(react@19.2.4) '@tanstack/react-router': specifier: ^1.163.3 version: 1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -77,16 +80,16 @@ importers: version: 0.0.12 '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.2.1(vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)) + version: 4.2.1(vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)) '@tanstack/react-router-devtools': specifier: ^1.163.3 version: 1.163.3(@tanstack/react-router@1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.163.3)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/router-plugin': - specifier: ^1.163.3 - version: 1.163.3(@tanstack/react-router@1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)) + specifier: ^1.164.0 + version: 1.164.0(@tanstack/react-router@1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)) '@types/node': - specifier: ^25.3.2 - version: 25.3.2 + specifier: ^25.3.3 + version: 25.3.3 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -95,7 +98,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^5.1.4 - version: 5.1.4(vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)) + version: 5.1.4(vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)) dotenv: specifier: ^17.3.1 version: 17.3.1 @@ -113,7 +116,7 @@ importers: version: 5.9.3 vite: specifier: v8.0.0-beta.15 - version: 8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) + version: 8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) packages: @@ -853,6 +856,14 @@ packages: resolution: {integrity: sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww==} engines: {node: '>=20.19'} + '@tanstack/query-core@5.90.20': + resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} + + '@tanstack/react-query@5.90.21': + resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} + peerDependencies: + react: ^18 || ^19 + '@tanstack/react-router-devtools@1.163.3': resolution: {integrity: sha512-42VMkV/2Z8ro7xzblPBRNZIEmCNXMzm2jD68G52p2qhjXm38wGpg46qneAESN9FtTQeVWk5aSXs47/jt7lkzmw==} engines: {node: '>=20.19'} @@ -892,12 +903,12 @@ packages: csstype: optional: true - '@tanstack/router-generator@1.163.3': - resolution: {integrity: sha512-i2rWRtqY/yCYUDXva1li4zeDP20oFjMt/wh9RnGJCrKSLWrvEGnxAOSyXgiOsoJnU96TTQ0mUDbGfXsSTupeZQ==} + '@tanstack/router-generator@1.164.0': + resolution: {integrity: sha512-Uiyj+RtW0kdeqEd8NEd3Np1Z2nhJ2xgLS8U+5mTvFrm/s3xkM2LYjJHoLzc6am7sKPDsmeF9a4/NYq3R7ZJP0Q==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.163.3': - resolution: {integrity: sha512-JOUYuUX2N9ZHnmkmvmiGzXGbkvrur/5BfW/+vpiZzuifSyvdc0XsfwkTpjvwWx9ymp4ZshSVKiQQKQi09YweIw==} + '@tanstack/router-plugin@1.164.0': + resolution: {integrity: sha512-cZPsEMhqzyzmuPuDbsTAzBZaT+cj0pGjwdhjxJfPCM06Ax8v4tFR7n/Ug0UCwnNAUEmKZWN3lA9uT+TxXnk9PQ==} engines: {node: '>=20.19'} peerDependencies: '@rsbuild/core': '>=1.0.2' @@ -943,8 +954,8 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/node@25.3.2': - resolution: {integrity: sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==} + '@types/node@25.3.3': + resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} @@ -2169,15 +2180,22 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/vite@4.2.1(vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))': + '@tailwindcss/vite@4.2.1(vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))': dependencies: '@tailwindcss/node': 4.2.1 '@tailwindcss/oxide': 4.2.1 tailwindcss: 4.2.1 - vite: 8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) + vite: 8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) '@tanstack/history@1.161.4': {} + '@tanstack/query-core@5.90.20': {} + + '@tanstack/react-query@5.90.21(react@19.2.4)': + dependencies: + '@tanstack/query-core': 5.90.20 + react: 19.2.4 + '@tanstack/react-router-devtools@1.163.3(@tanstack/react-router@1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.163.3)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/react-router': 1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -2226,7 +2244,7 @@ snapshots: optionalDependencies: csstype: 3.2.3 - '@tanstack/router-generator@1.163.3': + '@tanstack/router-generator@1.164.0': dependencies: '@tanstack/router-core': 1.163.3 '@tanstack/router-utils': 1.161.4 @@ -2239,7 +2257,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.163.3(@tanstack/react-router@1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))': + '@tanstack/router-plugin@1.164.0(@tanstack/react-router@1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) @@ -2248,7 +2266,7 @@ snapshots: '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 '@tanstack/router-core': 1.163.3 - '@tanstack/router-generator': 1.163.3 + '@tanstack/router-generator': 1.164.0 '@tanstack/router-utils': 1.161.4 '@tanstack/virtual-file-routes': 1.161.4 chokidar: 3.6.0 @@ -2256,7 +2274,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.163.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - vite: 8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) + vite: 8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -2304,7 +2322,7 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@types/node@25.3.2': + '@types/node@25.3.3': dependencies: undici-types: 7.18.2 @@ -2316,7 +2334,7 @@ snapshots: dependencies: csstype: 3.2.3 - '@vitejs/plugin-react@5.1.4(vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))': + '@vitejs/plugin-react@5.1.4(vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -2324,7 +2342,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) + vite: 8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -2783,7 +2801,7 @@ snapshots: dependencies: react: 19.2.4 - vite@8.0.0-beta.15(@types/node@25.3.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0): + vite@8.0.0-beta.15(@types/node@25.3.3)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0): dependencies: '@oxc-project/runtime': 0.114.0 lightningcss: 1.31.1 @@ -2792,7 +2810,7 @@ snapshots: rolldown: 1.0.0-rc.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 esbuild: 0.27.3 fsevents: 2.3.3 jiti: 2.6.1 diff --git a/src/modules/query.ts b/src/modules/query.ts index 6b7ef4f..b45efe0 100644 --- a/src/modules/query.ts +++ b/src/modules/query.ts @@ -1,6 +1,8 @@ import { Query, DataOpts } from '@kevisual/query'; import { QueryLoginBrowser } from '@kevisual/api/query-login' import { useContextKey } from '@kevisual/context'; +import { QueryClient } from '@tanstack/react-query'; + export const query = useContextKey('query', new Query({ url: '/api/router', })); @@ -11,4 +13,6 @@ export const queryClient = useContextKey('queryClient', new Query({ export const queryLogin = useContextKey('queryLogin', new QueryLoginBrowser({ query: query -})); \ No newline at end of file +})); + +export const stackQueryClient = useContextKey('stackQueryClient', new QueryClient()); \ No newline at end of file diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 28d78f7..d67ae5d 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -6,6 +6,8 @@ import { AuthProvider } from '@/pages/auth' import { TooltipProvider } from '@/components/ui/tooltip' import { useLayoutStore } from '@/pages/auth/store'; import { useShallow } from 'zustand/shallow'; +import { stackQueryClient } from '@/modules/query' +import { QueryClientProvider } from '@tanstack/react-query' import clsx from 'clsx'; export const Route = createRootRoute({ component: RootComponent, @@ -17,20 +19,22 @@ function RootComponent() { showBaseHeader: state.showBaseHeader, }))); return ( -
- - - -
- -
-
-
- - -
+ +
+ + + +
+ +
+
+
+ + +
+
) } \ No newline at end of file