From 18ac6af24d978291bf7dcd5867532103cbea6afe Mon Sep 17 00:00:00 2001 From: "yanqi.zong" Date: Sat, 3 May 2025 14:47:55 -0700 Subject: [PATCH 1/7] feat: update translation config --- .github/workflows/autofix.yml | 1 + .github/workflows/labeler.yml | 1 + .github/workflows/pr.yml | 2 + .github/workflows/translate.yml | 43 ++++++++ translation.config.mjs | 189 ++++++++++++++++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 .github/workflows/translate.yml create mode 100644 translation.config.mjs diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 107a3ef6226..ee7a8037abd 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -16,6 +16,7 @@ jobs: autofix: name: autofix runs-on: ubuntu-latest + if: github.repository_owner == 'TanStack' steps: - name: Checkout uses: actions/checkout@v4.2.2 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index a271cb7e680..1906aeb52d2 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -6,6 +6,7 @@ on: jobs: triage: runs-on: ubuntu-latest + if: github.repository_owner == 'TanStack' steps: - uses: actions/labeler@v4.3.0 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5ef53b56cef..c3ef6d378e0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,6 +20,7 @@ jobs: test: name: Test runs-on: ubuntu-latest + if: github.repository_owner == 'TanStack' steps: - name: Checkout uses: actions/checkout@v4.2.2 @@ -47,6 +48,7 @@ jobs: preview: name: Preview runs-on: ubuntu-latest + if: github.repository_owner == 'TanStack' steps: - name: Checkout uses: actions/checkout@v4.2.2 diff --git a/.github/workflows/translate.yml b/.github/workflows/translate.yml new file mode 100644 index 00000000000..971ee72983a --- /dev/null +++ b/.github/workflows/translate.yml @@ -0,0 +1,43 @@ +name: Translate Documentation + +on: + schedule: + - cron: + '0 20 * * *' # Daily at 20:00 UTC (DeepSeek API off-peak pricing window 16:30-00:30 UTC) + # Pacific Time: 1:00 PM PDT / 12:00 PM PST + # Off-peak window in PT: ~9:30 AM to 5:30 PM PDT / ~8:30 AM to 4:30 PM PST + workflow_dispatch: # Allow manual triggering + inputs: + custom_arguments: + description: 'Custom arguments to pass to the translation package command' + required: false + type: string + +# Add permissions needed for creating PRs +permissions: + contents: write + pull-requests: write + +jobs: + translate: + runs-on: ubuntu-latest + steps: + # Use the translate-docs-action + - name: Translate documentation + uses: TanStack-dev/translate-docs-action@main + with: + # Required inputs + api_key: ${{ secrets.OPENAI_API_KEY }} + + # Optional inputs with their default values shown + github_token: ${{ secrets.GITHUB_TOKEN }} + custom_arguments: ${{ github.event.inputs.custom_arguments }} + # translation_package: '@tanstack-dev/translate-docs' + # base_branch: 'main' + # pr_branch: 'docs/update-translations' + # pr_title: 'Update translations' + # pr_body: 'This PR updates the documentation translations automatically.\n\nGenerated by the translate workflow.' + # commit_message: 'docs: update documentation translations' + # add_paths: 'docs/**' + # enable_formatting: 'true' + # format_command: 'pnpm prettier:write' diff --git a/translation.config.mjs b/translation.config.mjs new file mode 100644 index 00000000000..f7446433a53 --- /dev/null +++ b/translation.config.mjs @@ -0,0 +1,189 @@ +export default { + langs: { + 'zh-Hans': { + code: 'zh-Hans', + name: 'Simplified Chinese', + // 翻译规则和指南 + guide: ` + - For technical terms that should not be fully translated, use the format: "中文翻译 (English term)" + Example: "服务端渲染 (SSR)" instead of just "SSR" or just "服务端渲染" + - Add a space between Chinese characters and English words/symbols to improve readability + - Maintain consistent translations for common terms across the entire document +`, + // 常见技术术语翻译词典 + // 格式: 'English term': '中文翻译' + terms: {}, + }, +// 'zh-Hant': { +// code: 'zh-Hant', +// name: 'Traditional Chinese', +// // 翻譯規則和指南 +// guide: ` +// - For technical terms that should not be fully translated, use the format: "繁體中文翻譯 (English term)" +// Example: "伺服器渲染 (SSR)" instead of just "SSR" or just "伺服器渲染" +// - Add a space between Chinese characters and English words/symbols to improve readability +// - Maintain consistent translations for common terms across the entire document +// `, +// // 常見技術術語翻譯詞典 +// // 格式: 'English term': '繁體中文翻譯' +// terms: {}, +// }, +// ja: { +// code: 'ja', +// name: 'Japanese', +// guide: ` +// - For technical terms that should not be fully translated, use the format: "日本語訳 (English term)" +// Example: "サーバーサイドレンダリング (SSR)" instead of just "SSR" or just "サーバーサイドレンダリング" +// - Maintain consistent translations for common terms across the entire document +// - Use katakana for foreign technical terms where appropriate +// `, +// terms: {}, +// }, +// es: { +// code: 'es', +// name: 'Spanish', +// guide: ` +// - For technical terms that should not be fully translated, use the format: "Traducción en español (English term)" +// Example: "Renderizado del lado del servidor (SSR)" instead of just "SSR" or just "Renderizado del lado del servidor" +// - Maintain consistent translations for common terms across the entire document +// - Use formal "usted" form instead of informal "tú" for instructions +// `, +// terms: {}, +// }, +// de: { +// code: 'de', +// name: 'German', +// guide: ` +// - For technical terms that should not be fully translated, use the format: "Deutsche Übersetzung (English term)" +// Example: "Server-seitiges Rendering (SSR)" instead of just "SSR" or just "Server-seitiges Rendering" +// - Maintain consistent translations for common terms across the entire document +// - Follow German capitalization rules for nouns +// `, +// terms: {}, +// }, +// fr: { +// code: 'fr', +// name: 'French', +// guide: ` +// - For technical terms that should not be fully translated, use the format: "Traduction française (English term)" +// Example: "Rendu côté serveur (SSR)" instead of just "SSR" or just "Rendu côté serveur" +// - Maintain consistent translations for common terms across the entire document +// - Use proper French punctuation with spaces before certain punctuation marks +// `, +// terms: {}, +// }, +// ru: { +// code: 'ru', +// name: 'Russian', +// guide: ` +// - For technical terms that should not be fully translated, use the format: "Русский перевод (English term)" +// Example: "Рендеринг на стороне сервера (SSR)" instead of just "SSR" or just "Рендеринг на стороне сервера" +// - Maintain consistent translations for common terms across the entire document +// - Use proper Russian cases for technical terms where appropriate +// `, +// terms: {}, +// }, +// ar: { +// code: 'ar', +// name: 'Arabic', +// guide: ` +// - For technical terms that should not be fully translated, use the format: "الترجمة العربية (English term)" +// Example: "العرض من جانب الخادم (SSR)" instead of just "SSR" or just "العرض من جانب الخادم" +// - Maintain consistent translations for common terms across the entire document +// - Arabic text should flow right-to-left, but keep code examples and technical terms left-to-right +// `, +// terms: {}, +// }, + }, + docsRoot: 'docs', + docsContext: `TanStack Query (formerly known as React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your web applications a breeze. + +## Motivation + +Most core web frameworks **do not** come with an opinionated way of fetching or updating data in a holistic way. Because of this developers end up building either meta-frameworks which encapsulate strict opinions about data-fetching, or they invent their own ways of fetching data. This usually means cobbling together component-based state and side-effects, or using more general purpose state management libraries to store and provide asynchronous data throughout their apps. + +While most traditional state management libraries are great for working with client state, they are **not so great at working with async or server state**. This is because **server state is totally different**. For starters, server state: + +- Is persisted remotely in a location you may not control or own +- Requires asynchronous APIs for fetching and updating +- Implies shared ownership and can be changed by other people without your knowledge +- Can potentially become "out of date" in your applications if you're not careful + +Once you grasp the nature of server state in your application, **even more challenges will arise** as you go, for example: + +- Caching... (possibly the hardest thing to do in programming) +- Deduping multiple requests for the same data into a single request +- Updating "out of date" data in the background +- Knowing when data is "out of date" +- Reflecting updates to data as quickly as possible +- Performance optimizations like pagination and lazy loading data +- Managing memory and garbage collection of server state +- Memoizing query results with structural sharing + +If you're not overwhelmed by that list, then that must mean that you've probably solved all of your server state problems already and deserve an award. However, if you are like a vast majority of people, you either have yet to tackle all or most of these challenges and we're only scratching the surface! + +TanStack Query is hands down one of the _best_ libraries for managing server state. It works amazingly well **out-of-the-box, with zero-config, and can be customized** to your liking as your application grows. + +TanStack Query allows you to defeat and overcome the tricky challenges and hurdles of _server state_ and control your app data before it starts to control you. + +On a more technical note, TanStack Query will likely: + +- Help you remove **many** lines of complicated and misunderstood code from your application and replace with just a handful of lines of TanStack Query logic. +- Make your application more maintainable and easier to build new features without worrying about wiring up new server state data sources +- Have a direct impact on your end-users by making your application feel faster and more responsive than ever before. +- Potentially help you save on bandwidth and increase memory performance`, + copyPath: [ + // === Angular === + // For angular guides + 'framework/angular/guides/does-this-replace-client-state', + 'framework/angular/guides/filters', + 'framework/angular/guides/important-defaults', + 'framework/angular/guides/network-mode', + 'framework/angular/guides/scroll-restoration', + 'framework/angular/guides/window-focus-refetching', + // For angular reference + 'framework/angular/reference/**', + '!framework/angular/reference/functions/injectquery', + '!framework/angular/reference/functions/injectmutation', + + // === Svelte === + 'framework/svelte/reference/**', + + // === Solid === + // For solid community + 'framework/solid/community/**', + // For solid guides + 'framework/solid/guides/**', + '!framework/solid/guides/advanced-ssr', + '!framework/solid/guides/ssr', + '!framework/solid/guides/suspense', + // For solid plugins + 'framework/solid/plugins/**', + // For solid reference, only useQuery needs translated + 'framework/solid/reference/**', + '!framework/vue/reference/useQuery', + // === Vue === + // For vue community + 'framework/vue/community/tkdodos-blog', + // guides + 'framework/vue/guides/caching', + 'framework/vue/guides/filters', + 'framework/vue/guides/important-defaults', + 'framework/vue/guides/infinite-queries', + 'framework/vue/guides/initial-query-data', + 'framework/vue/guides/network-mode', + 'framework/vue/guides/optimistic-updates', + 'framework/vue/guides/query-cancellation', + 'framework/vue/guides/query-functions', + 'framework/vue/guides/query-invalidation', + 'framework/vue/guides/query-keys', + 'framework/vue/guides/query-options', + 'framework/vue/guides/scroll-restoration', + 'framework/vue/guides/updates-from-mutation-responses', + 'framework/vue/guides/window-focus-refetching', + // Plugins + 'framework/vue/plugins/broadcastQueryClient', + // Reference + 'framework/vue/reference/**', + ], +} From 1efc8667210a302cd3aadbbaf60738d5aca3d08b Mon Sep 17 00:00:00 2001 From: "yanqi.zong" Date: Tue, 6 May 2025 09:57:08 -0700 Subject: [PATCH 2/7] docs: update translation --- docs/zh-hans/config.json | 1372 +++++++++++++++++ docs/zh-hans/eslint/eslint-plugin-query.md | 101 ++ docs/zh-hans/eslint/exhaustive-deps.md | 46 + .../eslint/infinite-query-property-order.md | 64 + docs/zh-hans/eslint/no-rest-destructuring.md | 46 + docs/zh-hans/eslint/no-unstable-deps.md | 57 + docs/zh-hans/eslint/stable-query-client.md | 62 + ...pclient-and-other-data-fetching-clients.md | 49 + docs/zh-hans/framework/angular/devtools.md | 116 ++ .../guides/background-fetching-indicators.md | 55 + .../framework/angular/guides/caching.md | 39 + .../angular/guides/default-query-function.md | 49 + .../angular/guides/dependent-queries.md | 65 + .../angular/guides/disabling-queries.md | 119 ++ .../guides/does-this-replace-client-state.md | 12 + .../framework/angular/guides/filters.md | 8 + .../angular/guides/important-defaults.md | 16 + .../angular/guides/infinite-queries.md | 243 +++ .../angular/guides/initial-query-data.md | 139 ++ .../guides/invalidations-from-mutations.md | 41 + .../angular/guides/mutation-options.md | 24 + .../framework/angular/guides/mutations.md | 299 ++++ .../framework/angular/guides/network-mode.md | 8 + .../angular/guides/optimistic-updates.md | 172 +++ .../angular/guides/paginated-queries.md | 106 ++ .../angular/guides/parallel-queries.md | 45 + .../angular/guides/placeholder-query-data.md | 74 + .../framework/angular/guides/queries.md | 125 ++ .../angular/guides/query-cancellation.md | 107 ++ .../angular/guides/query-functions.md | 102 ++ .../angular/guides/query-invalidation.md | 117 ++ .../framework/angular/guides/query-keys.md | 76 + .../framework/angular/guides/query-options.md | 57 + .../framework/angular/guides/query-retries.md | 64 + .../angular/guides/scroll-restoration.md | 8 + .../angular/guides/window-focus-refetching.md | 42 + .../zh-hans/framework/angular/installation.md | 35 + docs/zh-hans/framework/angular/overview.md | 114 ++ docs/zh-hans/framework/angular/quick-start.md | 119 ++ .../functions/infinitequeryoptions.md | 134 ++ .../functions/injectinfinitequery.md | 190 +++ .../reference/functions/injectisfetching.md | 37 + .../reference/functions/injectismutating.md | 36 + .../reference/functions/injectmutation.md | 48 + .../functions/injectmutationstate.md | 41 + .../reference/functions/injectqueries.md | 39 + .../reference/functions/injectquery.md | 318 ++++ .../reference/functions/injectqueryclient.md | 90 ++ .../functions/provideangularquery.md | 66 + .../reference/functions/providequeryclient.md | 29 + .../reference/functions/queryoptions.md | 146 ++ .../framework/angular/reference/index.md | 52 + .../interfaces/basemutationnarrowing.md | 98 ++ .../interfaces/basequerynarrowing.md | 74 + .../interfaces/createbasequeryoptions.md | 24 + .../interfaces/createinfinitequeryoptions.md | 26 + .../interfaces/createmutationoptions.md | 22 + .../interfaces/createqueryoptions.md | 22 + .../interfaces/injectmutationstateoptions.md | 20 + .../type-aliases/createbasemutationresult.md | 34 + .../type-aliases/createbasequeryresult.md | 24 + .../type-aliases/createinfinitequeryresult.md | 22 + .../type-aliases/createmutateasyncfunction.md | 26 + .../type-aliases/createmutatefunction.md | 34 + .../type-aliases/createmutationresult.md | 28 + .../type-aliases/createqueryresult.md | 22 + .../definedcreateinfinitequeryresult.md | 24 + .../type-aliases/definedcreatequeryresult.md | 24 + .../definedinitialdatainfiniteoptions.md | 36 + .../type-aliases/definedinitialdataoptions.md | 34 + .../type-aliases/nonundefinedguard.md | 20 + .../reference/type-aliases/queriesoptions.md | 26 + .../reference/type-aliases/queriesresults.md | 26 + .../undefinedinitialdatainfiniteoptions.md | 36 + .../undefinedinitialdataoptions.md | 34 + docs/zh-hans/framework/angular/typescript.md | 292 ++++ docs/zh-hans/framework/angular/zoneless.md | 12 + .../react/community/community-projects.md | 160 ++ .../framework/react/community/tkdodos-blog.md | 87 ++ docs/zh-hans/framework/react/comparison.md | 112 ++ docs/zh-hans/framework/react/devtools.md | 197 +++ docs/zh-hans/framework/react/graphql.md | 56 + .../framework/react/guides/advanced-ssr.md | 414 +++++ .../guides/background-fetching-indicators.md | 63 + .../zh-hans/framework/react/guides/caching.md | 40 + .../react/guides/default-query-function.md | 57 + .../react/guides/dependent-queries.md | 96 ++ .../react/guides/disabling-queries.md | 129 ++ .../guides/does-this-replace-client-state.md | 57 + .../zh-hans/framework/react/guides/filters.md | 91 ++ .../react/guides/important-defaults.md | 43 + .../react/guides/infinite-queries.md | 262 ++++ .../react/guides/initial-query-data.md | 175 +++ .../guides/invalidations-from-mutations.md | 42 + .../guides/migrating-to-react-query-3.md | 541 +++++++ .../guides/migrating-to-react-query-4.md | 384 +++++ .../framework/react/guides/migrating-to-v5.md | 355 +++++ .../framework/react/guides/mutations.md | 413 +++++ .../framework/react/guides/network-mode.md | 47 + .../react/guides/optimistic-updates.md | 187 +++ .../react/guides/paginated-queries.md | 94 ++ .../react/guides/parallel-queries.md | 57 + .../react/guides/placeholder-query-data.md | 102 ++ .../framework/react/guides/prefetching.md | 435 ++++++ .../zh-hans/framework/react/guides/queries.md | 146 ++ .../react/guides/query-cancellation.md | 194 +++ .../framework/react/guides/query-functions.md | 120 ++ .../react/guides/query-invalidation.md | 136 ++ .../framework/react/guides/query-keys.md | 103 ++ .../framework/react/guides/query-options.md | 52 + .../framework/react/guides/query-retries.md | 81 + .../react/guides/render-optimizations.md | 75 + .../react/guides/request-waterfalls.md | 339 ++++ .../react/guides/scroll-restoration.md | 11 + docs/zh-hans/framework/react/guides/ssr.md | 484 ++++++ .../framework/react/guides/suspense.md | 222 +++ .../zh-hans/framework/react/guides/testing.md | 168 ++ .../guides/updates-from-mutation-responses.md | 83 + .../react/guides/window-focus-refetching.md | 106 ++ docs/zh-hans/framework/react/installation.md | 92 ++ docs/zh-hans/framework/react/overview.md | 102 ++ .../react/plugins/broadcastQueryClient.md | 61 + .../plugins/createAsyncStoragePersister.md | 119 ++ .../react/plugins/createPersister.md | 135 ++ .../plugins/createSyncStoragePersister.md | 155 ++ .../react/plugins/persistQueryClient.md | 287 ++++ docs/zh-hans/framework/react/quick-start.md | 78 + docs/zh-hans/framework/react/react-native.md | 117 ++ .../react/reference/QueryClientProvider.md | 23 + .../reference/QueryErrorResetBoundary.md | 65 + .../framework/react/reference/hydration.md | 129 ++ .../react/reference/infiniteQueryOptions.md | 20 + .../framework/react/reference/queryOptions.md | 25 + .../react/reference/useInfiniteQuery.md | 93 ++ .../react/reference/useIsFetching.md | 26 + .../react/reference/useIsMutating.md | 26 + .../framework/react/reference/useMutation.md | 161 ++ .../react/reference/useMutationState.md | 82 + .../reference/usePrefetchInfiniteQuery.md | 38 + .../react/reference/usePrefetchQuery.md | 24 + .../framework/react/reference/useQueries.md | 68 + .../framework/react/reference/useQuery.md | 255 +++ .../react/reference/useQueryClient.md | 18 + .../reference/useQueryErrorResetBoundary.md | 29 + .../reference/useSuspenseInfiniteQuery.md | 31 + .../react/reference/useSuspenseQueries.md | 31 + .../react/reference/useSuspenseQuery.md | 30 + docs/zh-hans/framework/react/typescript.md | 251 +++ docs/zh-hans/framework/react/videos.md | 72 + .../solid/community/community-projects.md | 10 + .../framework/solid/community/tkdodos-blog.md | 8 + docs/zh-hans/framework/solid/devtools.md | 82 + .../framework/solid/guides/advanced-ssr.md | 7 + .../guides/background-fetching-indicators.md | 10 + .../zh-hans/framework/solid/guides/caching.md | 8 + .../solid/guides/default-query-function.md | 15 + .../solid/guides/dependent-queries.md | 15 + .../solid/guides/disabling-queries.md | 15 + .../guides/does-this-replace-client-state.md | 10 + .../zh-hans/framework/solid/guides/filters.md | 8 + .../solid/guides/important-defaults.md | 8 + .../solid/guides/infinite-queries.md | 15 + .../solid/guides/initial-query-data.md | 15 + .../guides/invalidations-from-mutations.md | 16 + .../framework/solid/guides/mutations.md | 16 + .../framework/solid/guides/network-mode.md | 8 + .../solid/guides/optimistic-updates.md | 16 + .../solid/guides/paginated-queries.md | 15 + .../solid/guides/parallel-queries.md | 15 + .../solid/guides/placeholder-query-data.md | 17 + .../framework/solid/guides/prefetching.md | 17 + .../zh-hans/framework/solid/guides/queries.md | 14 + .../solid/guides/query-cancellation.md | 34 + .../framework/solid/guides/query-functions.md | 15 + .../solid/guides/query-invalidation.md | 15 + .../framework/solid/guides/query-keys.md | 15 + .../framework/solid/guides/query-options.md | 15 + .../framework/solid/guides/query-retries.md | 15 + .../solid/guides/request-waterfalls.md | 16 + .../solid/guides/scroll-restoration.md | 8 + docs/zh-hans/framework/solid/guides/ssr.md | 7 + .../framework/solid/guides/suspense.md | 43 + .../zh-hans/framework/solid/guides/testing.md | 7 + .../guides/updates-from-mutation-responses.md | 16 + .../solid/guides/window-focus-refetching.md | 15 + docs/zh-hans/framework/solid/installation.md | 60 + docs/zh-hans/framework/solid/overview.md | 139 ++ .../solid/plugins/broadcastQueryClient.md | 10 + .../solid/plugins/createPersister.md | 10 + docs/zh-hans/framework/solid/quick-start.md | 230 +++ .../framework/solid/reference/hydration.md | 12 + .../solid/reference/infiniteQueryOptions.md | 8 + .../framework/solid/reference/queryOptions.md | 8 + .../solid/reference/useInfiniteQuery.md | 12 + .../solid/reference/useIsFetching.md | 10 + .../solid/reference/useIsMutating.md | 10 + .../framework/solid/reference/useMutation.md | 12 + .../solid/reference/useMutationState.md | 12 + .../framework/solid/reference/useQueries.md | 11 + .../framework/solid/reference/useQuery.md | 376 +++++ docs/zh-hans/framework/solid/typescript.md | 218 +++ docs/zh-hans/framework/svelte/devtools.md | 76 + docs/zh-hans/framework/svelte/installation.md | 35 + docs/zh-hans/framework/svelte/overview.md | 75 + docs/zh-hans/framework/svelte/reactivity.md | 50 + .../reference/classes/hydrationboundary.md | 276 ++++ .../functions/createinfinitequery.md | 44 + .../reference/functions/createmutation.md | 39 + .../reference/functions/createqueries.md | 39 + .../svelte/reference/functions/createquery.md | 107 ++ .../functions/getisrestoringcontext.md | 22 + .../functions/getqueryclientcontext.md | 22 + .../functions/infinitequeryoptions.md | 51 + .../reference/functions/queryoptions.md | 68 + .../functions/setisrestoringcontext.md | 26 + .../functions/setqueryclientcontext.md | 26 + .../svelte/reference/functions/usehydrate.md | 28 + .../reference/functions/useisfetching.md | 26 + .../reference/functions/useismutating.md | 26 + .../reference/functions/useisrestoring.md | 20 + .../reference/functions/usemutationstate.md | 30 + .../reference/functions/usequeryclient.md | 24 + .../framework/svelte/reference/index.md | 59 + .../type-aliases/createbasemutationresult.md | 34 + .../type-aliases/createbasequeryoptions.md | 30 + .../type-aliases/createbasequeryresult.md | 24 + .../createinfinitequeryoptions.md | 32 + .../type-aliases/createinfinitequeryresult.md | 24 + .../type-aliases/createmutateasyncfunction.md | 26 + .../type-aliases/createmutatefunction.md | 34 + .../type-aliases/createmutationoptions.md | 28 + .../type-aliases/createmutationresult.md | 28 + .../type-aliases/createqueryoptions.md | 28 + .../type-aliases/createqueryresult.md | 24 + .../definedcreatebasequeryresult.md | 24 + .../type-aliases/definedcreatequeryresult.md | 24 + .../type-aliases/definedinitialdataoptions.md | 34 + .../type-aliases/mutationstateoptions.md | 44 + .../reference/type-aliases/queriesoptions.md | 26 + .../reference/type-aliases/queriesresults.md | 26 + .../reference/type-aliases/storeorval.md | 22 + .../undefinedinitialdataoptions.md | 34 + docs/zh-hans/framework/svelte/ssr.md | 155 ++ .../vue/community/community-projects.md | 30 + .../framework/vue/community/tkdodos-blog.md | 8 + docs/zh-hans/framework/vue/devtools.md | 88 ++ docs/zh-hans/framework/vue/graphql.md | 9 + .../guides/background-fetching-indicators.md | 46 + docs/zh-hans/framework/vue/guides/caching.md | 8 + .../framework/vue/guides/custom-client.md | 73 + .../vue/guides/default-query-function.md | 32 + .../framework/vue/guides/dependent-queries.md | 88 ++ .../framework/vue/guides/disabling-queries.md | 103 ++ .../guides/does-this-replace-client-state.md | 57 + docs/zh-hans/framework/vue/guides/filters.md | 8 + .../vue/guides/important-defaults.md | 10 + .../framework/vue/guides/infinite-queries.md | 58 + .../vue/guides/initial-query-data.md | 10 + .../guides/invalidations-from-mutations.md | 34 + .../framework/vue/guides/migrating-to-v5.md | 358 +++++ .../zh-hans/framework/vue/guides/mutations.md | 304 ++++ .../framework/vue/guides/network-mode.md | 8 + .../vue/guides/optimistic-updates.md | 10 + .../framework/vue/guides/paginated-queries.md | 78 + .../framework/vue/guides/parallel-queries.md | 41 + .../vue/guides/placeholder-query-data.md | 59 + .../framework/vue/guides/prefetching.md | 50 + docs/zh-hans/framework/vue/guides/queries.md | 102 ++ .../vue/guides/query-cancellation.md | 27 + .../framework/vue/guides/query-functions.md | 24 + .../vue/guides/query-invalidation.md | 10 + .../framework/vue/guides/query-keys.md | 21 + .../framework/vue/guides/query-options.md | 10 + .../framework/vue/guides/query-retries.md | 60 + .../vue/guides/scroll-restoration.md | 8 + docs/zh-hans/framework/vue/guides/ssr.md | 259 ++++ docs/zh-hans/framework/vue/guides/suspense.md | 55 + docs/zh-hans/framework/vue/guides/testing.md | 7 + .../guides/updates-from-mutation-responses.md | 8 + .../vue/guides/window-focus-refetching.md | 28 + docs/zh-hans/framework/vue/installation.md | 70 + docs/zh-hans/framework/vue/overview.md | 46 + .../vue/plugins/broadcastQueryClient.md | 10 + .../framework/vue/plugins/createPersister.md | 134 ++ docs/zh-hans/framework/vue/quick-start.md | 56 + docs/zh-hans/framework/vue/reactivity.md | 163 ++ .../framework/vue/reference/hydration.md | 12 + .../vue/reference/infiniteQueryOptions.md | 8 + .../framework/vue/reference/queryOptions.md | 8 + .../vue/reference/useInfiniteQuery.md | 10 + .../framework/vue/reference/useIsFetching.md | 10 + .../framework/vue/reference/useIsMutating.md | 10 + .../framework/vue/reference/useMutation.md | 10 + .../vue/reference/useMutationState.md | 10 + .../framework/vue/reference/useQueries.md | 10 + .../framework/vue/reference/useQuery.md | 10 + .../framework/vue/reference/useQueryClient.md | 10 + docs/zh-hans/framework/vue/typescript.md | 118 ++ .../reference/InfiniteQueryObserver.md | 27 + docs/zh-hans/reference/MutationCache.md | 99 ++ docs/zh-hans/reference/QueriesObserver.md | 25 + docs/zh-hans/reference/QueryCache.md | 123 ++ docs/zh-hans/reference/QueryClient.md | 459 ++++++ docs/zh-hans/reference/QueryObserver.md | 22 + docs/zh-hans/reference/focusManager.md | 77 + docs/zh-hans/reference/notifyManager.md | 89 ++ docs/zh-hans/reference/onlineManager.md | 75 + docs/zh-hans/reference/streamedQuery.md | 28 + 308 files changed, 23825 insertions(+) create mode 100644 docs/zh-hans/config.json create mode 100644 docs/zh-hans/eslint/eslint-plugin-query.md create mode 100644 docs/zh-hans/eslint/exhaustive-deps.md create mode 100644 docs/zh-hans/eslint/infinite-query-property-order.md create mode 100644 docs/zh-hans/eslint/no-rest-destructuring.md create mode 100644 docs/zh-hans/eslint/no-unstable-deps.md create mode 100644 docs/zh-hans/eslint/stable-query-client.md create mode 100644 docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md create mode 100644 docs/zh-hans/framework/angular/devtools.md create mode 100644 docs/zh-hans/framework/angular/guides/background-fetching-indicators.md create mode 100644 docs/zh-hans/framework/angular/guides/caching.md create mode 100644 docs/zh-hans/framework/angular/guides/default-query-function.md create mode 100644 docs/zh-hans/framework/angular/guides/dependent-queries.md create mode 100644 docs/zh-hans/framework/angular/guides/disabling-queries.md create mode 100644 docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hans/framework/angular/guides/filters.md create mode 100644 docs/zh-hans/framework/angular/guides/important-defaults.md create mode 100644 docs/zh-hans/framework/angular/guides/infinite-queries.md create mode 100644 docs/zh-hans/framework/angular/guides/initial-query-data.md create mode 100644 docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hans/framework/angular/guides/mutation-options.md create mode 100644 docs/zh-hans/framework/angular/guides/mutations.md create mode 100644 docs/zh-hans/framework/angular/guides/network-mode.md create mode 100644 docs/zh-hans/framework/angular/guides/optimistic-updates.md create mode 100644 docs/zh-hans/framework/angular/guides/paginated-queries.md create mode 100644 docs/zh-hans/framework/angular/guides/parallel-queries.md create mode 100644 docs/zh-hans/framework/angular/guides/placeholder-query-data.md create mode 100644 docs/zh-hans/framework/angular/guides/queries.md create mode 100644 docs/zh-hans/framework/angular/guides/query-cancellation.md create mode 100644 docs/zh-hans/framework/angular/guides/query-functions.md create mode 100644 docs/zh-hans/framework/angular/guides/query-invalidation.md create mode 100644 docs/zh-hans/framework/angular/guides/query-keys.md create mode 100644 docs/zh-hans/framework/angular/guides/query-options.md create mode 100644 docs/zh-hans/framework/angular/guides/query-retries.md create mode 100644 docs/zh-hans/framework/angular/guides/scroll-restoration.md create mode 100644 docs/zh-hans/framework/angular/guides/window-focus-refetching.md create mode 100644 docs/zh-hans/framework/angular/installation.md create mode 100644 docs/zh-hans/framework/angular/overview.md create mode 100644 docs/zh-hans/framework/angular/quick-start.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/infinitequeryoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectinfinitequery.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectisfetching.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectismutating.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectmutation.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectmutationstate.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectqueries.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectquery.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/injectqueryclient.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/provideangularquery.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/providequeryclient.md create mode 100644 docs/zh-hans/framework/angular/reference/functions/queryoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/index.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/basemutationnarrowing.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/basequerynarrowing.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/createbasequeryoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/createinfinitequeryoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/createmutationoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/createqueryoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/interfaces/injectmutationstateoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createbasemutationresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createbasequeryresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createinfinitequeryresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createmutateasyncfunction.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createmutatefunction.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createmutationresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/createqueryresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/definedcreatequeryresult.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdataoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/nonundefinedguard.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/queriesoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/queriesresults.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md create mode 100644 docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md create mode 100644 docs/zh-hans/framework/angular/typescript.md create mode 100644 docs/zh-hans/framework/angular/zoneless.md create mode 100644 docs/zh-hans/framework/react/community/community-projects.md create mode 100644 docs/zh-hans/framework/react/community/tkdodos-blog.md create mode 100644 docs/zh-hans/framework/react/comparison.md create mode 100644 docs/zh-hans/framework/react/devtools.md create mode 100644 docs/zh-hans/framework/react/graphql.md create mode 100644 docs/zh-hans/framework/react/guides/advanced-ssr.md create mode 100644 docs/zh-hans/framework/react/guides/background-fetching-indicators.md create mode 100644 docs/zh-hans/framework/react/guides/caching.md create mode 100644 docs/zh-hans/framework/react/guides/default-query-function.md create mode 100644 docs/zh-hans/framework/react/guides/dependent-queries.md create mode 100644 docs/zh-hans/framework/react/guides/disabling-queries.md create mode 100644 docs/zh-hans/framework/react/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hans/framework/react/guides/filters.md create mode 100644 docs/zh-hans/framework/react/guides/important-defaults.md create mode 100644 docs/zh-hans/framework/react/guides/infinite-queries.md create mode 100644 docs/zh-hans/framework/react/guides/initial-query-data.md create mode 100644 docs/zh-hans/framework/react/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md create mode 100644 docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md create mode 100644 docs/zh-hans/framework/react/guides/migrating-to-v5.md create mode 100644 docs/zh-hans/framework/react/guides/mutations.md create mode 100644 docs/zh-hans/framework/react/guides/network-mode.md create mode 100644 docs/zh-hans/framework/react/guides/optimistic-updates.md create mode 100644 docs/zh-hans/framework/react/guides/paginated-queries.md create mode 100644 docs/zh-hans/framework/react/guides/parallel-queries.md create mode 100644 docs/zh-hans/framework/react/guides/placeholder-query-data.md create mode 100644 docs/zh-hans/framework/react/guides/prefetching.md create mode 100644 docs/zh-hans/framework/react/guides/queries.md create mode 100644 docs/zh-hans/framework/react/guides/query-cancellation.md create mode 100644 docs/zh-hans/framework/react/guides/query-functions.md create mode 100644 docs/zh-hans/framework/react/guides/query-invalidation.md create mode 100644 docs/zh-hans/framework/react/guides/query-keys.md create mode 100644 docs/zh-hans/framework/react/guides/query-options.md create mode 100644 docs/zh-hans/framework/react/guides/query-retries.md create mode 100644 docs/zh-hans/framework/react/guides/render-optimizations.md create mode 100644 docs/zh-hans/framework/react/guides/request-waterfalls.md create mode 100644 docs/zh-hans/framework/react/guides/scroll-restoration.md create mode 100644 docs/zh-hans/framework/react/guides/ssr.md create mode 100644 docs/zh-hans/framework/react/guides/suspense.md create mode 100644 docs/zh-hans/framework/react/guides/testing.md create mode 100644 docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md create mode 100644 docs/zh-hans/framework/react/guides/window-focus-refetching.md create mode 100644 docs/zh-hans/framework/react/installation.md create mode 100644 docs/zh-hans/framework/react/overview.md create mode 100644 docs/zh-hans/framework/react/plugins/broadcastQueryClient.md create mode 100644 docs/zh-hans/framework/react/plugins/createAsyncStoragePersister.md create mode 100644 docs/zh-hans/framework/react/plugins/createPersister.md create mode 100644 docs/zh-hans/framework/react/plugins/createSyncStoragePersister.md create mode 100644 docs/zh-hans/framework/react/plugins/persistQueryClient.md create mode 100644 docs/zh-hans/framework/react/quick-start.md create mode 100644 docs/zh-hans/framework/react/react-native.md create mode 100644 docs/zh-hans/framework/react/reference/QueryClientProvider.md create mode 100644 docs/zh-hans/framework/react/reference/QueryErrorResetBoundary.md create mode 100644 docs/zh-hans/framework/react/reference/hydration.md create mode 100644 docs/zh-hans/framework/react/reference/infiniteQueryOptions.md create mode 100644 docs/zh-hans/framework/react/reference/queryOptions.md create mode 100644 docs/zh-hans/framework/react/reference/useInfiniteQuery.md create mode 100644 docs/zh-hans/framework/react/reference/useIsFetching.md create mode 100644 docs/zh-hans/framework/react/reference/useIsMutating.md create mode 100644 docs/zh-hans/framework/react/reference/useMutation.md create mode 100644 docs/zh-hans/framework/react/reference/useMutationState.md create mode 100644 docs/zh-hans/framework/react/reference/usePrefetchInfiniteQuery.md create mode 100644 docs/zh-hans/framework/react/reference/usePrefetchQuery.md create mode 100644 docs/zh-hans/framework/react/reference/useQueries.md create mode 100644 docs/zh-hans/framework/react/reference/useQuery.md create mode 100644 docs/zh-hans/framework/react/reference/useQueryClient.md create mode 100644 docs/zh-hans/framework/react/reference/useQueryErrorResetBoundary.md create mode 100644 docs/zh-hans/framework/react/reference/useSuspenseInfiniteQuery.md create mode 100644 docs/zh-hans/framework/react/reference/useSuspenseQueries.md create mode 100644 docs/zh-hans/framework/react/reference/useSuspenseQuery.md create mode 100644 docs/zh-hans/framework/react/typescript.md create mode 100644 docs/zh-hans/framework/react/videos.md create mode 100644 docs/zh-hans/framework/solid/community/community-projects.md create mode 100644 docs/zh-hans/framework/solid/community/tkdodos-blog.md create mode 100644 docs/zh-hans/framework/solid/devtools.md create mode 100644 docs/zh-hans/framework/solid/guides/advanced-ssr.md create mode 100644 docs/zh-hans/framework/solid/guides/background-fetching-indicators.md create mode 100644 docs/zh-hans/framework/solid/guides/caching.md create mode 100644 docs/zh-hans/framework/solid/guides/default-query-function.md create mode 100644 docs/zh-hans/framework/solid/guides/dependent-queries.md create mode 100644 docs/zh-hans/framework/solid/guides/disabling-queries.md create mode 100644 docs/zh-hans/framework/solid/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hans/framework/solid/guides/filters.md create mode 100644 docs/zh-hans/framework/solid/guides/important-defaults.md create mode 100644 docs/zh-hans/framework/solid/guides/infinite-queries.md create mode 100644 docs/zh-hans/framework/solid/guides/initial-query-data.md create mode 100644 docs/zh-hans/framework/solid/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hans/framework/solid/guides/mutations.md create mode 100644 docs/zh-hans/framework/solid/guides/network-mode.md create mode 100644 docs/zh-hans/framework/solid/guides/optimistic-updates.md create mode 100644 docs/zh-hans/framework/solid/guides/paginated-queries.md create mode 100644 docs/zh-hans/framework/solid/guides/parallel-queries.md create mode 100644 docs/zh-hans/framework/solid/guides/placeholder-query-data.md create mode 100644 docs/zh-hans/framework/solid/guides/prefetching.md create mode 100644 docs/zh-hans/framework/solid/guides/queries.md create mode 100644 docs/zh-hans/framework/solid/guides/query-cancellation.md create mode 100644 docs/zh-hans/framework/solid/guides/query-functions.md create mode 100644 docs/zh-hans/framework/solid/guides/query-invalidation.md create mode 100644 docs/zh-hans/framework/solid/guides/query-keys.md create mode 100644 docs/zh-hans/framework/solid/guides/query-options.md create mode 100644 docs/zh-hans/framework/solid/guides/query-retries.md create mode 100644 docs/zh-hans/framework/solid/guides/request-waterfalls.md create mode 100644 docs/zh-hans/framework/solid/guides/scroll-restoration.md create mode 100644 docs/zh-hans/framework/solid/guides/ssr.md create mode 100644 docs/zh-hans/framework/solid/guides/suspense.md create mode 100644 docs/zh-hans/framework/solid/guides/testing.md create mode 100644 docs/zh-hans/framework/solid/guides/updates-from-mutation-responses.md create mode 100644 docs/zh-hans/framework/solid/guides/window-focus-refetching.md create mode 100644 docs/zh-hans/framework/solid/installation.md create mode 100644 docs/zh-hans/framework/solid/overview.md create mode 100644 docs/zh-hans/framework/solid/plugins/broadcastQueryClient.md create mode 100644 docs/zh-hans/framework/solid/plugins/createPersister.md create mode 100644 docs/zh-hans/framework/solid/quick-start.md create mode 100644 docs/zh-hans/framework/solid/reference/hydration.md create mode 100644 docs/zh-hans/framework/solid/reference/infiniteQueryOptions.md create mode 100644 docs/zh-hans/framework/solid/reference/queryOptions.md create mode 100644 docs/zh-hans/framework/solid/reference/useInfiniteQuery.md create mode 100644 docs/zh-hans/framework/solid/reference/useIsFetching.md create mode 100644 docs/zh-hans/framework/solid/reference/useIsMutating.md create mode 100644 docs/zh-hans/framework/solid/reference/useMutation.md create mode 100644 docs/zh-hans/framework/solid/reference/useMutationState.md create mode 100644 docs/zh-hans/framework/solid/reference/useQueries.md create mode 100644 docs/zh-hans/framework/solid/reference/useQuery.md create mode 100644 docs/zh-hans/framework/solid/typescript.md create mode 100644 docs/zh-hans/framework/svelte/devtools.md create mode 100644 docs/zh-hans/framework/svelte/installation.md create mode 100644 docs/zh-hans/framework/svelte/overview.md create mode 100644 docs/zh-hans/framework/svelte/reactivity.md create mode 100644 docs/zh-hans/framework/svelte/reference/classes/hydrationboundary.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/createinfinitequery.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/createmutation.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/createqueries.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/createquery.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/getisrestoringcontext.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/getqueryclientcontext.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/infinitequeryoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/queryoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/setisrestoringcontext.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/setqueryclientcontext.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/usehydrate.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/useisfetching.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/useismutating.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/useisrestoring.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/usemutationstate.md create mode 100644 docs/zh-hans/framework/svelte/reference/functions/usequeryclient.md create mode 100644 docs/zh-hans/framework/svelte/reference/index.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createbasemutationresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createmutateasyncfunction.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createmutatefunction.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createmutationoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createmutationresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createqueryoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/createqueryresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatequeryresult.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/definedinitialdataoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/mutationstateoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/queriesoptions.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/queriesresults.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/storeorval.md create mode 100644 docs/zh-hans/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md create mode 100644 docs/zh-hans/framework/svelte/ssr.md create mode 100644 docs/zh-hans/framework/vue/community/community-projects.md create mode 100644 docs/zh-hans/framework/vue/community/tkdodos-blog.md create mode 100644 docs/zh-hans/framework/vue/devtools.md create mode 100644 docs/zh-hans/framework/vue/graphql.md create mode 100644 docs/zh-hans/framework/vue/guides/background-fetching-indicators.md create mode 100644 docs/zh-hans/framework/vue/guides/caching.md create mode 100644 docs/zh-hans/framework/vue/guides/custom-client.md create mode 100644 docs/zh-hans/framework/vue/guides/default-query-function.md create mode 100644 docs/zh-hans/framework/vue/guides/dependent-queries.md create mode 100644 docs/zh-hans/framework/vue/guides/disabling-queries.md create mode 100644 docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hans/framework/vue/guides/filters.md create mode 100644 docs/zh-hans/framework/vue/guides/important-defaults.md create mode 100644 docs/zh-hans/framework/vue/guides/infinite-queries.md create mode 100644 docs/zh-hans/framework/vue/guides/initial-query-data.md create mode 100644 docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hans/framework/vue/guides/migrating-to-v5.md create mode 100644 docs/zh-hans/framework/vue/guides/mutations.md create mode 100644 docs/zh-hans/framework/vue/guides/network-mode.md create mode 100644 docs/zh-hans/framework/vue/guides/optimistic-updates.md create mode 100644 docs/zh-hans/framework/vue/guides/paginated-queries.md create mode 100644 docs/zh-hans/framework/vue/guides/parallel-queries.md create mode 100644 docs/zh-hans/framework/vue/guides/placeholder-query-data.md create mode 100644 docs/zh-hans/framework/vue/guides/prefetching.md create mode 100644 docs/zh-hans/framework/vue/guides/queries.md create mode 100644 docs/zh-hans/framework/vue/guides/query-cancellation.md create mode 100644 docs/zh-hans/framework/vue/guides/query-functions.md create mode 100644 docs/zh-hans/framework/vue/guides/query-invalidation.md create mode 100644 docs/zh-hans/framework/vue/guides/query-keys.md create mode 100644 docs/zh-hans/framework/vue/guides/query-options.md create mode 100644 docs/zh-hans/framework/vue/guides/query-retries.md create mode 100644 docs/zh-hans/framework/vue/guides/scroll-restoration.md create mode 100644 docs/zh-hans/framework/vue/guides/ssr.md create mode 100644 docs/zh-hans/framework/vue/guides/suspense.md create mode 100644 docs/zh-hans/framework/vue/guides/testing.md create mode 100644 docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md create mode 100644 docs/zh-hans/framework/vue/guides/window-focus-refetching.md create mode 100644 docs/zh-hans/framework/vue/installation.md create mode 100644 docs/zh-hans/framework/vue/overview.md create mode 100644 docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md create mode 100644 docs/zh-hans/framework/vue/plugins/createPersister.md create mode 100644 docs/zh-hans/framework/vue/quick-start.md create mode 100644 docs/zh-hans/framework/vue/reactivity.md create mode 100644 docs/zh-hans/framework/vue/reference/hydration.md create mode 100644 docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md create mode 100644 docs/zh-hans/framework/vue/reference/queryOptions.md create mode 100644 docs/zh-hans/framework/vue/reference/useInfiniteQuery.md create mode 100644 docs/zh-hans/framework/vue/reference/useIsFetching.md create mode 100644 docs/zh-hans/framework/vue/reference/useIsMutating.md create mode 100644 docs/zh-hans/framework/vue/reference/useMutation.md create mode 100644 docs/zh-hans/framework/vue/reference/useMutationState.md create mode 100644 docs/zh-hans/framework/vue/reference/useQueries.md create mode 100644 docs/zh-hans/framework/vue/reference/useQuery.md create mode 100644 docs/zh-hans/framework/vue/reference/useQueryClient.md create mode 100644 docs/zh-hans/framework/vue/typescript.md create mode 100644 docs/zh-hans/reference/InfiniteQueryObserver.md create mode 100644 docs/zh-hans/reference/MutationCache.md create mode 100644 docs/zh-hans/reference/QueriesObserver.md create mode 100644 docs/zh-hans/reference/QueryCache.md create mode 100644 docs/zh-hans/reference/QueryClient.md create mode 100644 docs/zh-hans/reference/QueryObserver.md create mode 100644 docs/zh-hans/reference/focusManager.md create mode 100644 docs/zh-hans/reference/notifyManager.md create mode 100644 docs/zh-hans/reference/onlineManager.md create mode 100644 docs/zh-hans/reference/streamedQuery.md diff --git a/docs/zh-hans/config.json b/docs/zh-hans/config.json new file mode 100644 index 00000000000..6a561072a69 --- /dev/null +++ b/docs/zh-hans/config.json @@ -0,0 +1,1372 @@ +{ + "$schema": "https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json", + "docSearch": { + "appId": "20TOVD6LOE", + "apiKey": "35bc6c51aa322700d1383e17c4f669f4", + "indexName": "tanstackquery" + }, + "sections": [ + { + "label": "入门指南", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "概述", + "to": "framework/react/overview" + }, + { + "label": "安装", + "to": "framework/react/installation" + }, + { + "label": "快速开始", + "to": "framework/react/quick-start" + }, + { + "label": "开发者工具", + "to": "framework/react/devtools" + }, + { + "label": "视频与演讲", + "to": "framework/react/videos" + }, + { + "label": "对比", + "to": "framework/react/comparison" + }, + { + "label": "TypeScript", + "to": "framework/react/typescript" + }, + { + "label": "GraphQL", + "to": "framework/react/graphql" + }, + { + "label": "React Native", + "to": "framework/react/react-native" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "概述", + "to": "framework/solid/overview" + }, + { + "label": "快速开始", + "to": "framework/solid/quick-start" + }, + { + "label": "安装", + "to": "framework/solid/installation" + }, + { + "label": "开发者工具", + "to": "framework/solid/devtools" + }, + { + "label": "TypeScript", + "to": "framework/solid/typescript" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "概述", + "to": "framework/vue/overview" + }, + { + "label": "安装", + "to": "framework/vue/installation" + }, + { + "label": "快速开始", + "to": "framework/vue/quick-start" + }, + { + "label": "开发者工具", + "to": "framework/vue/devtools" + }, + { + "label": "TypeScript", + "to": "framework/vue/typescript" + }, + { + "label": "响应式", + "to": "framework/vue/reactivity" + }, + { + "label": "GraphQL", + "to": "framework/vue/graphql" + } + ] + }, + { + "label": "svelte", + "children": [ + { + "label": "概述", + "to": "framework/svelte/overview" + }, + { + "label": "安装", + "to": "framework/svelte/installation" + }, + { + "label": "开发者工具", + "to": "framework/svelte/devtools" + }, + { + "label": "SSR 与 SvelteKit", + "to": "framework/svelte/ssr" + }, + { + "label": "响应式", + "to": "framework/svelte/reactivity" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "概述", + "to": "framework/angular/overview" + }, + { + "label": "安装", + "to": "framework/angular/installation" + }, + { + "label": "快速开始", + "to": "framework/angular/quick-start" + }, + { + "label": "Angular HttpClient 及其他数据获取客户端", + "to": "framework/angular/angular-httpclient-and-other-data-fetching-clients" + }, + { + "label": "开发者工具", + "to": "framework/angular/devtools" + }, + { + "label": "TypeScript", + "to": "framework/angular/typescript" + }, + { + "label": "无 Zone", + "to": "framework/angular/zoneless" + } + ] + } + ] + }, + { + "label": "指南与概念", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "重要默认设置", + "to": "framework/react/guides/important-defaults" + }, + { + "label": "查询", + "to": "framework/react/guides/queries" + }, + { + "label": "查询键", + "to": "framework/react/guides/query-keys" + }, + { + "label": "查询函数", + "to": "framework/react/guides/query-functions" + }, + { + "label": "查询选项", + "to": "framework/react/guides/query-options" + }, + { + "label": "网络模式", + "to": "framework/react/guides/network-mode" + }, + { + "label": "并行查询", + "to": "framework/react/guides/parallel-queries" + }, + { + "label": "依赖查询", + "to": "framework/react/guides/dependent-queries" + }, + { + "label": "后台获取指示器", + "to": "framework/react/guides/background-fetching-indicators" + }, + { + "label": "窗口焦点重新获取", + "to": "framework/react/guides/window-focus-refetching" + }, + { + "label": "禁用/暂停查询", + "to": "framework/react/guides/disabling-queries" + }, + { + "label": "查询重试", + "to": "framework/react/guides/query-retries" + }, + { + "label": "分页查询", + "to": "framework/react/guides/paginated-queries" + }, + { + "label": "无限查询", + "to": "framework/react/guides/infinite-queries" + }, + { + "label": "初始查询数据", + "to": "framework/react/guides/initial-query-data" + }, + { + "label": "占位查询数据", + "to": "framework/react/guides/placeholder-query-data" + }, + { + "label": "变更", + "to": "framework/react/guides/mutations" + }, + { + "label": "查询失效", + "to": "framework/react/guides/query-invalidation" + }, + { + "label": "从变更中失效", + "to": "framework/react/guides/invalidations-from-mutations" + }, + { + "label": "从变更响应中更新", + "to": "framework/react/guides/updates-from-mutation-responses" + }, + { + "label": "乐观更新", + "to": "framework/react/guides/optimistic-updates" + }, + { + "label": "查询取消", + "to": "framework/react/guides/query-cancellation" + }, + { + "label": "滚动恢复", + "to": "framework/react/guides/scroll-restoration" + }, + { + "label": "过滤器", + "to": "framework/react/guides/filters" + }, + { + "label": "性能与请求瀑布流", + "to": "framework/react/guides/request-waterfalls" + }, + { + "label": "预获取与路由集成", + "to": "framework/react/guides/prefetching" + }, + { + "label": "服务端渲染与注水", + "to": "framework/react/guides/ssr" + }, + { + "label": "高级服务端渲染", + "to": "framework/react/guides/advanced-ssr" + }, + { + "label": "缓存", + "to": "framework/react/guides/caching" + }, + { + "label": "渲染优化", + "to": "framework/react/guides/render-optimizations" + }, + { + "label": "默认查询函数", + "to": "framework/react/guides/default-query-function" + }, + { + "label": "Suspense", + "to": "framework/react/guides/suspense" + }, + { + "label": "测试", + "to": "framework/react/guides/testing" + }, + { + "label": "这会取代 [Redux, MobX 等] 吗?", + "to": "framework/react/guides/does-this-replace-client-state" + }, + { + "label": "迁移到 v3", + "to": "framework/react/guides/migrating-to-react-query-3" + }, + { + "label": "迁移到 v4", + "to": "framework/react/guides/migrating-to-react-query-4" + }, + { + "label": "迁移到 v5", + "to": "framework/react/guides/migrating-to-v5" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "重要默认设置", + "to": "framework/solid/guides/important-defaults" + }, + { + "label": "查询", + "to": "framework/solid/guides/queries" + }, + { + "label": "查询键", + "to": "framework/solid/guides/query-keys" + }, + { + "label": "查询函数", + "to": "framework/solid/guides/query-functions" + }, + { + "label": "查询选项", + "to": "framework/solid/guides/query-options" + }, + { + "label": "网络模式", + "to": "framework/solid/guides/network-mode" + }, + { + "label": "并行查询", + "to": "framework/solid/guides/parallel-queries" + }, + { + "label": "依赖查询", + "to": "framework/solid/guides/dependent-queries" + }, + { + "label": "后台获取指示器", + "to": "framework/solid/guides/background-fetching-indicators" + }, + { + "label": "窗口焦点重新获取", + "to": "framework/solid/guides/window-focus-refetching" + }, + { + "label": "禁用/暂停查询", + "to": "framework/solid/guides/disabling-queries" + }, + { + "label": "查询重试", + "to": "framework/solid/guides/query-retries" + }, + { + "label": "分页查询", + "to": "framework/solid/guides/paginated-queries" + }, + { + "label": "无限查询", + "to": "framework/solid/guides/infinite-queries" + }, + { + "label": "初始查询数据", + "to": "framework/solid/guides/initial-query-data" + }, + { + "label": "占位查询数据", + "to": "framework/solid/guides/placeholder-query-data" + }, + { + "label": "变更", + "to": "framework/solid/guides/mutations" + }, + { + "label": "查询失效", + "to": "framework/solid/guides/query-invalidation" + }, + { + "label": "从变更中失效", + "to": "framework/solid/guides/invalidations-from-mutations" + }, + { + "label": "从变更响应中更新", + "to": "framework/solid/guides/updates-from-mutation-responses" + }, + { + "label": "乐观更新", + "to": "framework/solid/guides/optimistic-updates" + }, + { + "label": "查询取消", + "to": "framework/solid/guides/query-cancellation" + }, + { + "label": "滚动恢复", + "to": "framework/solid/guides/scroll-restoration" + }, + { + "label": "过滤器", + "to": "framework/solid/guides/filters" + }, + { + "label": "请求瀑布流", + "to": "framework/solid/guides/request-waterfalls" + }, + { + "label": "预获取", + "to": "framework/solid/guides/prefetching" + }, + { + "label": "SSR", + "to": "framework/solid/guides/ssr" + }, + { + "label": "高级 SSR", + "to": "framework/solid/guides/advanced-ssr" + }, + { + "label": "缓存", + "to": "framework/solid/guides/caching" + }, + { + "label": "默认查询函数", + "to": "framework/solid/guides/default-query-function" + }, + { + "label": "Suspense", + "to": "framework/solid/guides/suspense" + }, + { + "label": "测试", + "to": "framework/solid/guides/testing" + }, + { + "label": "这会取代状态管理器吗?", + "to": "framework/angular/guides/does-this-replace-client-state" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "重要默认设置", + "to": "framework/vue/guides/important-defaults" + }, + { + "label": "查询", + "to": "framework/vue/guides/queries" + }, + { + "label": "查询键", + "to": "framework/vue/guides/query-keys" + }, + { + "label": "查询函数", + "to": "framework/vue/guides/query-functions" + }, + { + "label": "查询选项", + "to": "framework/vue/guides/query-options" + }, + { + "label": "网络模式", + "to": "framework/vue/guides/network-mode" + }, + { + "label": "并行查询", + "to": "framework/vue/guides/parallel-queries" + }, + { + "label": "依赖查询", + "to": "framework/vue/guides/dependent-queries" + }, + { + "label": "后台获取指示器", + "to": "framework/vue/guides/background-fetching-indicators" + }, + { + "label": "窗口焦点重新获取", + "to": "framework/vue/guides/window-focus-refetching" + }, + { + "label": "禁用/暂停查询", + "to": "framework/vue/guides/disabling-queries" + }, + { + "label": "查询重试", + "to": "framework/vue/guides/query-retries" + }, + { + "label": "分页查询", + "to": "framework/vue/guides/paginated-queries" + }, + { + "label": "无限查询", + "to": "framework/vue/guides/infinite-queries" + }, + { + "label": "初始查询数据", + "to": "framework/vue/guides/initial-query-data" + }, + { + "label": "占位查询数据", + "to": "framework/vue/guides/placeholder-query-data" + }, + { + "label": "变更", + "to": "framework/vue/guides/mutations" + }, + { + "label": "查询失效", + "to": "framework/vue/guides/query-invalidation" + }, + { + "label": "从变更中失效", + "to": "framework/vue/guides/invalidations-from-mutations" + }, + { + "label": "从变更响应中更新", + "to": "framework/vue/guides/updates-from-mutation-responses" + }, + { + "label": "乐观更新", + "to": "framework/vue/guides/optimistic-updates" + }, + { + "label": "查询取消", + "to": "framework/vue/guides/query-cancellation" + }, + { + "label": "滚动恢复", + "to": "framework/vue/guides/scroll-restoration" + }, + { + "label": "过滤器", + "to": "framework/vue/guides/filters" + }, + { + "label": "预获取", + "to": "framework/vue/guides/prefetching" + }, + { + "label": "SSR 与 Nuxt", + "to": "framework/vue/guides/ssr" + }, + { + "label": "缓存", + "to": "framework/vue/guides/caching" + }, + { + "label": "默认查询函数", + "to": "framework/vue/guides/default-query-function" + }, + { + "label": "Suspense", + "to": "framework/vue/guides/suspense" + }, + { + "label": "测试", + "to": "framework/vue/guides/testing" + }, + { + "label": "自定义客户端", + "to": "framework/vue/guides/custom-client" + }, + { + "label": "这会取代 [Vuex, Pinia] 吗?", + "to": "framework/vue/guides/does-this-replace-client-state" + }, + { + "label": "迁移到 v5", + "to": "framework/vue/guides/migrating-to-v5" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "重要默认设置", + "to": "framework/angular/guides/important-defaults" + }, + { + "label": "查询", + "to": "framework/angular/guides/queries" + }, + { + "label": "查询键", + "to": "framework/angular/guides/query-keys" + }, + { + "label": "查询函数", + "to": "framework/angular/guides/query-functions" + }, + { + "label": "查询选项", + "to": "framework/angular/guides/query-options" + }, + { + "label": "网络模式", + "to": "framework/angular/guides/network-mode" + }, + { + "label": "并行查询", + "to": "framework/angular/guides/parallel-queries" + }, + { + "label": "依赖查询", + "to": "framework/angular/guides/dependent-queries" + }, + { + "label": "后台获取指示器", + "to": "framework/angular/guides/background-fetching-indicators" + }, + { + "label": "窗口焦点重新获取", + "to": "framework/angular/guides/window-focus-refetching" + }, + { + "label": "禁用/暂停查询", + "to": "framework/angular/guides/disabling-queries" + }, + { + "label": "查询重试", + "to": "framework/angular/guides/query-retries" + }, + { + "label": "分页查询", + "to": "framework/angular/guides/paginated-queries" + }, + { + "label": "无限查询", + "to": "framework/angular/guides/infinite-queries" + }, + { + "label": "初始查询数据", + "to": "framework/angular/guides/initial-query-data" + }, + { + "label": "占位查询数据", + "to": "framework/angular/guides/placeholder-query-data" + }, + { + "label": "变更", + "to": "framework/angular/guides/mutations" + }, + { + "label": "变更选项", + "to": "framework/angular/guides/mutation-options" + }, + { + "label": "查询失效", + "to": "framework/angular/guides/query-invalidation" + }, + { + "label": "从变更中失效", + "to": "framework/angular/guides/invalidations-from-mutations" + }, + { + "label": "乐观更新", + "to": "framework/angular/guides/optimistic-updates" + }, + { + "label": "查询取消", + "to": "framework/angular/guides/query-cancellation" + }, + { + "label": "滚动恢复", + "to": "framework/angular/guides/scroll-restoration" + }, + { + "label": "过滤器", + "to": "framework/angular/guides/filters" + }, + { + "label": "缓存", + "to": "framework/angular/guides/caching" + }, + { + "label": "默认查询函数", + "to": "framework/angular/guides/default-query-function" + }, + { + "label": "这会取代状态管理器吗?", + "to": "framework/angular/guides/does-this-replace-client-state" + } + ] + } + ] + }, + { + "label": "API 参考", + "children": [ + { + "label": "QueryClient", + "to": "reference/QueryClient" + }, + { + "label": "QueryCache", + "to": "reference/QueryCache" + }, + { + "label": "MutationCache", + "to": "reference/MutationCache" + }, + { + "label": "QueryObserver", + "to": "reference/QueryObserver" + }, + { + "label": "InfiniteQueryObserver", + "to": "reference/InfiniteQueryObserver" + }, + { + "label": "QueriesObserver", + "to": "reference/QueriesObserver" + }, + { + "label": "streamedQuery", + "to": "reference/streamedQuery" + }, + { + "label": "focusManager", + "to": "reference/focusManager" + }, + { + "label": "onlineManager", + "to": "reference/onlineManager" + }, + { + "label": "notifyManager", + "to": "reference/notifyManager" + } + ], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "useQuery", + "to": "framework/react/reference/useQuery" + }, + { + "label": "useQueries", + "to": "framework/react/reference/useQueries" + }, + { + "label": "useInfiniteQuery", + "to": "framework/react/reference/useInfiniteQuery" + }, + { + "label": "useMutation", + "to": "framework/react/reference/useMutation" + }, + { + "label": "useIsFetching", + "to": "framework/react/reference/useIsFetching" + }, + { + "label": "useIsMutating", + "to": "framework/react/reference/useIsMutating" + }, + { + "label": "useMutationState", + "to": "framework/react/reference/useMutationState" + }, + { + "label": "useSuspenseQuery", + "to": "framework/react/reference/useSuspenseQuery" + }, + { + "label": "useSuspenseInfiniteQuery", + "to": "framework/react/reference/useSuspenseInfiniteQuery" + }, + { + "label": "useSuspenseQueries", + "to": "framework/react/reference/useSuspenseQueries" + }, + { + "label": "QueryClientProvider", + "to": "framework/react/reference/QueryClientProvider" + }, + { + "label": "useQueryClient", + "to": "framework/react/reference/useQueryClient" + }, + { + "label": "queryOptions", + "to": "framework/react/reference/queryOptions" + }, + { + "label": "infiniteQueryOptions", + "to": "framework/react/reference/infiniteQueryOptions" + }, + { + "label": "usePrefetchQuery", + "to": "framework/react/reference/usePrefetchQuery" + }, + { + "label": "usePrefetchInfiniteQuery", + "to": "framework/react/reference/usePrefetchInfiniteQuery" + }, + { + "label": "QueryErrorResetBoundary", + "to": "framework/react/reference/QueryErrorResetBoundary" + }, + { + "label": "useQueryErrorResetBoundary", + "to": "framework/react/reference/useQueryErrorResetBoundary" + }, + { + "label": "注水", + "to": "framework/react/reference/hydration" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "useQuery", + "to": "framework/vue/reference/useQuery" + }, + { + "label": "useQueries", + "to": "framework/vue/reference/useQueries" + }, + { + "label": "useInfiniteQuery", + "to": "framework/vue/reference/useInfiniteQuery" + }, + { + "label": "useMutation", + "to": "framework/vue/reference/useMutation" + }, + { + "label": "useIsFetching", + "to": "framework/vue/reference/useIsFetching" + }, + { + "label": "useIsMutating", + "to": "framework/vue/reference/useIsMutating" + }, + { + "label": "useMutationState", + "to": "framework/vue/reference/useMutationState" + }, + { + "label": "useQueryClient", + "to": "framework/vue/reference/useQueryClient" + }, + { + "label": "queryOptions", + "to": "framework/vue/reference/queryOptions" + }, + { + "label": "infiniteQueryOptions", + "to": "framework/vue/reference/infiniteQueryOptions" + }, + { + "label": "注水", + "to": "framework/vue/reference/hydration" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "useQuery", + "to": "framework/solid/reference/useQuery" + }, + { + "label": "useQueries", + "to": "framework/solid/reference/useQueries" + }, + { + "label": "useInfiniteQuery", + "to": "framework/solid/reference/useInfiniteQuery" + }, + { + "label": "useMutation", + "to": "framework/solid/reference/useMutation" + }, + { + "label": "useIsFetching", + "to": "framework/solid/reference/useIsFetching" + }, + { + "label": "useIsMutating", + "to": "framework/solid/reference/useIsMutating" + }, + { + "label": "useMutationState", + "to": "framework/solid/reference/useMutationState" + }, + { + "label": "queryOptions", + "to": "framework/solid/reference/queryOptions" + }, + { + "label": "infiniteQueryOptions", + "to": "framework/solid/reference/infiniteQueryOptions" + }, + { + "label": "注水", + "to": "framework/solid/reference/hydration" + } + ] + }, + { + "label": "svelte", + "children": [ + { + "label": "Svelte 参考", + "to": "framework/svelte/reference/index" + }, + { + "label": "函数 / createQuery", + "to": "framework/svelte/reference/functions/createquery" + }, + { + "label": "函数 / createMutation", + "to": "framework/svelte/reference/functions/createmutation" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "Angular 参考", + "to": "framework/angular/reference/index" + }, + { + "label": "函数 / injectQuery", + "to": "framework/angular/reference/functions/injectquery" + }, + { + "label": "函数 / injectMutation", + "to": "framework/angular/reference/functions/injectmutation" + } + ] + } + ] + }, + { + "label": "ESLint", + "children": [ + { + "label": "ESLint 插件 Query", + "to": "eslint/eslint-plugin-query" + }, + { + "label": "彻底依赖检查", + "to": "eslint/exhaustive-deps" + }, + { + "label": "稳定的 Query Client", + "to": "eslint/stable-query-client" + }, + { + "label": "禁止剩余解构", + "to": "eslint/no-rest-destructuring" + }, + { + "label": "禁止不稳定依赖", + "to": "eslint/no-unstable-deps" + }, + { + "label": "无限查询属性顺序", + "to": "eslint/infinite-query-property-order" + } + ] + }, + { + "label": "社区资源", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "TkDodo 的博客", + "to": "framework/react/community/tkdodos-blog" + }, + { + "label": "社区项目", + "to": "framework/react/community/community-projects" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "TkDodo 的博客", + "to": "framework/solid/community/tkdodos-blog" + }, + { + "label": "社区项目", + "to": "framework/solid/community/community-projects" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "TkDodo 的博客", + "to": "framework/vue/community/tkdodos-blog" + }, + { + "label": "社区项目", + "to": "framework/vue/community/community-projects" + } + ] + } + ] + }, + { + "label": "示例", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "简单示例", + "to": "framework/react/examples/simple" + }, + { + "label": "基础示例", + "to": "framework/react/examples/basic" + }, + { + "label": "基础示例 (GraphQL-Request)", + "to": "framework/react/examples/basic-graphql-request" + }, + { + "label": "自动重新获取/轮询/实时", + "to": "framework/react/examples/auto-refetching" + }, + { + "label": "乐观更新 (UI)", + "to": "framework/react/examples/optimistic-updates-ui" + }, + { + "label": "乐观更新 (缓存)", + "to": "framework/react/examples/optimistic-updates-cache" + }, + { + "label": "分页", + "to": "framework/react/examples/pagination" + }, + { + "label": "加载更多与无限滚动", + "to": "framework/react/examples/load-more-infinite-scroll" + }, + { + "label": "带最大页数的无限查询", + "to": "framework/react/examples/infinite-query-with-max-pages" + }, + { + "label": "Suspense", + "to": "framework/react/examples/suspense" + }, + { + "label": "默认查询函数", + "to": "framework/react/examples/default-query-function" + }, + { + "label": "演练场", + "to": "framework/react/examples/playground" + }, + { + "label": "预获取", + "to": "framework/react/examples/prefetching" + }, + { + "label": "星球大战", + "to": "framework/react/examples/star-wars" + }, + { + "label": "瑞克和莫蒂", + "to": "framework/react/examples/rick-morty" + }, + { + "label": "Next.js 页面", + "to": "framework/react/examples/nextjs" + }, + { + "label": "带预获取的 Next.js 应用", + "to": "framework/react/examples/nextjs-app-prefetching" + }, + { + "label": "带流式渲染的 Next.js 应用", + "to": "framework/react/examples/nextjs-suspense-streaming" + }, + { + "label": "React Native", + "to": "framework/react/examples/react-native" + }, + { + "label": "React Router", + "to": "framework/react/examples/react-router" + }, + { + "label": "离线查询与变更", + "to": "framework/react/examples/offline" + }, + { + "label": "Algolia", + "to": "framework/react/examples/algolia" + }, + { + "label": "Shadow DOM", + "to": "framework/react/examples/shadow-dom" + }, + { + "label": "开发者工具内嵌面板", + "to": "framework/react/examples/devtools-panel" + }, + { + "label": "聊天示例 (流式)", + "to": "framework/react/examples/chat" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "简单示例", + "to": "framework/solid/examples/simple" + }, + { + "label": "基础示例", + "to": "framework/solid/examples/basic" + }, + { + "label": "基础示例 (GraphQL-Request)", + "to": "framework/solid/examples/basic-graphql-request" + }, + { + "label": "默认查询函数", + "to": "framework/solid/examples/default-query-function" + }, + { + "label": "Solid Start", + "to": "framework/solid/examples/solid-start-streaming" + }, + { + "label": "Astro", + "to": "framework/solid/examples/astro" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "基础示例", + "to": "framework/vue/examples/basic" + }, + { + "label": "Vue 2.6", + "to": "framework/vue/examples/2.6-basic" + }, + { + "label": "Nuxt 3", + "to": "framework/vue/examples/nuxt3" + }, + { + "label": "持久化", + "to": "framework/vue/examples/persister" + } + ] + }, + { + "label": "svelte", + "children": [ + { + "label": "简单示例", + "to": "framework/svelte/examples/simple" + }, + { + "label": "基础示例", + "to": "framework/svelte/examples/basic" + }, + { + "label": "自动重新获取/轮询/实时", + "to": "framework/svelte/examples/auto-refetching" + }, + { + "label": "SSR", + "to": "framework/svelte/examples/ssr" + }, + { + "label": "乐观更新", + "to": "framework/svelte/examples/optimistic-updates" + }, + { + "label": "演练场", + "to": "framework/svelte/examples/playground" + }, + { + "label": "星球大战", + "to": "framework/svelte/examples/star-wars" + }, + { + "label": "无限查询", + "to": "framework/svelte/examples/load-more-infinite-scroll" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "简单示例", + "to": "framework/angular/examples/simple" + }, + { + "label": "基础示例", + "to": "framework/angular/examples/basic" + }, + { + "label": "自动重新获取/轮询/实时", + "to": "framework/angular/examples/auto-refetching" + }, + { + "label": "乐观更新", + "to": "framework/angular/examples/optimistic-updates" + }, + { + "label": "分页", + "to": "framework/angular/examples/pagination" + }, + { + "label": "带最大页数的无限查询", + "to": "framework/angular/examples/infinite-query-with-max-pages" + }, + { + "label": "Angular 路由", + "to": "framework/angular/examples/router" + }, + { + "label": "RxJS 自动补全", + "to": "framework/angular/examples/rxjs" + }, + { + "label": "从服务获取查询选项", + "to": "framework/angular/examples/query-options-from-a-service" + }, + { + "label": "开发者工具内嵌面板", + "to": "framework/angular/examples/devtools-panel" + } + ] + } + ] + }, + { + "label": "插件", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "persistQueryClient", + "to": "framework/react/plugins/persistQueryClient" + }, + { + "label": "createSyncStoragePersister", + "to": "framework/react/plugins/createSyncStoragePersister" + }, + { + "label": "createAsyncStoragePersister", + "to": "framework/react/plugins/createAsyncStoragePersister" + }, + { + "label": "broadcastQueryClient (实验性)", + "to": "framework/react/plugins/broadcastQueryClient" + }, + { + "label": "createPersister (实验性)", + "to": "framework/react/plugins/createPersister" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "broadcastQueryClient (实验性)", + "to": "framework/solid/plugins/broadcastQueryClient" + }, + { + "label": "createPersister (实验性)", + "to": "framework/solid/plugins/createPersister" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "broadcastQueryClient (实验性)", + "to": "framework/vue/plugins/broadcastQueryClient" + }, + { + "label": "createPersister (实验性)", + "to": "framework/vue/plugins/createPersister" + } + ] + } + ] + } + ], + "users": [ + "Google", + "Walmart", + "Facebook", + "PayPal", + "Amazon", + "American Express", + "Microsoft", + "Target", + "Ebay", + "Autodesk", + "CarFAX", + "Docusign", + "HP", + "MLB", + "Volvo", + "Ocado", + "UPC.ch", + "EFI.com", + "ReactBricks", + "Nozzle.io", + "Uber" + ] +} \ No newline at end of file diff --git a/docs/zh-hans/eslint/eslint-plugin-query.md b/docs/zh-hans/eslint/eslint-plugin-query.md new file mode 100644 index 00000000000..8912b0b9926 --- /dev/null +++ b/docs/zh-hans/eslint/eslint-plugin-query.md @@ -0,0 +1,101 @@ +--- +source-updated-at: '2025-03-30T12:48:40.000Z' +translation-updated-at: '2025-05-06T03:48:42.197Z' +id: eslint-plugin-query +title: ESLint 插件 Query +--- +TanStack Query 提供了专属的 ESLint 插件。该插件用于强制执行最佳实践,并帮助您避免常见错误。 + +## 安装 + +该插件是一个独立包,需要单独安装: + +```bash +npm i -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +pnpm add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +yarn add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +bun add -D @tanstack/eslint-plugin-query +``` + +## 扁平化配置 (`eslint.config.js`) + +### 推荐配置 + +要启用所有推荐规则,请添加以下配置: + +```js +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', + }, + }, + // 其他配置... +] +``` + +## 传统配置 (`.eslintrc`) + +### 推荐配置 + +要启用所有推荐规则,请在 extends 中添加 `plugin:@tanstack/query/recommended`: + +```json +{ + "extends": ["plugin:@tanstack/query/recommended"] +} +``` + +### 自定义配置 + +或在 plugins 部分添加 `@tanstack/query`,并配置所需规则: + +```json +{ + "plugins": ["@tanstack/query"], + "rules": { + "@tanstack/query/exhaustive-deps": "error" + } +} +``` + +## 规则列表 + +- [@tanstack/query/exhaustive-deps](./exhaustive-deps.md) +- [@tanstack/query/no-rest-destructuring](./no-rest-destructuring.md) +- [@tanstack/query/stable-query-client](./stable-query-client.md) +- [@tanstack/query/no-unstable-deps](./no-unstable-deps.md) +- [@tanstack/query/infinite-query-property-order](./infinite-query-property-order.md) diff --git a/docs/zh-hans/eslint/exhaustive-deps.md b/docs/zh-hans/eslint/exhaustive-deps.md new file mode 100644 index 00000000000..6501bd1df9e --- /dev/null +++ b/docs/zh-hans/eslint/exhaustive-deps.md @@ -0,0 +1,46 @@ +--- +source-updated-at: '2024-01-26T20:53:47.000Z' +translation-updated-at: '2025-05-06T03:49:05.078Z' +id: exhaustive-deps +title: 彻底依赖检查 +--- +应将查询键 (query keys) 视为查询函数 (query function) 的依赖数组:所有在 queryFn 内部使用的变量都应添加到查询键中。这确保了查询能够独立缓存,并在变量变化时自动重新获取数据。 + +## 规则详情 + +**错误**代码示例: + +```tsx +/* 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) }), +} +``` + +## 何时不使用 + +如果您不关心查询键 (query keys) 的规则,则无需启用此规则。 + +## 属性 + +- [x] ✅ 推荐启用 +- [x] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/infinite-query-property-order.md b/docs/zh-hans/eslint/infinite-query-property-order.md new file mode 100644 index 00000000000..393ec6d0bdd --- /dev/null +++ b/docs/zh-hans/eslint/infinite-query-property-order.md @@ -0,0 +1,64 @@ +--- +source-updated-at: '2024-09-20T19:45:40.000Z' +translation-updated-at: '2025-05-06T03:48:55.772Z' +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, +}) +``` + +该规则的 **正确** 代码示例: + +```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, +}) +``` + +## 特性 + +- [x] ✅ 推荐 +- [x] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/no-rest-destructuring.md b/docs/zh-hans/eslint/no-rest-destructuring.md new file mode 100644 index 00000000000..44d4ccd74b3 --- /dev/null +++ b/docs/zh-hans/eslint/no-rest-destructuring.md @@ -0,0 +1,46 @@ +--- +source-updated-at: '2024-01-26T20:53:47.000Z' +translation-updated-at: '2025-05-06T03:49:19.238Z' +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 } +} +``` + +**正确**代码示例: + +```tsx +const todosQuery = useQuery({ + queryKey: ['todos'], + queryFn: () => api.getTodos(), +}) + +// 普通对象解构是允许的 +const { data: todos } = todosQuery +``` + +## 何时禁用 + +如果手动设置了 `notifyOnChangeProps` 选项,可以禁用此规则。 +由于未使用追踪查询,你需要自行指定哪些属性应触发重新渲染。 + +## 特性 + +- [x] ✅ 推荐规则 +- [ ] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/no-unstable-deps.md b/docs/zh-hans/eslint/no-unstable-deps.md new file mode 100644 index 00000000000..656603727cc --- /dev/null +++ b/docs/zh-hans/eslint/no-unstable-deps.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-08-20T18:51:35.000Z' +translation-updated-at: '2025-05-06T03:49:37.842Z' +id: no-unstable-deps +title: 禁止不稳定依赖 +--- +以下查询钩子返回的对象**不具备**引用稳定性: + +- `useQuery` +- `useSuspenseQuery` +- `useQueries` +- `useSuspenseQueries` +- `useInfiniteQuery` +- `useSuspenseInfiniteQuery` +- `useMutation` + +这些钩子返回的对象**不应**直接放入 React 钩子(如 `useEffect`、`useMemo`、`useCallback`)的依赖数组中。 +正确的做法是:解构查询钩子的返回值,并将解构后的值传入 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 +} +``` + +**正确**代码示例: + +```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 +} +``` + +## 特性 + +- [x] ✅ 推荐规则 +- [ ] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/stable-query-client.md b/docs/zh-hans/eslint/stable-query-client.md new file mode 100644 index 00000000000..e03cc16868b --- /dev/null +++ b/docs/zh-hans/eslint/stable-query-client.md @@ -0,0 +1,62 @@ +--- +source-updated-at: '2024-09-26T17:04:33.000Z' +translation-updated-at: '2025-05-06T03:48:33.223Z' +id: stable-query-client +title: 稳定的 Query Client +--- +QueryClient 包含了 QueryCache,因此你应当只为应用的生命周期创建一个 QueryClient 实例 —— 而非在每次渲染时都创建新实例。 + +> 例外情况:允许在异步服务端组件 (async Server Component) 内部创建新的 QueryClient,因为该异步函数仅在服务端调用一次。 + +## 规则详情 + +以下为该规则的 **错误** 代码示例: + +```tsx +/* eslint "@tanstack/query/stable-query-client": "error" */ + +function App() { + const queryClient = new QueryClient() + return ( + + + + ) +} +``` + +以下为该规则的 **正确** 代码示例: + +```tsx +function App() { + const [queryClient] = useState(() => new QueryClient()) + return ( + + + + ) +} +``` + +```tsx +const queryClient = new QueryClient() +function App() { + return ( + + + + ) +} +``` + +```tsx +async function App() { + const queryClient = new QueryClient() + await queryClient.prefetchQuery(options) +} +``` + +## 特性 + +- [x] ✅ 推荐配置 +- [x] 🔧 可自动修复 diff --git a/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md b/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md new file mode 100644 index 00000000000..62c8364939c --- /dev/null +++ b/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md @@ -0,0 +1,49 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-06T04:50:39.857Z' +id: Angular-HttpClient-and-other-data-fetching-clients +title: Angular HttpClient 及其他数据获取客户端 +--- +由于 TanStack Query 的请求机制基于 Promise 无感知构建,您实际上可以使用任何异步数据请求客户端,包括浏览器原生的 `fetch` API、`graphql-request` 等。 + +## 使用 Angular 的 `HttpClient` 获取数据 + +`HttpClient` 是 Angular 强大且深度集成的组成部分,具有以下优势: + +- 在单元测试中使用 [provideHttpClientTesting](https://angular.dev/guide/http/testing) 模拟响应。 +- [拦截器](https://angular.dev/guide/http/interceptors) 可用于广泛功能,如添加认证头、执行日志记录等。虽然某些数据请求库拥有自己的拦截器系统,但 `HttpClient` 拦截器与 Angular 的依赖注入系统深度集成。 +- `HttpClient` 会自动通知 [`PendingTasks`](https://angular.dev/api/core/PendingTasks#),使 Angular 能感知待处理请求。单元测试和服务端渲染 (SSR) 可利用应用 _稳定性_ 信息等待请求完成,这极大简化了 [无 Zone (Zoneless)](https://angular.dev/guide/experimental/zoneless) 应用的单元测试。 +- 使用 SSR 时,`HttpClient` 会 [缓存服务端请求](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient),避免客户端重复请求。其 SSR 缓存开箱即用。虽然 TanStack Query 的 hydration 功能更强大但需额外配置,具体选择取决于您的使用场景。 + +### 在 `queryFn` 中使用 Observable + +由于 TanStack Query 是基于 Promise 的库,需将 `HttpClient` 的 Observable 转换为 Promise。可通过 `rxjs` 的 `lastValueFrom` 或 `firstValueFrom` 实现: + +```ts +@Component({ + // ... +}) +class ExampleComponent { + private readonly http = inject(HttpClient) + + readonly query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + lastValueFrom( + this.http.get('https://api.github.com/repos/tanstack/query'), + ), + })) +} +``` + +> 随着 Angular 逐步将 RxJS 作为可选依赖,预计 `HttpClient` 未来也将支持 Promise。 +> +> TanStack Query for Angular 的 Observable 支持已在规划中。 + +## 对比表格 + +| 数据请求客户端 | 优势 | 局限性 | +| --------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------- | +| **Angular HttpClient** | 功能丰富且与 Angular 深度集成。 | 需将 Observable 转换为 Promise。 | +| **Fetch** | 浏览器原生 API,不增加包体积。 | 功能基础,缺乏高级特性。 | +| **专用库如 `graphql-request`** | 针对特定场景的专用功能。 | 若非 Angular 专用库则框架集成度较差。 | diff --git a/docs/zh-hans/framework/angular/devtools.md b/docs/zh-hans/framework/angular/devtools.md new file mode 100644 index 00000000000..8f7afafa4f2 --- /dev/null +++ b/docs/zh-hans/framework/angular/devtools.md @@ -0,0 +1,116 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-06T04:52:21.298Z' +id: devtools +title: 开发者工具 +--- +## 启用开发者工具 (devtools) + +开发者工具 (devtools) 可帮助您调试和检查查询 (queries) 与变更 (mutations)。您可以通过在 `provideTanStackQuery` 中添加 `withDevtools` 来启用开发者工具。 + +默认情况下,当 Angular 的 [`isDevMode`](https://angular.dev/api/core/isDevMode) 返回 true 时,开发者工具会自动启用。因此您无需担心在生产构建中排除它们。核心工具会按需懒加载,并且不会包含在打包代码中。大多数情况下,您只需在 `provideTanStackQuery` 中添加 `withDevtools()` 即可,无需额外配置。 + +```ts +import { + QueryClient, + provideTanStackQuery, + withDevtools, +} from '@tanstack/angular-query-experimental' + +export const appConfig: ApplicationConfig = { + providers: [provideTanStackQuery(new QueryClient(), withDevtools())], +} +``` + +## 配置开发者工具加载条件 + +如果需要更精细地控制开发者工具的加载时机,可以使用 `loadDevtools` 选项。这在基于环境配置加载开发者工具时特别有用。例如,您的测试环境可能运行在生产模式下,但仍需要开发者工具可用。 + +当不设置该选项或设置为 'auto' 时,开发者工具会在 Angular 处于开发模式时加载。 + +```ts +provideTanStackQuery(new QueryClient(), withDevtools()) + +// 等价于 +provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ loadDevtools: 'auto' })), +) +``` + +当设置为 true 时,开发者工具会在开发和生产模式下都加载。 + +```ts +provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ loadDevtools: true })), +) +``` + +当设置为 false 时,开发者工具将不会加载。 + +```ts +provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ loadDevtools: false })), +) +``` + +`withDevtools` 的选项通过回调函数返回,以支持通过信号 (signals) 实现响应式。以下示例中,我们通过监听键盘快捷键的 RxJS 可观察对象创建信号。当事件触发时,开发者工具会按需加载。这种技术允许您支持在生产模式下按需加载开发者工具,而无需将完整工具包含在打包代码中。 + +```ts +@Injectable({ providedIn: 'root' }) +class DevtoolsOptionsManager { + loadDevtools = toSignal( + fromEvent(document, 'keydown').pipe( + map( + (event): boolean => + event.metaKey && event.ctrlKey && event.shiftKey && event.key === 'D', + ), + scan((acc, curr) => acc || curr, false), + ), + { + initialValue: false, + }, + ) +} + +export const appConfig: ApplicationConfig = { + providers: [ + provideHttpClient(), + provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ + initialIsOpen: true, + loadDevtools: inject(DevtoolsOptionsManager).loadDevtools(), + })), + ), + ], +} +``` + +### 配置选项 + +以下选项 `client`、`position`、`errorTypes`、`buttonPosition` 和 `initialIsOpen` 支持通过信号实现响应式。 + +- `loadDevtools?: 'auto' | boolean` + - 默认为 `auto`:在开发模式下按需加载开发者工具,生产模式下跳过加载。 + - 用于控制是否加载开发者工具。 +- `initialIsOpen?: Boolean` + - 设置为 `true` 可使工具面板默认展开 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` + - 默认为 `bottom-right` + - TanStack 徽标按钮的位置,用于展开/关闭开发者工具面板 + - 设为 `relative` 时,按钮将呈现在您渲染开发者工具的位置。 +- `position?: "top" | "bottom" | "left" | "right"` + - 默认为 `bottom` + - Angular Query 开发者工具面板的位置 +- `client?: QueryClient`, + - 用于指定自定义 QueryClient。未设置时,将注入通过 `provideTanStackQuery` 提供的 QueryClient。 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}[]` + - 用于预定义可在查询中触发的错误类型。当从 UI 切换该错误时,初始化器 (initializer) 会以特定查询为参数被调用,必须返回一个 Error 对象。 +- `styleNonce?: string` + - 用于向添加到文档头部的 style 标签传递 nonce 值。这在您使用内容安全策略 (CSP) nonce 允许内联样式时非常有用。 +- `shadowDOMTarget?: ShadowRoot` + - 默认行为会将开发者工具的样式应用到 DOM 的 head 标签中。 + - 用于指定 shadow DOM 目标,使样式应用到 shadow DOM 内部而非 light DOM 的 head 标签中。 diff --git a/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md b/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..a83cb973470 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md @@ -0,0 +1,55 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-06T05:08:13.992Z' +id: background-fetching-indicators +title: 后台获取指示器 +--- +## 后台获取指示器 + +查询的 `status === 'pending'` 状态足以显示查询的初始硬加载状态,但有时您可能希望额外显示一个指示器,表明查询正在后台重新获取。为此,查询还提供了一个 `isFetching` 布尔值,无论 `status` 变量的状态如何,您都可以用它来显示查询正处于获取状态: + +```angular-ts +@Component({ + selector: 'todos', + template: ` + @if (todosQuery.isPending()) { + 加载中... + } @else if (todosQuery.isError()) { + 发生错误:{{ todosQuery.error().message }} + } @else if (todosQuery.isSuccess()) { + @if (todosQuery.isFetching()) { + 正在刷新... + } + @for (todos of todosQuery.data(); track todo.id) { + + } + } + `, +}) +class TodosComponent { + todosQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) +} +``` + +## 显示全局后台获取加载状态 + +除了单个查询的加载状态外,如果您希望在**任何**查询(包括后台查询)处于获取状态时显示全局加载指示器,可以使用 `useIsFetching` 钩子: + +```angular-ts +import { injectIsFetching } from '@tanstack/angular-query-experimental' + +@Component({ + selector: 'global-loading-indicator', + template: ` + @if (isFetching()) { +
查询正在后台获取中...
+ } + `, +}) +export class GlobalLoadingIndicatorComponent { + isFetching = injectIsFetching() +} +``` diff --git a/docs/zh-hans/framework/angular/guides/caching.md b/docs/zh-hans/framework/angular/guides/caching.md new file mode 100644 index 00000000000..267aeb3db5d --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/caching.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-06T05:07:51.213Z' +id: caching +title: 缓存 +--- +> 请先完整阅读 [重要默认配置](../important-defaults) 再阅读本指南 + +## 基础示例 + +这个缓存示例展示了以下场景及其生命周期: + +- 包含缓存数据与不包含缓存数据的查询实例 +- 后台重新获取 +- 非活跃查询 +- 垃圾回收 + +假设我们使用默认的 `gcTime`(5 分钟)和默认的 `staleTime`(0)。 + +- 初始化一个新的 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos }))` 实例: + - 由于之前没有使用 `['todos']` 查询键发起过其他查询,该查询会显示硬加载状态并发起网络请求获取数据。 + - 当网络请求完成时,返回的数据会被缓存在 `['todos']` 键下。 + - 数据会在配置的 `staleTime`(默认为 `0`,即立即)后被标记为过时。 + +- 在其他地方初始化第二个 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例: + - 由于缓存中已存在第一个查询的 `['todos']` 键数据,该数据会立即从缓存中返回。 + - 新实例会使用其查询函数触发一个新的网络请求。 + - 注意:无论两个 `fetchTodos` 查询函数是否相同,两个查询的 [`status`](../../reference/injectQuery)(包括 `isFetching`、`isPending` 等相关值)都会更新,因为它们共享相同的查询键。 + - 当请求成功完成时,`['todos']` 键下的缓存数据会更新为新数据,两个实例都会同步到新数据。 + +- 两个 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例被销毁且不再使用: + - 由于该查询没有活跃实例,系统会基于 `gcTime` 设置垃圾回收超时(默认为 5 分钟)来删除并回收该查询。 + +- 在缓存超时完成前,另一个 `injectQuery(() => ({ queryKey: ['todos'], queyFn: fetchTodos })` 实例挂载: + - 查询会立即返回可用的缓存数据,同时 `fetchTodos` 函数在后台运行。当成功完成后,会用新数据更新缓存。 + +- 最后一个 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例被销毁: + - 在 5 分钟内没有再出现 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例。 + - `['todos']` 键下的缓存数据被删除并完成垃圾回收。 diff --git a/docs/zh-hans/framework/angular/guides/default-query-function.md b/docs/zh-hans/framework/angular/guides/default-query-function.md new file mode 100644 index 00000000000..f6fc8aa84c1 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/default-query-function.md @@ -0,0 +1,49 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-06T05:07:23.242Z' +id: default-query-function +title: 默认查询函数 +--- +如果你出于某种原因希望在整个应用中共享同一个查询函数,仅通过查询键 (query key) 来标识应该获取什么数据,那么可以通过为 TanStack Query 提供 **默认查询函数 (default query function)** 来实现: + +```ts +// 定义一个默认查询函数,它将接收查询键作为参数 +const defaultQueryFn: QueryFunction = async ({ queryKey }) => { + const { data } = await axios.get( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + ) + return data +} + +// 通过 defaultOptions 将默认查询函数提供给整个应用 +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, + }, +}) + +bootstrapApplication(MyAppComponent, { + providers: [provideTanStackQuery(queryClient)], +}) + +export class PostsComponent { + // 现在你只需要传入一个键即可! + postsQuery = injectQuery>(() => ({ + queryKey: ['/posts'], + })) + // ... +} + +export class PostComponent { + // 你甚至可以省略 queryFn,直接传入配置项 + postQuery = injectQuery(() => ({ + enabled: this.postIdSignal() > 0, + queryKey: [`/posts/${this.postIdSignal()}`], + })) + // ... +} +``` + +如果需要覆盖默认的 queryFn,只需像平常那样提供你自己的查询函数即可。 diff --git a/docs/zh-hans/framework/angular/guides/dependent-queries.md b/docs/zh-hans/framework/angular/guides/dependent-queries.md new file mode 100644 index 00000000000..34e3c56222e --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/dependent-queries.md @@ -0,0 +1,65 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:07:06.536Z' +id: dependent-queries +title: 依赖查询 +--- +## 依赖查询 (Dependent Query) + +依赖查询(或串行查询)需要等待前一个查询完成才能执行。实现这一功能非常简单,只需使用 `enabled` 选项来告知查询何时可以运行: + +```ts +// 获取用户信息 +userQuery = injectQuery(() => ({ + queryKey: ['user', email], + queryFn: getUserByEmail, +})) + +// 然后获取用户的项目列表 +projectsQuery = injectQuery(() => ({ + queryKey: ['projects', this.userQuery.data()?.id], + queryFn: getProjectsByUser, + // 该查询只有在用户ID存在时才会执行 + enabled: !!this.userQuery.data()?.id, +})) +``` + +`projects` 查询会以以下状态开始: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'idle' +``` + +一旦 `user` 数据可用,`projects` 查询将被 `enabled` 并转为: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'fetching' +``` + +当项目数据获取完成后,状态会变为: + +```tsx +status: 'success' +isPending: false +fetchStatus: 'idle' +``` + +## injectQueries 依赖查询 + +动态并行查询 —— `injectQueries` 也可以依赖于前一个查询,实现方式如下: + +```ts +// injectQueries 功能正在 Angular Query 中开发 +``` + +**注意**:`injectQueries` 返回的是**查询结果数组** + +## 关于性能的说明 + +依赖查询本质上会形成一种[请求瀑布流 (request waterfall)](./request-waterfalls.md),这会损害性能。假设两个查询耗时相同,串行执行总是比并行执行多花一倍时间,对于高延迟的客户端尤为不利。如果可能,最好重构后端 API 使两个查询能并行获取,尽管这在实际中不一定可行。 + +在上面的例子中,与其先获取 `getUserByEmail` 才能调用 `getProjectsByUser`,不如新增一个 `getProjectsByUserEmail` 查询来消除瀑布流。 diff --git a/docs/zh-hans/framework/angular/guides/disabling-queries.md b/docs/zh-hans/framework/angular/guides/disabling-queries.md new file mode 100644 index 00000000000..b5d0220538f --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/disabling-queries.md @@ -0,0 +1,119 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-06T05:06:44.473Z' +id: disabling-queries +title: 禁用/暂停查询 +--- +如果你希望阻止某个查询自动执行,可以使用 `enabled = false` 选项。该选项也支持传入返回布尔值的回调函数。 + +当 `enabled` 为 `false` 时: + +- 如果查询存在缓存数据,则初始化状态为 `status === 'success'` 或 `isSuccess` +- 如果查询没有缓存数据,则初始化状态为 `status === 'pending'` 且 `fetchStatus === 'idle'` +- 查询不会在挂载时自动获取数据 +- 查询不会在后台自动重新获取数据 +- 查询会忽略 query client 的 `invalidateQueries` 和 `refetchQueries` 调用(这些调用通常会导致查询重新获取数据) +- 通过 `injectQuery` 返回的 `refetch` 可用于手动触发查询获取数据(但无法与 `skipToken` 配合使用) + +> TypeScript 用户可考虑使用 [skipToken](#typesafe-disabling-of-queries-using-skiptoken) 作为 `enabled = false` 的替代方案 + +```angular-ts +@Component({ + selector: 'todos', + template: `
+ + + @if (query.data()) { +
    + @for (todo of query.data(); track todo.id) { +
  • {{ todo.title }}
  • + } +
+ } @else { + @if (query.isError()) { + Error: {{ query.error().message }} + } @else if (query.isLoading()) { + Loading... + } @else if (!query.isLoading() && !query.isError()) { + Not ready ... + } + } + +
{{ query.isLoading() ? 'Fetching...' : '' }}
+
`, +}) +export class TodosComponent { + query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + enabled: false, + })) +} +``` + +永久禁用查询会使你无法使用 TanStack Query 的许多优秀特性(如后台重新获取),这也不符合惯用模式。这会让你从声明式模式(定义查询运行依赖条件)退回到命令式模式(点击按钮时才获取数据),且无法向 `refetch` 传递参数。通常你需要的只是一个延迟初始获取的惰性查询: + +## 惰性查询 + +`enabled` 选项不仅能永久禁用查询,还能实现动态启用/禁用。典型场景是筛选表单——只有当用户输入筛选值后才发起首次请求: + +```angular-ts +@Component({ + selector: 'todos', + template: ` +
+ // 🚀 应用筛选条件时将启用并执行查询 + + +
+ `, +}) +export class TodosComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + enabled: !!this.filter(), + })) +} +``` + +### isLoading (原名为 `isInitialLoading`) + +惰性查询会始终处于 `status: 'pending'` 状态,因为 `pending` 表示尚无数据。虽然技术上是正确的,但由于当前并未获取数据(查询未被 _启用_),这个状态可能不适合用来显示加载指示器。 + +对于禁用或惰性查询,可以使用 `isLoading` 标志。这是一个衍生标志,由以下条件计算得出: + +`isPending && isFetching` + +因此只有当查询首次获取数据时才会返回 `true`。 + +## 使用 `skipToken` 实现类型安全的查询禁用 + +TypeScript 用户可以使用 `skipToken` 禁用查询。适用于需要根据条件禁用查询,同时保持类型安全的场景。 + +> 重要提示:`injectQuery` 返回的 `refetch` 无法与 `skipToken` 配合使用。除此之外,`skipToken` 的行为与 `enabled: false` 完全一致。 + +```angular-ts +import { skipToken, injectQuery } from '@tanstack/query-angular' + +@Component({ + selector: 'todos', + template: ` +
+ // 🚀 应用筛选条件时将启用并执行查询 + + +
+ `, +}) +export class TodosComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: this.filter() ? () => fetchTodos(this.filter()) : skipToken, + })) +} +``` diff --git a/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..b0cae483abe --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:58:12.575Z' +id: does-this-replace-client-state +title: Does TanStack Query replace global state managers? +ref: docs/zh-hans/framework/react/guides/does-this-replace-client-state.md +replace: + useQuery: injectQuery + useMutation: injectMutation + hook: function +--- + diff --git a/docs/zh-hans/framework/angular/guides/filters.md b/docs/zh-hans/framework/angular/guides/filters.md new file mode 100644 index 00000000000..f3416586d69 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/filters.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:58:12.518Z' +id: filters +title: Filters +ref: docs/zh-hans/framework/react/guides/filters.md +--- + diff --git a/docs/zh-hans/framework/angular/guides/important-defaults.md b/docs/zh-hans/framework/angular/guides/important-defaults.md new file mode 100644 index 00000000000..af2707ee0bf --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/important-defaults.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:58:12.464Z' +id: important-defaults +title: Important Defaults +ref: docs/zh-hans/framework/react/guides/important-defaults.md +replace: + React: Angular + react-query: angular-query + useQuery: injectQuery + useInfiniteQuery: injectInfiniteQuery + useMemo and useCallback: setting signal values +--- + +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/angular/guides/infinite-queries.md b/docs/zh-hans/framework/angular/guides/infinite-queries.md new file mode 100644 index 00000000000..bbe0b43adcd --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/infinite-queries.md @@ -0,0 +1,243 @@ +--- +source-updated-at: '2024-07-19T09:36:35.000Z' +translation-updated-at: '2025-05-06T05:05:56.640Z' +id: infinite-queries +title: 无限查询 +--- +## 无限查询 (Infinite Queries) + +能够以增量方式"加载更多"数据到现有数据集或实现"无限滚动"的列表渲染是一种非常常见的 UI 模式。TanStack Query 为此提供了一个名为 `injectInfiniteQuery` 的 `injectQuery` 变体,专门用于查询这类列表。 + +使用 `injectInfiniteQuery` 时,您会注意到以下几点不同: + +- `data` 现在是一个包含无限查询数据的对象: + - `data.pages` 数组包含已获取的页面 + - `data.pageParams` 数组包含用于获取页面的参数 +- 现在提供了 `fetchNextPage` 和 `fetchPreviousPage` 函数(必须实现 `fetchNextPage`) +- 新增了 `initialPageParam` 选项(必须指定初始页面参数) +- 提供了 `getNextPageParam` 和 `getPreviousPageParam` 选项,用于确定是否有更多数据要加载以及获取这些数据所需的信息。这些信息会作为额外参数传递给查询函数 +- 新增了 `hasNextPage` 布尔值,当 `getNextPageParam` 返回值不为 `null` 或 `undefined` 时为 `true` +- 新增了 `hasPreviousPage` 布尔值,当 `getPreviousPageParam` 返回值不为 `null` 或 `undefined` 时为 `true` +- 新增了 `isFetchingNextPage` 和 `isFetchingPreviousPage` 布尔值,用于区分后台刷新状态和加载更多状态 + +> 注意:选项 `initialData` 或 `placeholderData` 需要符合具有 `data.pages` 和 `data.pageParams` 属性的对象结构。 + +## 示例 + +假设我们有一个 API,它基于 `cursor` 索引每次返回 3 个 `projects` 页面,同时返回可用于获取下一组项目的游标: + +```tsx +fetch('/api/projects?cursor=0') +// { data: [...], nextCursor: 3} +fetch('/api/projects?cursor=3') +// { data: [...], nextCursor: 6} +fetch('/api/projects?cursor=6') +// { data: [...], nextCursor: 9} +fetch('/api/projects?cursor=9') +// { data: [...] } +``` + +通过这些信息,我们可以创建一个"加载更多"的 UI: +- 默认情况下等待 `injectInfiniteQuery` 请求第一组数据 +- 在 `getNextPageParam` 中返回下一个查询的信息 +- 调用 `fetchNextPage` 函数 + +```angular-ts +import { Component, computed, inject } from '@angular/core' +import { injectInfiniteQuery } from '@tanstack/angular-query-experimental' +import { lastValueFrom } from 'rxjs' +import { ProjectsService } from './projects-service' + +@Component({ + selector: 'example', + templateUrl: './example.component.html', +}) +export class Example { + projectsService = inject(ProjectsService) + + query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: async ({ pageParam }) => { + return lastValueFrom(this.projectsService.getProjects(pageParam)) + }, + initialPageParam: 0, + getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined, + getNextPageParam: (lastPage) => lastPage.nextId ?? undefined, + maxPages: 3, + })) + + nextButtonDisabled = computed( + () => !this.#hasNextPage() || this.#isFetchingNextPage(), + ) + nextButtonText = computed(() => + this.#isFetchingNextPage() + ? '正在加载更多...' + : this.#hasNextPage() + ? '加载更新' + : '已加载全部内容', + ) + + #hasNextPage = this.query.hasNextPage + #isFetchingNextPage = this.query.isFetchingNextPage +} +``` + +```angular-html +
+ @if (query.isPending()) { +

加载中...

+ } @else if (query.isError()) { + 错误: {{ query?.error().message }} + } @else { @for (page of query?.data().pages; track $index) { @for (project of + page.data; track project.id) { +

{{ project.name }} {{ project.id }}

+ } } +
+ +
+ } +
+``` + +必须理解的是,在后台正在进行数据获取时调用 `fetchNextPage` 有可能覆盖正在后台刷新的数据。当同时渲染列表和触发 `fetchNextPage` 时,这种情况变得尤为关键。 + +请记住,一个 InfiniteQuery 只能有一个正在进行的获取操作。所有页面共享单个缓存条目,尝试同时进行两次获取可能会导致数据覆盖。 + +如果您希望启用并行获取,可以在 `fetchNextPage` 中使用 `{ cancelRefetch: false }` 选项(默认为 true)。 + +为了确保查询过程无冲突,强烈建议检查查询是否不处于 `isFetching` 状态,特别是当用户不会直接控制该调用时。 + +```angular-ts +@Component({ + template: ` `, +}) +export class Example { + query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: async ({ pageParam }) => { + return lastValueFrom(this.projectsService.getProjects(pageParam)) + }, + })) + + fetchNextPage() { + // 如果正在获取,则不执行任何操作 + if (this.query.isFetching()) return + this.query.fetchNextPage() + } +} +``` + +## 当无限查询需要重新获取时会发生什么? + +当无限查询变为 `stale` 并需要重新获取时,每组数据会从第一组开始`顺序`获取。这确保了即使底层数据发生变更,我们也不会使用过期的游标,从而避免获取重复记录或跳过记录。如果无限查询的结果从 queryCache 中移除,分页将从初始状态重新开始,仅请求初始组。 + +## 如何实现双向无限列表? + +双向列表可以通过使用 `getPreviousPageParam`、`fetchPreviousPage`、`hasPreviousPage` 和 `isFetchingPreviousPage` 属性和函数来实现。 + +```ts +query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, +})) +``` + +## 如何以相反顺序显示页面? + +有时您可能希望以相反的顺序显示页面。这种情况下,可以使用 `select` 选项: + +```ts +query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), +})) +``` + +## 如何手动更新无限查询? + +### 手动移除第一页: + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1), +})) +``` + +### 手动从单个页面中移除某个值: + +```tsx +const newPagesArray = + oldPagesArray?.pages.map((page) => + page.filter((val) => val.id !== updatedId), + ) ?? [] + +queryClient.setQueryData(['projects'], (data) => ({ + pages: newPagesArray, + pageParams: data.pageParams, +})) +``` + +### 仅保留第一页: + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(0, 1), + pageParams: data.pageParams.slice(0, 1), +})) +``` + +请确保始终保持 pages 和 pageParams 的相同数据结构! + +## 如何限制页面数量? + +在某些使用场景中,您可能希望限制查询数据中存储的页面数量以提高性能和用户体验: +- 当用户可以加载大量页面时(内存使用) +- 当需要重新获取包含数十个页面的无限查询时(网络使用:所有页面都会顺序获取) + +解决方案是使用"有限无限查询"。这可以通过结合使用 `maxPages` 选项与 `getNextPageParam` 和 `getPreviousPageParam` 来实现,以便在需要时双向获取页面。 + +在以下示例中,查询数据 pages 数组中仅保留 3 个页面。如果需要重新获取,只会顺序重新获取 3 个页面。 + +```ts +injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, + maxPages: 3, +})) +``` + +## 如果我的 API 不返回游标怎么办? + +如果您的 API 不返回游标,您可以使用 `pageParam` 作为游标。因为 `getNextPageParam` 和 `getPreviousPageParam` 也会获取当前页面的 `pageParam`,所以您可以用它来计算下一个/上一个页面参数。 + +```ts +injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, allPages, lastPageParam) => { + if (lastPage.length === 0) { + return undefined + } + return lastPageParam + 1 + }, + getPreviousPageParam: (firstPage, allPages, firstPageParam) => { + if (firstPageParam <= 1) { + return undefined + } + return firstPageParam - 1 + }, +})) +``` diff --git a/docs/zh-hans/framework/angular/guides/initial-query-data.md b/docs/zh-hans/framework/angular/guides/initial-query-data.md new file mode 100644 index 00000000000..a60a961cb9b --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/initial-query-data.md @@ -0,0 +1,139 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:04:23.076Z' +id: initial-query-data +title: 初始查询数据 +--- +在需要之前,有多种方式可以为查询提供初始数据到缓存中: + +- 声明式: + - 为查询提供 `initialData`,以便在缓存为空时预填充 +- 命令式: + - [使用 `queryClient.prefetchQuery` 预取数据](./prefetching.md) + - [使用 `queryClient.setQueryData` 手动将数据放入缓存](./prefetching.md) + +## 使用 `initialData` 预填充查询 + +有时你可能已经在应用中拥有查询的初始数据,并可以直接提供给查询。这种情况下,你可以使用 `config.initialData` 选项来设置查询的初始数据,并跳过初始加载状态! + +> 重要提示:`initialData` 会被持久化到缓存中,因此不建议为此选项提供占位符、部分或不完整的数据,而应使用 `placeholderData`。 + +```ts +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, +})) +``` + +### `staleTime` 和 `initialDataUpdatedAt` + +默认情况下,`initialData` 被视为完全新鲜的数据,就像刚刚获取的一样。这也意味着它会影响到 `staleTime` 选项的解释方式。 + +- 如果为查询观察者配置了 `initialData` 且没有 `staleTime`(默认 `staleTime: 0`),查询会立即重新获取: + +```ts +// 会立即显示 initialTodos,但在创建组件或服务的实例时也会立即重新获取 todos +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, +})) +``` + +- 如果为查询观察者配置了 `initialData` 且 `staleTime` 为 `1000` 毫秒,数据将在相同时间内被视为新鲜数据,就像刚刚从查询函数中获取的一样。 + +```ts +// 立即显示 initialTodos,但在 1000 毫秒后遇到另一个交互事件之前不会重新获取 +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 1000, +})) +``` + +- 那么如果 `initialData` 不是完全新鲜的怎么办?这引出了最后一种配置,它实际上是最准确的,并使用了一个名为 `initialDataUpdatedAt` 的选项。该选项允许你传递一个以毫秒为单位的 JS 时间戳,表示 `initialData` 上次更新的时间,例如 `Date.now()` 提供的时间戳。请注意,如果你有一个 Unix 时间戳,需要通过乘以 `1000` 将其转换为 JS 时间戳。 + +```ts +// 立即显示 initialTodos,但在 1 分钟后遇到另一个交互事件之前不会重新获取 +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 60 * 1000, // 1 分钟 + // 可能是 10 秒前或 10 分钟前 + initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052 +})) +``` + +该选项允许 `staleTime` 用于其原始目的,即确定数据需要多新鲜,同时也允许在初始化时重新获取数据(如果 `initialData` 比 `staleTime` 更旧)。在上面的示例中,我们的数据需要在 1 分钟内保持新鲜,并且我们可以提示查询 `initialData` 上次更新的时间,以便查询自行决定是否需要重新获取数据。 + +> 如果你更愿意将数据视为 **预取数据**,建议使用 `prefetchQuery` 或 `fetchQuery` API 预先填充缓存,从而可以独立于 `initialData` 配置 `staleTime`。 + +### 初始数据函数 + +如果访问查询初始数据的过程很耗时,或者你不希望在每次服务或组件实例上执行该过程,可以将一个函数作为 `initialData` 的值传递。该函数仅在查询初始化时执行一次,从而节省宝贵的内存和/或 CPU: + +```ts +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: () => getExpensiveTodos(), +})) +``` + +### 从缓存中获取初始数据 + +在某些情况下,你可以从另一个查询的缓存结果中为查询提供初始数据。一个很好的例子是从 todos 列表查询中搜索缓存的单个 todo 项数据,然后将其用作单个 todo 查询的初始数据: + +```ts +result = injectQuery(() => ({ + queryKey: ['todo', this.todoId()], + queryFn: () => fetch('/todos'), + initialData: () => { + // 使用 'todos' 查询中的一个 todo 作为此 todo 查询的初始数据 + return this.queryClient + .getQueryData(['todos']) + ?.find((d) => d.id === this.todoId()) + }, +})) +``` + +### 从缓存中获取初始数据并设置 `initialDataUpdatedAt` + +从缓存中获取初始数据意味着你用于查找初始数据的源查询可能已经过时。与其使用人为的 `staleTime` 来防止查询立即重新获取,建议将源查询的 `dataUpdatedAt` 传递给 `initialDataUpdatedAt`。这为查询实例提供了所需的所有信息,以确定是否以及何时需要重新获取查询,而不管是否提供了初始数据。 + +```ts +result = injectQuery(() => ({ + queryKey: ['todos', this.todoId()], + queryFn: () => fetch(`/todos/${this.todoId()}`), + initialData: () => + queryClient.getQueryData(['todos'])?.find((d) => d.id === this.todoId()), + initialDataUpdatedAt: () => + queryClient.getQueryState(['todos'])?.dataUpdatedAt, +})) +``` + +### 从缓存中条件性获取初始数据 + +如果用于查找初始数据的源查询已经过时,你可能根本不想使用缓存的数据,而是直接从服务器获取。为了更容易做出这个决定,可以使用 `queryClient.getQueryState` 方法来获取有关源查询的更多信息,包括 `state.dataUpdatedAt` 时间戳,你可以用它来决定查询是否足够“新鲜”以满足你的需求: + +```ts +result = injectQuery(() => ({ + queryKey: ['todo', this.todoId()], + queryFn: () => fetch(`/todos/${this.todoId()}`), + initialData: () => { + // 获取查询状态 + const state = queryClient.getQueryState(['todos']) + + // 如果查询存在且数据不超过 10 秒... + if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) { + // 返回单个 todo + return state.data.find((d) => d.id === this.todoId()) + } + + // 否则返回 undefined,让它从硬加载状态获取! + }, +})) +``` diff --git a/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..6fea0ea6e43 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2025-01-03T15:54:42.000Z' +translation-updated-at: '2025-05-06T05:03:17.566Z' +id: invalidations-from-mutations +title: 从变更中失效 +--- +## 变更触发的失效 + +使查询失效只是成功的一半,而知道**何时**使其失效则是另一半。通常,当应用中的某个变更 (mutation) 成功执行时,应用中很可能存在相关的查询需要失效,并可能需要重新获取数据以反映该变更带来的新变化。 + +例如,假设我们有一个用于发布新待办事项的变更: + +```ts +mutation = injectMutation(() => ({ + mutationFn: postTodo, +})) +``` + +当 `postTodo` 变更成功执行时,我们很可能希望所有 `todos` 查询失效并可能重新获取,以显示新的待办事项。为此,你可以使用 `injectMutation` 的 `onSuccess` 选项和 `client` 的 `invalidateQueries` 函数: + +```ts +import { + injectMutation, + QueryClient, +} from '@tanstack/angular-query-experimental' + +export class TodosComponent { + queryClient = inject(QueryClient) + + // 当此变更成功时,使所有带有 `todos` 或 `reminders` 查询键 (query key) 的查询失效 + mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: () => { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + this.queryClient.invalidateQueries({ queryKey: ['reminders'] }) + }, + })) +} +``` + +你可以利用 [`injectMutation` 函数](./mutations.md) 中提供的任何回调来设置失效逻辑。 diff --git a/docs/zh-hans/framework/angular/guides/mutation-options.md b/docs/zh-hans/framework/angular/guides/mutation-options.md new file mode 100644 index 00000000000..bbf30215b1e --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/mutation-options.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-11-20T12:58:00.000Z' +translation-updated-at: '2025-05-06T05:03:00.167Z' +id: query-options +title: 变更选项 +--- +在多个地方共享变更选项 (mutation options) 的最佳方式之一,就是使用 `mutationOptions` 辅助函数。在运行时,这个辅助函数会原样返回你传入的内容,但[配合 TypeScript](../../typescript#typing-query-options) 使用时能带来诸多优势。你可以在一个地方定义变更操作的所有可能选项,同时还能获得完整的类型推断和类型安全。 + +```ts +export class QueriesService { + private http = inject(HttpClient) + + updatePost(id: number) { + return mutationOptions({ + mutationFn: (post: Post) => Promise.resolve(post), + mutationKey: ['updatePost', id], + onSuccess: (newPost) => { + // ^? newPost: Post + this.queryClient.setQueryData(['posts', id], newPost) + }, + }) + } +} +``` diff --git a/docs/zh-hans/framework/angular/guides/mutations.md b/docs/zh-hans/framework/angular/guides/mutations.md new file mode 100644 index 00000000000..e536edb60a8 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/mutations.md @@ -0,0 +1,299 @@ +--- +source-updated-at: '2024-07-19T09:36:35.000Z' +translation-updated-at: '2025-05-06T05:02:49.197Z' +id: mutations +title: 变更 +--- +与查询 (query) 不同,变更 (mutation) 通常用于创建/更新/删除数据或执行服务端副作用。为此,TanStack Query 导出了 `injectMutation` 函数。 + +以下是一个向服务器添加新待办事项的变更示例: + +```angular-ts +@Component({ + template: ` +
+ @if (mutation.isPending()) { + 添加待办事项中... + } @else if (mutation.isError()) { +
发生错误:{{ mutation.error()?.message }}
+ } @else if (mutation.isSuccess()) { +
待办事项已添加!
+ } + +
+ `, +}) +export class TodosComponent { + todoService = inject(TodoService) + mutation = injectMutation(() => ({ + mutationFn: (todoId: number) => + lastValueFrom(this.todoService.create(todoId)), + })) +} +``` + +变更在任何时刻只能处于以下状态之一: + +- `isIdle` 或 `status === 'idle'` - 变更当前处于空闲或重置状态 +- `isPending` 或 `status === 'pending'` - 变更正在执行中 +- `isError` 或 `status === 'error'` - 变更遇到错误 +- `isSuccess` 或 `status === 'success'` - 变更成功完成且数据可用 + +除这些主要状态外,根据变更状态还可获取更多信息: + +- `error` - 如果变更处于 `error` 状态,可通过 `error` 属性获取错误信息 +- `data` - 如果变更处于 `success` 状态,可通过 `data` 属性获取返回数据 + +在上例中,您还可以看到通过调用 `mutate` 函数并传入**单个变量或对象**来向变更函数传递参数。 + +仅使用变量时,变更并不特殊,但当与 `onSuccess` 选项、[Query Client 的 `invalidateQueries` 方法](../../../reference/QueryClient.md#queryclientinvalidatequeries) 以及 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 结合使用时,变更将成为非常强大的工具。 + +## 重置变更状态 + +有时您需要清除变更请求的 `error` 或 `data`。为此,可以使用 `reset` 函数处理: + +```angular-ts +@Component({ + standalone: true, + selector: 'todo-item', + imports: [ReactiveFormsModule], + template: ` +
+ @if (mutation.error()) { +
{{ mutation.error() }}
+ } + +
+ +
+ `, +}) +export class TodosComponent { + mutation = injectMutation(() => ({ + mutationFn: createTodo, + })) + + fb = inject(NonNullableFormBuilder) + + todoForm = this.fb.group({ + title: this.fb.control('', { + validators: [Validators.required], + }), + }) + + title = toSignal(this.todoForm.controls.title.valueChanges, { + initialValue: '', + }) + + onCreateTodo = () => { + this.mutation.mutate(this.title()) + } +} +``` + +## 变更副作用 + +`injectMutation` 提供了一些辅助选项,允许在变更生命周期的任何阶段快速实现副作用。这些选项对于[变更后使查询失效并重新获取](./invalidations-from-mutations.md)甚至[乐观更新](./optimistic-updates.md)非常有用。 + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + onMutate: (variables) => { + // 变更即将执行! + // 可选返回包含数据的上下文,用于回滚等场景 + return { id: 1 } + }, + onError: (error, variables, context) => { + // 发生错误! + console.log(`正在回滚 ID 为 ${context.id} 的乐观更新`) + }, + onSuccess: (data, variables, context) => { + // 成功! + }, + onSettled: (data, error, variables, context) => { + // 无论成功或失败都会执行! + }, +})) +``` + +在任何回调函数中返回 Promise 时,会先等待该 Promise 完成再执行下一个回调: + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: async () => { + console.log("我先执行!") + }, + onSettled: async () => { + console.log("我第二个执行!") + }, +})) +``` + +您可能希望在调用 `mutate` 时**触发额外的回调**,而不仅限于 `injectMutation` 定义的回调。这可用于触发组件特定的副作用。为此,您可以在变更变量后向 `mutate` 函数提供相同的回调选项。支持的选项包括:`onSuccess`、`onError` 和 `onSettled`。请注意,如果组件在变更完成前被销毁,这些额外回调将不会执行。 + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 我会第一个触发 + }, + onError: (error, variables, context) => { + // 我会第一个触发 + }, + onSettled: (data, error, variables, context) => { + // 我会第一个触发 + }, +})) + +mutation.mutate(todo, { + onSuccess: (data, variables, context) => { + // 我会第二个触发! + }, + onError: (error, variables, context) => { + // 我会第二个触发! + }, + onSettled: (data, error, variables, context) => { + // 我会第二个触发! + }, +}) +``` + +### 连续变更 + +处理连续变更时,`onSuccess`、`onError` 和 `onSettled` 回调的行为略有不同。当传递给 `mutate` 函数时,它们只会在组件仍处于活动状态时触发一次。这是因为每次调用 `mutate` 函数时,变更观察器都会被移除并重新订阅。相反,`injectMutation` 的处理程序会为每个 `mutate` 调用执行。 + +> 请注意,传递给 `injectMutation` 的 `mutationFn` 很可能是异步的。在这种情况下,变更完成的顺序可能与 `mutate` 函数调用的顺序不同。 + +```ts +export class Example { + mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 会被调用 3 次 + }, + })) + + doMutations() { + ;['Todo 1', 'Todo 2', 'Todo 3'].forEach((todo) => { + this.mutation.mutate(todo, { + onSuccess: (data, variables, context) => { + // 只会执行一次,针对最后一个变更 (Todo 3), + // 无论哪个变更先完成 + }, + }) + }) + } +} +``` + +## Promise + +使用 `mutateAsync` 替代 `mutate` 可以获取一个 Promise,该 Promise 在成功时解析或在出错时抛出异常。例如,这可用于组合副作用。 + +```ts +mutation = injectMutation(() => ({ mutationFn: addTodo })) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('完成') +} +``` + +## 重试 + +默认情况下,TanStack Query 不会在出错时重试变更,但可以通过 `retry` 选项启用: + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + retry: 3, +})) +``` + +如果因设备离线导致变更失败,它们会在设备重新连接时按相同顺序重试。 + +## 持久化变更 + +如果需要,可以将变更持久化到存储中,并在以后恢复。这可以通过水合 (hydration) 函数实现: + +```ts +const queryClient = new QueryClient() + +// 定义 "addTodo" 变更 +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables) => { + // 取消当前待办事项列表的查询 + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 创建乐观待办事项 + const optimisticTodo = { id: uuid(), title: variables.title } + + // 将乐观待办事项添加到列表 + queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // 返回包含乐观待办事项的上下文 + return { optimisticTodo } + }, + onSuccess: (result, variables, context) => { + // 用结果替换列表中的乐观待办事项 + queryClient.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === context.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, context) => { + // 从列表中移除乐观待办事项 + queryClient.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== context.optimisticTodo.id), + ) + }, + retry: 3, +}) + +class someComponent { + // 在组件中启动变更: + mutation = injectMutation(() => ({ mutationKey: ['addTodo'] })) + + someMethod() { + mutation.mutate({ title: '标题' }) + } +} + +// 如果变更因设备离线等原因被暂停, +// 可以在应用退出时将暂停的变更脱水: +const state = dehydrate(queryClient) + +// 然后在应用启动时重新水合: +hydrate(queryClient, state) + +// 恢复暂停的变更: +queryClient.resumePausedMutations() +``` + +### 持久化离线变更 + +如果使用 [persistQueryClient 插件](../plugins/persistQueryClient.md) 持久化离线变更,除非提供默认的变更函数,否则在页面重新加载时无法恢复变更。 + +这是一个技术限制。当持久化到外部存储时,只能持久化变更的状态,因为函数无法被序列化。水合后,触发变更的组件可能尚未初始化,因此调用 `resumePausedMutations` 可能会导致错误:`未找到 mutationFn`。 + +我们还提供了一个全面的[离线示例](../examples/react/offline),涵盖查询和变更。 + +## 变更作用域 + +默认情况下,所有变更并行运行——即使多次调用同一变更的 `.mutate()`。可以通过带有 `id` 的 `scope` 来避免这种情况。具有相同 `scope.id` 的所有变更将串行运行,这意味着当它们被触发时,如果该作用域已有变更在进行中,它们将以 `isPaused: true` 状态启动。它们会被放入队列,并在轮到它们时自动恢复。 + +```tsx +const mutation = injectMutation({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +}) +``` diff --git a/docs/zh-hans/framework/angular/guides/network-mode.md b/docs/zh-hans/framework/angular/guides/network-mode.md new file mode 100644 index 00000000000..6ab614b65e6 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/network-mode.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:54:02.024Z' +id: network-mode +title: Network Mode +ref: docs/zh-hans/framework/react/guides/network-mode.md +--- + diff --git a/docs/zh-hans/framework/angular/guides/optimistic-updates.md b/docs/zh-hans/framework/angular/guides/optimistic-updates.md new file mode 100644 index 00000000000..b24df1b71bd --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/optimistic-updates.md @@ -0,0 +1,172 @@ +--- +source-updated-at: '2024-11-19T18:32:49.000Z' +translation-updated-at: '2025-05-06T05:00:55.440Z' +id: optimistic-updates +title: 乐观更新 +--- +Angular Query 提供了两种在变更操作完成前乐观更新 UI 的方式。您既可以使用 `onMutate` 选项直接更新缓存,也可以利用 `injectMutation` 返回的 `variables` 从结果中更新 UI。 + +## 通过 UI 更新 + +这是更简单的实现方式,因为它不直接与缓存交互。 + +```ts +addTodo = injectMutation(() => ({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + // 确保返回查询失效的 Promise + // 使变更保持 `pending` 状态直到重新获取完成 + onSettled: async () => { + return await queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, +})) +``` + +之后您可以通过 `addTodo.variables` 访问新增的待办事项。在渲染查询的 UI 列表中,当变更处于 `isPending` 状态时,您可以向列表追加一个临时项: + +```angular-ts +@Component({ + template: ` + @for (todo of todos.data(); track todo.id) { +
  • {{ todo.title }}
  • + } + @if (addTodo.isPending()) { +
  • {{ addTodo.variables() }}
  • + } + `, +}) +class TodosComponent {} +``` + +我们通过不同的 `opacity` 样式渲染临时项,直到变更完成。成功后该项会自动消失。如果重新获取成功,该项会作为"正常条目"出现在列表中。 + +若变更出错,该项同样会消失。但如果我们希望保留显示,可以通过检查变更的 `isError` 状态实现。出错时 `variables` 不会被清除,因此我们仍可访问它们,甚至可以显示重试按钮: + +```angular-ts +@Component({ + template: ` + @if (addTodo.isError()) { +
  • + {{ addTodo.variables() }} + +
  • + } + `, +}) +class TodosComponent {} +``` + +### 当变更与查询不在同一组件时 + +这种方式在变更与查询同处一个组件时效果最佳。但您也可以通过专用的 `injectMutationState` 函数访问其他组件中的所有变更,建议配合 `mutationKey` 使用: + +```ts +// 在应用某处 +addTodo = injectMutation(() => ({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + mutationKey: ['addTodo'], +})) + +// 在其他位置访问变量 +mutationState = injectMutationState(() => ({ + filters: { mutationKey: ['addTodo'], status: 'pending' }, + select: (mutation) => mutation.state.variables, +})) +``` + +由于可能同时存在多个变更,`variables` 会是一个数组。如果需要唯一键,我们还可以选择 `mutation.state.submittedAt`,这将使并发乐观更新变得轻而易举。 + +## 通过缓存更新 + +在执行变更前乐观更新状态时,存在操作失败的可能性。多数情况下只需触发乐观查询的重新获取即可恢复至真实服务端状态。但某些情况下重新获取可能失效,此时您可以选择回滚更新。 + +通过 `injectMutation` 的 `onMutate` 处理程序,您可以返回一个值,该值将作为最后一个参数传递给 `onError` 和 `onSettled` 处理程序。通常传递回滚函数最为实用。 + +### 添加新待办事项时更新列表 + +```ts +queryClient = inject(QueryClient) + +updateTodo = injectMutation(() => ({ + mutationFn: updateTodo, + // 当调用 mutate 时: + onMutate: async (newTodo) => { + // 取消所有待处理的查询 + // (防止覆盖我们的乐观更新) + await this.queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 保存当前值的快照 + const previousTodos = client.getQueryData(['todos']) + + // 乐观更新为新值 + this.queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) + + // 返回包含快照值的上下文对象 + return { previousTodos } + }, + // 如果变更失败 + // 使用 onMutate 返回的上下文进行回滚 + onError: (err, newTodo, context) => { + client.setQueryData(['todos'], context.previousTodos) + }, + // 无论成功失败都重新获取: + onSettled: () => { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, +})) +``` + +### 更新单个待办事项 + +```ts +queryClient = inject(QueryClient) + +updateTodo = injectMutation(() => ({ + mutationFn: updateTodo, + // 当调用 mutate 时: + onMutate: async (newTodo) => { + // 取消相关查询 + await this.queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] }) + + // 保存当前值的快照 + const previousTodo = this.queryClient.getQueryData(['todos', newTodo.id]) + + // 乐观更新 + this.queryClient.setQueryData(['todos', newTodo.id], newTodo) + + // 返回包含新旧值的上下文 + return { previousTodo, newTodo } + }, + // 出错时使用上文返回的上下文 + onError: (err, newTodo, context) => { + this.queryClient.setQueryData( + ['todos', context.newTodo.id], + context.previousTodo, + ) + }, + // 总是重新获取: + onSettled: (newTodo) => { + this.queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] }) + }, +})) +``` + +您也可以用 `onSettled` 替代单独的 `onError` 和 `onSuccess` 处理程序: + +```ts +injectMutation({ + mutationFn: updateTodo, + // ... + onSettled: (newTodo, error, variables, context) => { + if (error) { + // 错误处理 + } + }, +}) +``` + +## 方案选择建议 + +如果只需在单一位置显示乐观结果,使用 `variables` 直接更新 UI 的方式代码更少且更易维护。例如您完全不需要处理回滚逻辑。 + +但如果您需要在屏幕上多个位置同步更新状态,直接操作缓存的方式会自动为您处理这些关联更新。 diff --git a/docs/zh-hans/framework/angular/guides/paginated-queries.md b/docs/zh-hans/framework/angular/guides/paginated-queries.md new file mode 100644 index 00000000000..35ba43f3315 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/paginated-queries.md @@ -0,0 +1,106 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-06T04:59:52.176Z' +id: paginated-queries +title: 分页查询 +--- +分页渲染数据是一种非常常见的 UI 模式,在 TanStack Query 中,只需将页码信息包含在查询键 (query key) 中即可实现: + +```ts +const result = injectQuery(() => ({ + queryKey: ['projects', page()], + queryFn: fetchProjects, +})) +``` + +但运行这个简单示例时,你可能会注意到一个奇怪的现象: + +**UI 会在 `success` 和 `pending` 状态之间反复切换,因为每个新页面都被视为一个全新的查询。** + +这种体验并不理想,遗憾的是许多工具至今仍坚持这种工作方式。但 TanStack Query 不同!你可能已经猜到,TanStack Query 提供了一个名为 `placeholderData` 的强大功能来解决这个问题。 + +## 使用 `placeholderData` 优化分页查询 + +考虑以下场景:我们希望逐步增加查询的页码索引 (pageIndex) 或游标 (cursor)。如果使用 `injectQuery`,**技术上虽然可行**,但 UI 会随着每个页面创建和销毁不同查询而在 `success` 和 `pending` 状态间跳转。通过将 `placeholderData` 设为 `(previousData) => previousData` 或使用 TanStack Query 导出的 `keepPreviousData` 函数,我们可以获得以下特性: + +- **即使查询键已变更,在请求新数据期间仍能访问上次成功获取的数据** +- 当新数据到达时,会无缝切换显示新数据 +- 可通过 `isPlaceholderData` 判断当前查询提供的数据类型 + +```angular-ts +@Component({ + selector: 'pagination-example', + template: ` +
    +

    + 本示例中,当获取下一页时,当前页数据仍保持可见。下一页按钮和相关功能会在获取到下一页游标前禁用。每页数据都会作为普通查询缓存,因此返回上一页时会立即显示,同时后台会自动重新获取最新数据。 +

    + @if (query.status() === 'pending') { +
    加载中...
    + } @else if (query.status() === 'error') { +
    错误: {{ query.error().message }}
    + } @else { + + +
    + @for (project of query.data().projects; track project.id) { +

    {{ project.name }}

    + } +
    + } + +
    当前页: {{ page() + 1 }}
    + + + + + + @if (query.isFetching()) { + 加载中... + } +
    + `, +}) +export class PaginationExampleComponent { + page = signal(0) + queryClient = inject(QueryClient) + + query = injectQuery(() => ({ + queryKey: ['projects', this.page()], + queryFn: () => lastValueFrom(fetchProjects(this.page())), + placeholderData: keepPreviousData, + staleTime: 5000, + })) + + constructor() { + effect(() => { + // 预取下一页! + if (!this.query.isPlaceholderData() && this.query.data()?.hasMore) { + this.#queryClient.prefetchQuery({ + queryKey: ['projects', this.page() + 1], + queryFn: () => lastValueFrom(fetchProjects(this.page() + 1)), + }) + } + }) + } + + previousPage() { + this.page.update((old) => Math.max(old - 1, 0)) + } + + nextPage() { + this.page.update((old) => (this.query.data()?.hasMore ? old + 1 : old)) + } +} +``` + +## 使用 `placeholderData` 实现无限查询结果延迟加载 + +虽然不太常见,但 `placeholderData` 选项与 `injectInfiniteQuery` 函数也能完美配合,让你可以在无限查询键随时间变化时,仍让用户无缝查看缓存数据。 diff --git a/docs/zh-hans/framework/angular/guides/parallel-queries.md b/docs/zh-hans/framework/angular/guides/parallel-queries.md new file mode 100644 index 00000000000..0a575aa3fc8 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/parallel-queries.md @@ -0,0 +1,45 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:59:05.811Z' +id: parallel-queries +title: 并行查询 +--- +"并行 (Parallel)" 查询是指同时执行多个查询,以最大化数据获取的并发性。 + +## 手动并行查询 + +当并行查询的数量固定时,使用并行查询**无需额外操作**。只需并排使用任意数量的 TanStack Query 提供的 `injectQuery` 和 `injectInfiniteQuery` 函数即可! + +```ts +export class AppComponent { + // 以下查询将并行执行 + usersQuery = injectQuery(() => ({ queryKey: ['users'], queryFn: fetchUsers })) + teamsQuery = injectQuery(() => ({ queryKey: ['teams'], queryFn: fetchTeams })) + projectsQuery = injectQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + })) +} +``` + +## 使用 `injectQueries` 实现动态并行查询 + +TanStack Query 提供了 `injectQueries` 方法,可用于动态执行任意数量的并行查询。 + +`injectQueries` 接收一个包含 **queries 键**的**配置对象**,该键的值是**查询对象数组**。该方法会返回一个**查询结果数组**: + +```ts +export class AppComponent { + users = signal>([]) + + // 请注意 injectQueries 正在开发中,以下代码暂不可用 + userQueries = injectQueries(() => ({ + queries: users().map((user) => { + return { + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + } + }), + })) +} +``` diff --git a/docs/zh-hans/framework/angular/guides/placeholder-query-data.md b/docs/zh-hans/framework/angular/guides/placeholder-query-data.md new file mode 100644 index 00000000000..aa0464491e0 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/placeholder-query-data.md @@ -0,0 +1,74 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-06T04:58:48.269Z' +id: placeholder-query-data +title: 占位查询数据 +--- +## 什么是占位数据? + +占位数据允许查询表现得好像已经拥有数据,类似于 `initialData` 选项,但**这些数据不会被持久化到缓存中**。这在以下场景中非常有用:当实际数据还在后台获取时,你已经拥有足够的局部(或模拟)数据来成功渲染查询。 + +> 示例:一篇博客文章的查询可以从父级博客列表拉取仅包含标题和文章片段缩略的"预览"数据。你可能不希望将这些局部数据持久化到独立查询的结果中,但它能帮助在完整数据获取完成前尽可能快地展示内容布局。 + +在需要实际数据前,有几种方式可以为查询提供占位数据到缓存: + +- 声明式: + - 为查询提供 `placeholderData` 以便在缓存为空时预填充 +- 命令式: + - [使用 `queryClient` 和 `placeholderData` 选项预取或获取数据](./prefetching.md) + +当我们使用 `placeholderData` 时,查询不会处于 `pending` 状态——它会直接从 `success` 状态开始,因为已有可展示的 `data`(即使只是"占位"数据)。为了与"真实"数据区分,查询结果中还会将 `isPlaceholderData` 标志设为 `true`。 + +## 作为值的占位数据 + +```ts +class TodosComponent { + result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData: placeholderTodos, + })) +} +``` + +## 作为函数的占位数据 + +`placeholderData` 也可以是一个函数,通过它你能访问"先前"成功查询的数据和元信息。这在需要将一个查询的数据作为另一个查询的占位数据时非常实用。当 QueryKey 变化时(例如从 `['todos', 1]` 变为 `['todos', 2]`),我们可以继续显示"旧"数据,避免在数据从一个查询过渡到下一个时展示加载动画。更多信息请参阅[分页查询](./paginated-queries.md)。 + +```ts +class TodosComponent { + result = injectQuery(() => ({ + queryKey: ['todos', id()], + queryFn: () => fetch(`/todos/${id}`), + placeholderData: (previousData, previousQuery) => previousData, + })) +} +``` + +### 从缓存获取占位数据 + +某些情况下,你可以从其他查询的缓存结果中提供占位数据。一个典型场景是:从博客文章列表查询的缓存数据中搜索文章的预览版本,然后将其用作独立文章查询的占位数据: + +```ts +export class BlogPostComponent { + // 在 Angular 支持基于信号的输入前,我们需要手动设置信号 + @Input({ required: true, alias: 'postId' }) + set _postId(value: number) { + this.postId.set(value) + } + postId = signal(0) + queryClient = inject(QueryClient) + + result = injectQuery(() => ({ + queryKey: ['blogPost', this.postId()], + queryFn: () => fetch(`/blogPosts/${this.postId()}`), + placeholderData: () => { + // 使用 'blogPosts' 查询中的精简/预览版博客文章 + // 作为当前博客文章查询的占位数据 + return queryClient + .getQueryData(['blogPosts']) + ?.find((d) => d.id === this.postId()) + }, + })) +} +``` diff --git a/docs/zh-hans/framework/angular/guides/queries.md b/docs/zh-hans/framework/angular/guides/queries.md new file mode 100644 index 00000000000..8f60f0c28b9 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/queries.md @@ -0,0 +1,125 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-06T04:58:12.365Z' +id: queries +title: 查询 +--- +## 查询基础 + +查询是与**唯一键**绑定的、对异步数据源的声明式依赖。查询可用于任何基于 Promise 的方法(包括 GET 和 POST 方法)从服务器获取数据。如果您的方法会修改服务器上的数据,建议改用[变更](./mutations.md)。 + +要在组件或服务中订阅查询,调用 `injectQuery` 时至少需要提供: + +- **查询的唯一键** +- 返回 Promise 或 Observable 的函数,该函数应: + - 解析数据,或 + - 抛出错误 + +```ts +import { injectQuery } from '@tanstack/angular-query-experimental' + +export class TodosComponent { + info = injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodoList })) +} +``` + +您提供的**唯一键**将在内部用于重新获取、缓存和在应用程序中共享查询。 + +`injectQuery` 返回的查询结果包含您需要用于模板渲染或任何其他数据操作的所有查询信息: + +```ts +result = injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodoList })) +``` + +`result` 对象包含几个非常重要的状态,您需要了解这些状态才能高效使用。查询在任何时刻只能处于以下状态之一: + +- `isPending` 或 `status === 'pending'` - 查询尚无数据 +- `isError` 或 `status === 'error'` - 查询遇到错误 +- `isSuccess` 或 `status === 'success'` - 查询成功且数据可用 + +除了这些主要状态外,根据查询状态还可获取更多信息: + +- `error` - 如果查询处于 `isError` 状态,可通过 `error` 属性获取错误信息。 +- `data` - 如果查询处于 `isSuccess` 状态,可通过 `data` 属性获取数据。 +- `isFetching` - 在任何状态下,如果查询正在获取数据(包括后台重新获取),`isFetching` 将为 `true`。 + +对于**大多数**查询,通常只需检查 `isPending` 状态,然后是 `isError` 状态,最后可以假设数据可用并渲染成功状态: + +```angular-ts +@Component({ + selector: 'todos', + standalone: true, + template: ` + @if (todos.isPending()) { + Loading... + } @else if (todos.isError()) { + Error: {{ todos.error()?.message }} + } @else { + + @for (todo of todos.data(); track todo.id) { +
  • {{ todo.title }}
  • + } @empty { +
  • No todos found
  • + } + } + `, +}) +export class PostsComponent { + todos = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + })) +} +``` + +如果不喜欢布尔值,也可以始终使用 `status` 状态: + +```angular-ts +@Component({ + selector: 'todos', + standalone: true, + template: ` + @switch (todos.status()) { + @case ('pending') { + Loading... + } + @case ('error') { + Error: {{ todos.error()?.message }} + } + + @default { +
      + @for (todo of todos.data(); track todo.id) { +
    • {{ todo.title }}
    • + } @empty { +
    • No todos found
    • + } +
    + } + } + `, +}) +class TodosComponent {} +``` + +如果您在访问 `data` 之前检查了 `pending` 和 `error`,TypeScript 也会正确缩小 `data` 的类型范围。 + +### 获取状态 (FetchStatus) + +除了 `status` 字段外,您还会获得一个额外的 `fetchStatus` 属性,其可选值为: + +- `fetchStatus === 'fetching'` - 查询正在获取数据。 +- `fetchStatus === 'paused'` - 查询希望获取数据,但被暂停。更多信息请参阅[网络模式](./network-mode.md)指南。 +- `fetchStatus === 'idle'` - 查询当前未进行任何操作。 + +### 为什么有两种不同的状态? + +后台重新获取和“陈旧数据优先重新验证”逻辑使得 `status` 和 `fetchStatus` 的所有组合都可能出现。例如: + +- 处于 `success` 状态的查询通常处于 `idle` 获取状态,但如果正在进行后台重新获取,也可能处于 `fetching` 状态。 +- 刚挂载且没有数据的查询通常处于 `pending` 状态和 `fetching` 获取状态,但如果无网络连接,也可能处于 `paused` 状态。 + +因此请记住,查询可能处于 `pending` 状态而实际上并未获取数据。作为经验法则: + +- `status` 提供关于 `data` 的信息:我们是否有数据? +- `fetchStatus` 提供关于 `queryFn` 的信息:它是否正在运行? diff --git a/docs/zh-hans/framework/angular/guides/query-cancellation.md b/docs/zh-hans/framework/angular/guides/query-cancellation.md new file mode 100644 index 00000000000..6229deb838d --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/query-cancellation.md @@ -0,0 +1,107 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-06T04:57:20.291Z' +id: query-cancellation +title: 查询取消 +--- +TanStack Query 为每个查询函数提供了一个 [`AbortSignal` 实例](https://developer.mozilla.org/docs/Web/API/AbortSignal)。当查询过期或变为非活跃状态时,该 `signal` 会被中止。这意味着所有查询均可取消,并且您可以根据需要在查询函数内部响应取消操作。最棒的是,它允许您继续使用普通的 async/await 语法,同时获得自动取消的所有优势。 + +## 默认行为 + +默认情况下,在 Promise 解析之前卸载或不再使用的查询_不会_被取消。这意味着 Promise 解析后,结果数据仍会保留在缓存中。这对于已经开始接收查询但随后在完成前卸载组件的情况非常有用。如果您再次挂载组件且查询尚未被垃圾回收,数据将仍然可用。 + +然而,如果您使用了 `AbortSignal`,Promise 将被取消(例如中止 fetch 请求),因此查询也必须被取消。取消查询将导致其状态_回退_到之前的状态。 + +## 使用 `HttpClient` + +```ts +import { HttpClient } from '@angular/common/http' +import { injectQuery } from '@tanstack/angular-query-experimental' + +postQuery = injectQuery(() => ({ + enabled: this.postId() > 0, + queryKey: ['post', this.postId()], + queryFn: async (context): Promise => { + const abort$ = fromEvent(context.signal, 'abort') + return lastValueFrom(this.getPost$(this.postId()).pipe(takeUntil(abort$)) + }, +})) +``` + +## 使用 `fetch` + +[//]: # 'Example2' + +```ts +query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const todosResponse = await fetch('/todos', { + // 将 signal 传递给 fetch + signal, + }) + const todos = await todosResponse.json() + + const todoDetails = todos.map(async ({ details }) => { + const response = await fetch(details, { + // 或者传递给多个请求 + signal, + }) + return response.json() + }) + + return Promise.all(todoDetails) + }, +})) +``` + +[//]: # 'Example2' + +## 使用 `axios` + +[//]: # 'Example3' + +```tsx +import axios from 'axios' + +const query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => + axios.get('/todos', { + // 将 signal 传递给 `axios` + signal, + }), +})) +``` + +[//]: # 'Example3' + +## 手动取消 + +您可能需要手动取消查询。例如,如果请求需要很长时间才能完成,您可以允许用户点击取消按钮来停止请求。为此,只需调用 `queryClient.cancelQueries({ queryKey })`,这将取消查询并将其状态回退到之前的状态。如果您已经使用了传递给查询函数的 `signal`,TanStack Query 还会额外取消 Promise。 + +[//]: # 'Example7' + +```angular-ts +@Component({ + standalone: true, + template: ``, +}) +export class TodosComponent { + query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, + })) + + queryClient = inject(QueryClient) + + onCancel() { + this.queryClient.cancelQueries(['todos']) + } +} +``` + +[//]: # 'Example7' diff --git a/docs/zh-hans/framework/angular/guides/query-functions.md b/docs/zh-hans/framework/angular/guides/query-functions.md new file mode 100644 index 00000000000..48a8c26d012 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/query-functions.md @@ -0,0 +1,102 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:56:44.357Z' +id: query-functions +title: 查询函数 +--- +# 查询函数 (Query Functions) + +查询函数可以是**任何返回 Promise 的函数**。返回的 Promise 应当**解析数据 (resolve the data)** 或**抛出错误 (throw an error)**。 + +以下是所有有效的查询函数配置示例: + +```ts +injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchAllTodos })) +injectQuery(() => ({ queryKey: ['todos', todoId], queryFn: () => fetchTodoById(todoId) }) +injectQuery(() => ({ + queryKey: ['todos', todoId], + queryFn: async () => { + const data = await fetchTodoById(todoId) + return data + }, +})) +injectQuery(() => ({ + queryKey: ['todos', todoId], + queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]), +})) +``` + +## 错误处理与抛出 (Handling and Throwing Errors) + +为了让 TanStack Query 判定查询失败,查询函数**必须抛出错误**或返回一个**被拒绝的 Promise (rejected Promise)**。查询函数中抛出的任何错误都会被持久化到查询的 `error` 状态中。 + +```ts +todos = injectQuery(() => ({ + queryKey: ['todos', todoId()], + queryFn: async () => { + if (somethingGoesWrong) { + throw new Error('Oh no!') + } + if (somethingElseGoesWrong) { + return Promise.reject(new Error('Oh no!')) + } + + return data + }, +})) +``` + +## 与 `fetch` 等默认不抛出错误的客户端一起使用 (Usage with `fetch` and other clients that do not throw by default) + +虽然大多数工具如 `axios` 或 `graphql-request` 会自动为失败的 HTTP 调用抛出错误,但像 `fetch` 这样的工具默认不会抛出错误。这种情况下,您需要手动抛出错误。以下是使用流行的 `fetch` API 实现这一点的简单方法: + +```ts +todos = injectQuery(() => ({ + queryKey: ['todos', todoId()], + queryFn: async () => { + const response = await fetch('/todos/' + todoId) + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() + }, +})) +``` + +## 查询函数变量 (Query Function Variables) + +查询键 (Query keys) 不仅用于唯一标识您要获取的数据,还会作为 QueryFunctionContext 的一部分方便地传递到您的查询函数中。虽然并不总是必要,但这使得在需要时可以提取您的查询函数: + +```ts +result = injectQuery(() => ({ + queryKey: ['todos', { status: status(), page: page() }], + queryFn: fetchTodoList, +})) + +// 在查询函数中访问 key、status 和 page 变量! +function fetchTodoList({ queryKey }) { + const [_key, { status, page }] = queryKey + return new Promise() +} +``` + +### 查询函数上下文 (QueryFunctionContext) + +`QueryFunctionContext` 是传递给每个查询函数的对象,包含以下属性: + +- `queryKey: QueryKey`: [查询键 (Query Keys)](./query-keys.md) +- `client: QueryClient`: [查询客户端 (QueryClient)](../../../reference/QueryClient.md) +- `signal?: AbortSignal` + - 由 TanStack Query 提供的 [中止信号 (AbortSignal)](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) 实例 + - 可用于 [查询取消 (Query Cancellation)](./query-cancellation.md) +- `meta: Record | undefined` + - 可选字段,可填充有关查询的附加信息 + +此外,[无限查询 (Infinite Queries)](./infinite-queries.md) 还会获得以下选项: + +- `pageParam: TPageParam` + - 用于获取当前页面的页面参数 +- `direction: 'forward' | 'backward'` + - **已弃用** + - 当前页面获取的方向 + - 要获取当前页面获取的方向,请从 `getNextPageParam` 和 `getPreviousPageParam` 向 `pageParam` 添加方向参数 diff --git a/docs/zh-hans/framework/angular/guides/query-invalidation.md b/docs/zh-hans/framework/angular/guides/query-invalidation.md new file mode 100644 index 00000000000..03c9da37184 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/query-invalidation.md @@ -0,0 +1,117 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-06T04:53:07.857Z' +id: query-invalidation +title: 查询失效 +--- +等待查询自动变陈旧(stale)后再重新获取并不总是适用,尤其是当用户操作明确导致某查询数据过期时。为此,`QueryClient` 提供了 `invalidateQueries` 方法,允许您智能地标记查询为陈旧状态,并可能触发重新获取! + +```tsx +// 使缓存中的所有查询失效 +queryClient.invalidateQueries() +// 使所有以 `todos` 开头的查询键的查询失效 +queryClient.invalidateQueries({ queryKey: ['todos'] }) +``` + +> 注意:其他使用规范化缓存(normalized caches)的库会尝试通过命令式或模式推断来更新本地查询,而 TanStack Query 提供了工具来避免维护规范化缓存的手动操作,转而采用**定向失效(targeted invalidation)、后台重新获取(background-refetching)以及最终的原子更新(atomic updates)**。 + +当使用 `invalidateQueries` 使查询失效时,会发生两件事: + +- 该查询被标记为陈旧(stale)。此状态会覆盖 `injectQuery` 或相关函数中配置的任何 `staleTime` 值 +- 如果该查询当前正通过 `injectQuery` 或相关函数渲染,还会在后台触发重新获取 + +## 使用 `invalidateQueries` 进行查询匹配 + +在使用 `invalidateQueries` 和 `removeQueries` 等支持部分查询匹配的 API 时,您可以通过前缀匹配多个查询,或精确匹配特定查询。关于可用的筛选器类型,请参阅[查询筛选器](./filters.md#query-filters)。 + +以下示例中,我们使用 `todos` 前缀来使所有查询键以 `todos` 开头的查询失效: + +```ts +import { injectQuery, QueryClient } from '@tanstack/angular-query-experimental' + +class QueryInvalidationExample { + queryClient = inject(QueryClient) + + invalidateQueries() { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + } + + // 以下两个查询都将失效 + todoListQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + })) + todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { page: 1 }], + queryFn: fetchTodoList, + })) +} +``` + +您还可以通过传递更具体的查询键,使带特定变量的查询失效: + +```ts +queryClient.invalidateQueries({ + queryKey: ['todos', { type: 'done' }], +}) + +// 以下查询将失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +})) + +// 但以下查询不会失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, +})) +``` + +`invalidateQueries` API 非常灵活。如果您**只想**使不包含额外变量或子键的 `todos` 查询失效,可以传递 `exact: true` 选项: + +```ts +queryClient.invalidateQueries({ + queryKey: ['todos'], + exact: true, +}) + +// 以下查询将失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, +})) + +// 但以下查询不会失效 +const todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +})) +``` + +如果需要**更精细**的控制,可以向 `invalidateQueries` 方法传递一个断言函数。该函数会接收查询缓存中的每个 `Query` 实例,并让您通过返回 `true` 或 `false` 来决定是否使其失效: + +```ts +queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10, +}) + +// 以下查询将失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { version: 20 }], + queryFn: fetchTodoList, +})) + +// 以下查询将失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { version: 10 }], + queryFn: fetchTodoList, +})) + +// 但以下查询不会失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { version: 5 }], + queryFn: fetchTodoList, +})) +``` diff --git a/docs/zh-hans/framework/angular/guides/query-keys.md b/docs/zh-hans/framework/angular/guides/query-keys.md new file mode 100644 index 00000000000..929e2f161b6 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/query-keys.md @@ -0,0 +1,76 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:54:01.961Z' +id: query-keys +title: 查询键 +--- +TanStack Query 的核心是基于查询键 (query keys) 为您管理查询缓存。查询键在顶层必须是一个数组,可以简单到仅包含单个字符串的数组,也可以复杂到包含多个字符串和嵌套对象的数组。只要查询键是可序列化的,并且**能唯一标识查询的数据**,您就可以使用它! + +## 简单查询键 + +最简单的键形式是由常量值组成的数组。这种格式适用于: + +- 通用列表/索引资源 +- 非层级化资源 + +```ts +// 待办事项列表 +injectQuery(() => ({ queryKey: ['todos'], ... })) + +// 其他任意内容! +injectQuery(() => ({ queryKey: ['something', 'special'], ... })) +``` + +## 带变量的数组键 + +当查询需要更多信息来唯一描述其数据时,您可以使用包含字符串和任意数量可序列化对象的数组。这种形式适用于: + +- 层级化或嵌套资源 + - 通常会传递 ID、索引或其他原始值来唯一标识项 +- 带附加参数的查询 + - 通常会传递包含附加选项的对象 + +```ts +// 单个待办事项 +injectQuery(() => ({queryKey: ['todo', 5], ...})) + +// "预览"格式的单个待办事项 +injectQuery(() => ({queryKey: ['todo', 5, {preview: true}], ...})) + +// 已完成待办事项列表 +injectQuery(() => ({queryKey: ['todos', {type: 'done'}], ...})) +``` + +## 查询键会被确定性哈希! + +这意味着无论对象中键的顺序如何,以下所有查询都被视为等同: + +```ts +injectQuery(() => ({ queryKey: ['todos', { status, page }], ... })) +injectQuery(() => ({ queryKey: ['todos', { page, status }], ...})) +injectQuery(() => ({ queryKey: ['todos', { page, status, other: undefined }], ... })) +``` + +但以下查询键并不等同。数组项的顺序很重要! + +```ts +injectQuery(() => ({ queryKey: ['todos', status, page], ... })) +injectQuery(() => ({ queryKey: ['todos', page, status], ...})) +injectQuery(() => ({ queryKey: ['todos', undefined, page, status], ...})) +``` + +## 如果查询函数依赖变量,请将其包含在查询键中 + +由于查询键唯一描述了它们获取的数据,因此应包含查询函数中使用的任何**会变化**的变量。例如: + +```ts +todoId = signal(-1) + +injectQuery(() => ({ + enabled: todoId() > 0, + queryKey: ['todos', todoId()], + queryFn: () => fetchTodoById(todoId()), +})) +``` + +请注意,查询键充当查询函数的依赖项。将依赖变量添加到查询键可确保查询被独立缓存,并且每当变量变化时,_查询会自动重新获取_(取决于您的 `staleTime` 设置)。更多信息和示例请参阅 [exhaustive-deps](../../../eslint/exhaustive-deps.md) 部分。 diff --git a/docs/zh-hans/framework/angular/guides/query-options.md b/docs/zh-hans/framework/angular/guides/query-options.md new file mode 100644 index 00000000000..729c5276cf8 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/query-options.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2025-02-02T18:26:57.000Z' +translation-updated-at: '2025-05-06T04:53:28.456Z' +id: query-options +title: 查询选项 +--- +在多个地方共享 `queryKey` 和 `queryFn` 同时保持它们彼此关联的最佳方式之一是使用 `queryOptions` 辅助函数。在运行时,该辅助函数仅返回传入的内容,但[结合 TypeScript 使用时](../typescript.md#typing-query-options)能提供诸多优势。您可以在一个地方定义查询的所有可能选项,并同时获得类型推断和类型安全。 + +```ts +import { queryOptions } from '@tanstack/angular-query-experimental' + +@Injectable({ + providedIn: 'root', +}) +export class QueriesService { + private http = inject(HttpClient) + + post(postId: number) { + return queryOptions({ + queryKey: ['post', postId], + queryFn: () => { + return lastValueFrom( + this.http.get( + `https://jsonplaceholder.typicode.com/posts/${postId}`, + ), + ) + }, + }) + } +} + +// 使用示例: + +postId = input.required({ + transform: numberAttribute, +}) +queries = inject(QueriesService) + +postQuery = injectQuery(() => this.queries.post(this.postId())) + +queryClient.prefetchQuery(this.queries.post(23)) +queryClient.setQueryData(this.queries.post(42).queryKey, newPost) +``` + +对于无限查询 (Infinite Queries),另有专用的 [`infiniteQueryOptions`](../reference/infiniteQueryOptions.md) 辅助函数可用。 + +您仍可在组件级别覆盖某些选项。一个非常常见且实用的模式是为每个组件创建 [`select`](./render-optimizations.md#select) 函数: + +```ts +// 类型推断仍然有效,因此 query.data 将是 select 的返回类型而非 queryFn 的返回类型 +queries = inject(QueriesService) + +query = injectQuery(() => ({ + ...groupOptions(1), + select: (data) => data.title, +})) +``` diff --git a/docs/zh-hans/framework/angular/guides/query-retries.md b/docs/zh-hans/framework/angular/guides/query-retries.md new file mode 100644 index 00000000000..0867f36b939 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/query-retries.md @@ -0,0 +1,64 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-06T04:56:03.413Z' +id: query-retries +title: 查询重试 +--- +当 `injectQuery` 查询失败时(查询函数抛出错误),若该查询请求未达到最大连续重试次数(默认为 `3`)或提供了判断是否允许重试的函数,TanStack Query 将自动重试该查询。 + +您可以在全局级别和单个查询级别配置重试行为: + +- 设置 `retry = false` 将禁用重试 +- 设置 `retry = 6` 将在显示函数抛出的最终错误前重试失败请求 6 次 +- 设置 `retry = true` 将无限重试失败请求 +- 设置 `retry = (failureCount, error) => ...` 允许根据失败原因自定义逻辑 + +```ts +import { injectQuery } from '@tanstack/angular-query-experimental' + +// 使特定查询重试指定次数 +const result = injectQuery(() => ({ + queryKey: ['todos', 1], + queryFn: fetchTodoListPage, + retry: 10, // 将在显示错误前重试失败请求 10 次 +})) +``` + +> 提示:在最后一次重试尝试前,`error` 属性的内容将作为 `failureReason` 响应属性的一部分存在于 `injectQuery` 中。因此在上例中,前 9 次重试尝试(共 10 次)的任何错误内容都将属于 `failureReason` 属性,若所有重试后错误仍存在,最终它们会在最后一次尝试后成为 `error` 的一部分。 + +## 重试延迟 + +默认情况下,TanStack Query 的重试不会在请求失败后立即执行。按照标准做法,每次重试尝试会逐渐增加退避延迟时间。 + +默认 `retryDelay` 设置为每次尝试翻倍(从 `1000` 毫秒开始),但不超过 30 秒: + +```ts +// 为所有查询配置 +import { + QueryCache, + QueryClient, + QueryClientProvider, +} from '@tanstack/angular-query-experimental' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, + }, +}) + +bootstrapApplication(AppComponent, { + providers: [provideTanStackQuery(queryClient)], +}) +``` + +虽然不推荐,但您显然可以在插件和单个查询选项中覆盖 `retryDelay` 函数/整数值。如果设置为整数而非函数,延迟时间将始终保持不变: + +```ts +const result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + retryDelay: 1000, // 无论重试多少次,始终等待 1000 毫秒后重试 +})) +``` diff --git a/docs/zh-hans/framework/angular/guides/scroll-restoration.md b/docs/zh-hans/framework/angular/guides/scroll-restoration.md new file mode 100644 index 00000000000..25a9b03b8e1 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/scroll-restoration.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T04:49:46.983Z' +id: scroll-restoration +title: Scroll Restoration +ref: docs/zh-hans/framework/react/guides/scroll-restoration.md +--- + diff --git a/docs/zh-hans/framework/angular/guides/window-focus-refetching.md b/docs/zh-hans/framework/angular/guides/window-focus-refetching.md new file mode 100644 index 00000000000..81832485c82 --- /dev/null +++ b/docs/zh-hans/framework/angular/guides/window-focus-refetching.md @@ -0,0 +1,42 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-06T04:49:46.933Z' +id: window-focus-refetching +title: Window Focus Refetching +ref: docs/zh-hans/framework/react/guides/window-focus-refetching.md +replace: + '@tanstack/react-query': '@tanstack/angular-query-experimental' +--- + +[//]: # 'Example' + +```ts +export const appConfig: ApplicationConfig = { + providers: [ + provideTanStackQuery( + new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, // default: true + }, + }, + }), + ), + ], +} +``` + +[//]: # 'Example' +[//]: # 'Example2' + +```ts +injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + refetchOnWindowFocus: false, +})) +``` + +[//]: # 'Example2' +[//]: # 'ReactNative' +[//]: # 'ReactNative' diff --git a/docs/zh-hans/framework/angular/installation.md b/docs/zh-hans/framework/angular/installation.md new file mode 100644 index 00000000000..c21f97f9b2c --- /dev/null +++ b/docs/zh-hans/framework/angular/installation.md @@ -0,0 +1,35 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-06T04:50:07.866Z' +id: installation +title: 安装 +--- +> 重要提示:当前该库处于实验阶段。这意味着在次要版本和补丁版本中可能会发生破坏性变更。请谨慎升级。如果您在生产环境中使用此实验阶段的库,请将版本锁定到具体的补丁版本以避免意外的破坏性变更。 + +### NPM + +_Angular Query 兼容 Angular v16 及以上版本_ + +```bash +npm i @tanstack/angular-query-experimental +``` + +或 + +```bash +pnpm add @tanstack/angular-query-experimental +``` + +或 + +```bash +yarn add @tanstack/angular-query-experimental +``` + +或 + +```bash +bun add @tanstack/angular-query-experimental +``` + +> 想在下载前试用一下?可以尝试 [简单示例](../examples/simple) 或 [基础示例](../examples/basic)! diff --git a/docs/zh-hans/framework/angular/overview.md b/docs/zh-hans/framework/angular/overview.md new file mode 100644 index 00000000000..45cdd2969fc --- /dev/null +++ b/docs/zh-hans/framework/angular/overview.md @@ -0,0 +1,114 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-06T04:51:31.172Z' +id: overview +title: 概述 +--- +> 重要提示:该库目前处于实验阶段。这意味着在次要版本和补丁版本中可能会出现破坏性变更。升级时请谨慎操作。如果您在生产环境中使用此实验阶段的库,请将版本锁定到具体的补丁版本以避免意外的破坏性变更。 + +`@tanstack/angular-query-experimental` 包为通过 Angular 使用 TanStack Query 提供了一流的 API。 + +## 欢迎反馈! + +我们正在努力为 Angular 上的 TanStack Query 提供稳定的 API。如果您有任何反馈意见,请联系我们的 [TanStack Discord](https://tlinz.com/discord) 服务器或在 GitHub 上[参与此讨论](https://github.com/TanStack/query/discussions/6293)。 + +## 支持的 Angular 版本 + +TanStack Query 兼容 Angular v16 及以上版本。 + +TanStack Query(前身为 React Query)常被描述为 Web 应用程序中缺失的数据获取库,但从技术角度而言,它能让 **获取、缓存、同步和更新服务端状态** 在您的 Web 应用中变得轻而易举。 + +## 动机 + +大多数核心 Web 框架 **并未** 提供一种全面的数据获取或更新方式。因此,开发者最终要么构建封装了严格数据获取理念的元框架,要么发明自己的数据获取方法。这通常意味着拼凑基于组件的状态和副作用,或者使用更通用的状态管理库来存储和提供整个应用中的异步数据。 + +虽然大多数传统状态管理库在处理客户端状态时表现优异,但 **在处理异步或服务端状态时并不理想**。这是因为 **服务端状态完全不同**。首先,服务端状态: + +- 持久化存储在您可能无法控制或拥有的远程位置 +- 需要异步 API 进行获取和更新 +- 意味着共享所有权,可能被他人更改而您不知情 +- 如果不小心处理,可能在您的应用中变得“过时” + +一旦您理解了应用中服务端状态的本质,**更多挑战会随之而来**,例如: + +- 缓存...(可能是编程中最难的事情) +- 将针对相同数据的多个请求去重为单个请求 +- 在后台更新“过时”数据 +- 知道数据何时“过时” +- 尽可能快地反映数据更新 +- 分页和懒加载数据等性能优化 +- 管理服务端状态的内存和垃圾回收 +- 通过结构共享记忆查询结果 + +如果这个列表没有让您感到压力,那一定意味着您已经解决了所有服务端状态问题并值得嘉奖。然而,如果您和大多数人一样,可能尚未应对全部或大部分挑战,而我们才刚刚触及表面! + +TanStack Query 无疑是管理服务端状态的**最佳**库之一。它**开箱即用、零配置**,并且可以随着应用的增长按需定制。 + +TanStack Query 让您能够战胜和克服**服务端状态**的棘手挑战,在数据开始控制您之前掌控应用数据。 + +从技术角度来看,TanStack Query 可能会: + +- 帮助您从应用中移除**大量**复杂且难以理解的代码,仅用寥寥几行 TanStack Query 逻辑替代 +- 提高应用的可维护性,轻松构建新功能而无需担心连接新的服务端状态数据源 +- 通过使应用感觉比以往更快、响应更迅速,直接影响最终用户体验 +- 可能帮助您节省带宽并提升内存性能 + +[//]: # '示例' + +## 说够了,直接看代码吧! + +在下面的示例中,您可以看到 TanStack Query 最基本和简单的形式,用于获取 TanStack Query GitHub 项目本身的统计信息: + +[在 StackBlitz 中打开](https://stackblitz.com/github/TanStack/query/tree/main/examples/angular/simple) + +```angular-ts +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { HttpClient } from '@angular/common/http' +import { CommonModule } from '@angular/common' +import { injectQuery } from '@tanstack/angular-query-experimental' +import { lastValueFrom } from 'rxjs' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'simple-example', + standalone: true, + template: ` + @if (query.isPending()) { + Loading... + } + @if (query.error()) { + An error has occurred: {{ query.error().message }} + } + @if (query.data(); as data) { +

    {{ data.name }}

    +

    {{ data.description }}

    + 👀 {{ data.subscribers_count }} + ✨ {{ data.stargazers_count }} + 🍴 {{ data.forks_count }} + } + ` +}) +export class SimpleExampleComponent { + http = inject(HttpClient) + + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + lastValueFrom( + this.http.get('https://api.github.com/repos/tanstack/query'), + ), + })) +} + +interface Response { + name: string + description: string + subscribers_count: number + stargazers_count: number + forks_count: number +} +``` + +## 您说服了我,接下来该怎么做? + +- 按照自己的节奏学习 TanStack Query,参考我们详尽的[入门指南](../installation)和[API 参考](../reference/functions/injectquery) diff --git a/docs/zh-hans/framework/angular/quick-start.md b/docs/zh-hans/framework/angular/quick-start.md new file mode 100644 index 00000000000..7c9aea7819f --- /dev/null +++ b/docs/zh-hans/framework/angular/quick-start.md @@ -0,0 +1,119 @@ +--- +source-updated-at: '2024-11-19T18:32:49.000Z' +translation-updated-at: '2025-05-03T22:08:27.205Z' +id: quick-start +title: 快速开始 +--- +> 重要提示:当前该库处于实验阶段。这意味着次要版本和补丁版本都可能包含破坏性变更。升级时请谨慎操作。如果在生产环境中使用实验阶段的版本,请锁定到具体的补丁版本以避免意外的破坏性变更。 + +[//]: # '示例' + +如需查看完整功能示例,请参考我们的 [基础 codesandbox 示例](../examples/basic) + +### 为应用提供客户端 + +```ts +import { provideHttpClient } from '@angular/common/http' +import { + provideTanStackQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +bootstrapApplication(AppComponent, { + providers: [provideHttpClient(), provideTanStackQuery(new QueryClient())], +}) +``` + +或在基于 NgModule 的应用中 + +```ts +import { provideHttpClient } from '@angular/common/http' +import { + provideTanStackQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule], + providers: [provideTanStackQuery(new QueryClient())], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +### 包含查询与变更的组件 + +```angular-ts +import { Component, Injectable, inject } from '@angular/core' +import { HttpClient } from '@angular/common/http' +import { lastValueFrom } from 'rxjs' + +import { + injectMutation, + injectQuery, + QueryClient +} from '@tanstack/angular-query-experimental' + +@Component({ + standalone: true, + template: ` +
    + + +
      + @for (todo of query.data(); track todo.title) { +
    • {{ todo.title }}
    • + } +
    +
    + `, +}) +export class TodosComponent { + todoService = inject(TodoService) + queryClient = inject(QueryClient) + + query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => this.todoService.getTodos(), + })) + + mutation = injectMutation(() => ({ + mutationFn: (todo: Todo) => this.todoService.addTodo(todo), + onSuccess: () => { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, + })) + + onAddTodo() { + this.mutation.mutate({ + id: Date.now().toString(), + title: '洗衣服', + }) + } +} + +@Injectable({ providedIn: 'root' }) +export class TodoService { + private http = inject(HttpClient) + + getTodos(): Promise { + return lastValueFrom( + this.http.get('https://jsonplaceholder.typicode.com/todos'), + ) + } + + addTodo(todo: Todo): Promise { + return lastValueFrom( + this.http.post('https://jsonplaceholder.typicode.com/todos', todo), + ) + } +} + +interface Todo { + id: string + title: string +} +``` + +[//]: # '示例' diff --git a/docs/zh-hans/framework/angular/reference/functions/infinitequeryoptions.md b/docs/zh-hans/framework/angular/reference/functions/infinitequeryoptions.md new file mode 100644 index 00000000000..405c454fbcd --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/infinitequeryoptions.md @@ -0,0 +1,134 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.816Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +--- + +# Function: infiniteQueryOptions() + +Allows to share and re-use infinite query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +## Param + +The infinite query options to tag with the type from `queryFn`. + +## infiniteQueryOptions(options) + +```ts +function infiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>( + options, +): UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + object +``` + +Allows to share and re-use infinite query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **options**: [`UndefinedInitialDataInfiniteOptions`](../type-aliases/undefinedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> + +The infinite query options to tag with the type from `queryFn`. + +### Returns + +[`UndefinedInitialDataInfiniteOptions`](../type-aliases/undefinedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` + +The tagged infinite query options. + +The tagged infinite query options. + +### Param + +The infinite query options to tag with the type from `queryFn`. + +### Defined in + +[infinite-query-options.ts:59](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L59) + +## infiniteQueryOptions(options) + +```ts +function infiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>( + options, +): DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + object +``` + +Allows to share and re-use infinite query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **options**: [`DefinedInitialDataInfiniteOptions`](../type-aliases/definedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> + +The infinite query options to tag with the type from `queryFn`. + +### Returns + +[`DefinedInitialDataInfiniteOptions`](../type-aliases/definedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` + +The tagged infinite query options. + +The tagged infinite query options. + +### Param + +The infinite query options to tag with the type from `queryFn`. + +### Defined in + +[infinite-query-options.ts:91](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L91) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectinfinitequery.md b/docs/zh-hans/framework/angular/reference/functions/injectinfinitequery.md new file mode 100644 index 00000000000..7cee4c63ee0 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectinfinitequery.md @@ -0,0 +1,190 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.757Z' +id: injectInfiniteQuery +title: injectInfiniteQuery +--- + +# Function: injectInfiniteQuery() + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +## Param + +A function that returns infinite query options. + +## Param + +The Angular injector to use. + +## injectInfiniteQuery(optionsFn, injector) + +```ts +function injectInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(optionsFn, injector?): CreateInfiniteQueryResult +``` + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **optionsFn** + +A function that returns infinite query options. + +• **injector?**: `Injector` + +The Angular injector to use. + +### Returns + +[`CreateInfiniteQueryResult`](../type-aliases/createinfinitequeryresult.md)\<`TData`, `TError`\> + +The infinite query result. + +The infinite query result. + +### Param + +A function that returns infinite query options. + +### Param + +The Angular injector to use. + +### Defined in + +[inject-infinite-query.ts:30](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-infinite-query.ts#L30) + +## injectInfiniteQuery(optionsFn, injector) + +```ts +function injectInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(optionsFn, injector?): DefinedCreateInfiniteQueryResult +``` + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **optionsFn** + +A function that returns infinite query options. + +• **injector?**: `Injector` + +The Angular injector to use. + +### Returns + +[`DefinedCreateInfiniteQueryResult`](../type-aliases/definedcreateinfinitequeryresult.md)\<`TData`, `TError`\> + +The infinite query result. + +The infinite query result. + +### Param + +A function that returns infinite query options. + +### Param + +The Angular injector to use. + +### Defined in + +[inject-infinite-query.ts:57](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-infinite-query.ts#L57) + +## injectInfiniteQuery(optionsFn, injector) + +```ts +function injectInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(optionsFn, injector?): CreateInfiniteQueryResult +``` + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **optionsFn** + +A function that returns infinite query options. + +• **injector?**: `Injector` + +The Angular injector to use. + +### Returns + +[`CreateInfiniteQueryResult`](../type-aliases/createinfinitequeryresult.md)\<`TData`, `TError`\> + +The infinite query result. + +The infinite query result. + +### Param + +A function that returns infinite query options. + +### Param + +The Angular injector to use. + +### Defined in + +[inject-infinite-query.ts:84](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-infinite-query.ts#L84) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectisfetching.md b/docs/zh-hans/framework/angular/reference/functions/injectisfetching.md new file mode 100644 index 00000000000..95733f29320 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectisfetching.md @@ -0,0 +1,37 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.704Z' +id: injectIsFetching +title: injectIsFetching +--- + +# Function: injectIsFetching() + +```ts +function injectIsFetching(filters?, injector?): Signal +``` + +Injects a signal that tracks the number of queries that your application is loading or +fetching in the background. + +Can be used for app-wide loading indicators + +## Parameters + +• **filters?**: `QueryFilters` + +The filters to apply to the query. + +• **injector?**: `Injector` + +The Angular injector to use. + +## Returns + +`Signal`\<`number`\> + +signal with number of loading or fetching queries. + +## Defined in + +[inject-is-fetching.ts:17](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-is-fetching.ts#L17) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectismutating.md b/docs/zh-hans/framework/angular/reference/functions/injectismutating.md new file mode 100644 index 00000000000..cb224beb0ff --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectismutating.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.631Z' +id: injectIsMutating +title: injectIsMutating +--- + +# Function: injectIsMutating() + +```ts +function injectIsMutating(filters?, injector?): Signal +``` + +Injects a signal that tracks the number of mutations that your application is fetching. + +Can be used for app-wide loading indicators + +## Parameters + +• **filters?**: `MutationFilters` + +The filters to apply to the query. + +• **injector?**: `Injector` + +The Angular injector to use. + +## Returns + +`Signal`\<`number`\> + +signal with number of fetching mutations. + +## Defined in + +[inject-is-mutating.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-is-mutating.ts#L16) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectmutation.md b/docs/zh-hans/framework/angular/reference/functions/injectmutation.md new file mode 100644 index 00000000000..eed94ef9a5d --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectmutation.md @@ -0,0 +1,48 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:09:50.452Z' +id: injectMutation +title: 函数 / injectMutation +--- +# 函数: injectMutation() + +```ts +function injectMutation( + optionsFn, + injector?, +): CreateMutationResult +``` + +注入一个变更操作 (mutation):这是一个可被调用的命令式函数,通常用于执行服务端副作用。 + +与查询 (queries) 不同,变更操作不会自动执行。 + +## 类型参数 + +• **TData** = `unknown` + +• **TError** = `Error` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## 参数 + +• **optionsFn** + +返回变更操作选项的函数。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器 (injector)。 + +## 返回值 + +[`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`\> + +变更操作实例。 + +## 定义位置 + +[inject-mutation.ts:38](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-mutation.ts#L38) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectmutationstate.md b/docs/zh-hans/framework/angular/reference/functions/injectmutationstate.md new file mode 100644 index 00000000000..404edab4bbe --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectmutationstate.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:00.306Z' +id: injectMutationState +title: injectMutationState +--- + +# Function: injectMutationState() + +```ts +function injectMutationState( + mutationStateOptionsFn, + options?, +): Signal +``` + +Injects a signal that tracks the state of all mutations. + +## Type Parameters + +• **TResult** = `MutationState`\<`unknown`, `Error`, `unknown`, `unknown`\> + +## Parameters + +• **mutationStateOptionsFn** = `...` + +A function that returns mutation state options. + +• **options?**: [`InjectMutationStateOptions`](../interfaces/injectmutationstateoptions.md) + +The Angular injector to use. + +## Returns + +`Signal`\<`TResult`[]\> + +The signal that tracks the state of all mutations. + +## Defined in + +[inject-mutation-state.ts:53](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-mutation-state.ts#L53) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectqueries.md b/docs/zh-hans/framework/angular/reference/functions/injectqueries.md new file mode 100644 index 00000000000..27c9e9abe8d --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectqueries.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:00.233Z' +id: injectQueries +title: injectQueries +--- + +# Function: injectQueries() + +```ts +function injectQueries( + __namedParameters, + injector?, +): Signal +``` + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TCombinedResult** = `T` _extends_ [] ? [] : `T` _extends_ [`Head`] ? [`GetResults`\<`Head`\>] : `T` _extends_ [`Head`, `...Tail[]`] ? [`...Tail[]`] _extends_ [] ? [] : [`...Tail[]`] _extends_ [`Head`] ? [`GetResults`\<`Head`\>, `GetResults`\<`Head`\>] : [`...Tail[]`] _extends_ [`Head`, `...Tail[]`] ? [`...Tail[]`] _extends_ [] ? [] : [`...Tail[]`] _extends_ [`Head`] ? [`GetResults`\<`Head`\>, `GetResults`\<`Head`\>, `GetResults`\<`Head`\>] : [`...Tail[]`] _extends_ [`Head`, `...Tail[]`] ? [`...(...)[]`] _extends_ [] ? [] : ... _extends_ ... ? ... : ... : [`...(...)[]`] _extends_ ...[] ? ...[] : ...[] : [`...Tail[]`] _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] : `T` _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] + +## Parameters + +• **\_\_namedParameters** + +• **\_\_namedParameters.combine?** + +• **\_\_namedParameters.queries?**: `Signal`\<[`...(T extends [] ? [] : T extends [Head] ? [GetOptions] : T extends [Head, ...Tail[]] ? [...Tail[]] extends [] ? [] : [...Tail[]] extends [Head] ? [GetOptions, GetOptions] : [...Tail[]] extends [Head, ...Tail[]] ? [...(...)[]] extends [] ? [] : (...) extends (...) ? (...) : (...) : readonly (...)[] extends [...(...)[]] ? [...(...)[]] : (...) extends (...) ? (...) : (...) : readonly unknown[] extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[])[]`]\> + +• **injector?**: `Injector` + +## Returns + +`Signal`\<`TCombinedResult`\> + +## Defined in + +[inject-queries.ts:188](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-queries.ts#L188) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectquery.md b/docs/zh-hans/framework/angular/reference/functions/injectquery.md new file mode 100644 index 00000000000..89f5a932fc6 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectquery.md @@ -0,0 +1,318 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:09:34.795Z' +id: injectQuery +title: 函数 / injectQuery +--- +# 函数: injectQuery() + +注入一个查询:声明式依赖与异步数据源的绑定关系,该数据源与唯一键相关联。 + +**基础示例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +类似于 Angular 中的 `computed`,传递给 `injectQuery` 的函数将在响应式上下文中运行。 +在下面的示例中,当 filter 信号变为真值时,查询会自动启用并执行。当 filter 信号变回假值时,查询将被禁用。 + +**响应式示例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // 信号可以与表达式结合使用 + enabled: !!this.filter(), + })) +} +``` + +## 参数 + +返回查询选项的函数。 + +## 参数 + +要使用的 Angular 注入器。 + +## 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +## injectQuery(optionsFn, injector) + +```ts +function injectQuery( + optionsFn, + injector?, +): DefinedCreateQueryResult +``` + +注入一个查询:声明式依赖与异步数据源的绑定关系,该数据源与唯一键相关联。 + +**基础示例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +类似于 Angular 中的 `computed`,传递给 `injectQuery` 的函数将在响应式上下文中运行。 +在下面的示例中,当 filter 信号变为真值时,查询会自动启用并执行。当 filter 信号变回假值时,查询将被禁用。 + +**响应式示例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // 信号可以与表达式结合使用 + enabled: !!this.filter(), + })) +} +``` + +### 类型参数 + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _继承_ `QueryKey` = `QueryKey` + +### 参数 + +• **optionsFn** + +返回查询选项的函数。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器。 + +### 返回值 + +[`DefinedCreateQueryResult`](../type-aliases/definedcreatequeryresult.md)\<`TData`, `TError`\> + +查询结果。 + +查询结果。 + +### 参数 + +返回查询选项的函数。 + +### 参数 + +要使用的 Angular 注入器。 + +### 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 定义于 + +[inject-query.ts:53](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query.ts#L53) + +## injectQuery(optionsFn, injector) + +```ts +function injectQuery( + optionsFn, + injector?, +): CreateQueryResult +``` + +注入一个查询:声明式依赖与异步数据源的绑定关系,该数据源与唯一键相关联。 + +**基础示例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +类似于 Angular 中的 `computed`,传递给 `injectQuery` 的函数将在响应式上下文中运行。 +在下面的示例中,当 filter 信号变为真值时,查询会自动启用并执行。当 filter 信号变回假值时,查询将被禁用。 + +**响应式示例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // 信号可以与表达式结合使用 + enabled: !!this.filter(), + })) +} +``` + +### 类型参数 + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _继承_ `QueryKey` = `QueryKey` + +### 参数 + +• **optionsFn** + +返回查询选项的函数。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器。 + +### 返回值 + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +查询结果。 + +查询结果。 + +### 参数 + +返回查询选项的函数。 + +### 参数 + +要使用的 Angular 注入器。 + +### 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 定义于 + +[inject-query.ts:102](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query.ts#L102) + +## injectQuery(optionsFn, injector) + +```ts +function injectQuery( + optionsFn, + injector?, +): CreateQueryResult +``` + +注入一个查询:声明式依赖与异步数据源的绑定关系,该数据源与唯一键相关联。 + +**基础示例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +类似于 Angular 中的 `computed`,传递给 `injectQuery` 的函数将在响应式上下文中运行。 +在下面的示例中,当 filter 信号变为真值时,查询会自动启用并执行。当 filter 信号变回假值时,查询将被禁用。 + +**响应式示例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // 信号可以与表达式结合使用 + enabled: !!this.filter(), + })) +} +``` + +### 类型参数 + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _继承_ `QueryKey` = `QueryKey` + +### 参数 + +• **optionsFn** + +返回查询选项的函数。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器。 + +### 返回值 + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +查询结果。 + +查询结果。 + +### 参数 + +返回查询选项的函数。 + +### 参数 + +要使用的 Angular 注入器。 + +### 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 参见 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 定义于 + +[inject-query.ts:151](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query.ts#L151) diff --git a/docs/zh-hans/framework/angular/reference/functions/injectqueryclient.md b/docs/zh-hans/framework/angular/reference/functions/injectqueryclient.md new file mode 100644 index 00000000000..1ff9b435ff4 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/injectqueryclient.md @@ -0,0 +1,90 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.756Z' +id: injectQueryClient +title: injectQueryClient +--- + +# Function: injectQueryClient() + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +## injectQueryClient() + +```ts +function injectQueryClient(): QueryClient +``` + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +### Returns + +`QueryClient` + +### Defined in + +[inject-query-client.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L16) + +## injectQueryClient(injectOptions) + +```ts +function injectQueryClient(injectOptions): QueryClient +``` + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +### Parameters + +• **injectOptions**: `InjectOptions` & `object` & `object` + +### Returns + +`QueryClient` + +### Defined in + +[inject-query-client.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L16) + +## injectQueryClient(injectOptions) + +```ts +function injectQueryClient(injectOptions): null | QueryClient +``` + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +### Parameters + +• **injectOptions**: `InjectOptions` & `object` + +### Returns + +`null` \| `QueryClient` + +### Defined in + +[inject-query-client.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L16) diff --git a/docs/zh-hans/framework/angular/reference/functions/provideangularquery.md b/docs/zh-hans/framework/angular/reference/functions/provideangularquery.md new file mode 100644 index 00000000000..a2b15403ab5 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/provideangularquery.md @@ -0,0 +1,66 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.704Z' +id: provideAngularQuery +title: provideAngularQuery +--- + +# Function: provideAngularQuery() + +```ts +function provideAngularQuery(queryClient): EnvironmentProviders +``` + +Sets up providers necessary to enable TanStack Query functionality for Angular applications. + +Allows to configure a `QueryClient`. + +**Example - standalone** + +```ts +import { + provideAngularQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +bootstrapApplication(AppComponent, { + providers: [provideAngularQuery(new QueryClient())], +}) +``` + +**Example - NgModule-based** + +```ts +import { + provideAngularQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule], + providers: [provideAngularQuery(new QueryClient())], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +## Parameters + +• **queryClient**: `QueryClient` + +A `QueryClient` instance. + +## Returns + +`EnvironmentProviders` + +A set of providers to set up TanStack Query. + +## See + +https://tanstack.com/query/v5/docs/framework/angular/quick-start + +## Defined in + +[providers.ts:50](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/providers.ts#L50) diff --git a/docs/zh-hans/framework/angular/reference/functions/providequeryclient.md b/docs/zh-hans/framework/angular/reference/functions/providequeryclient.md new file mode 100644 index 00000000000..43d229204e1 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/providequeryclient.md @@ -0,0 +1,29 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.668Z' +id: provideQueryClient +title: provideQueryClient +--- + +# Function: provideQueryClient() + +```ts +function provideQueryClient(value): Provider +``` + +Usually [provideAngularQuery](provideangularquery.md) is used once to set up TanStack Query and the +[https://tanstack.com/query/latest/docs/reference/QueryClient|QueryClient](https://tanstack.com/query/latest/docs/reference/QueryClient|QueryClient) +for the entire application. You can use `provideQueryClient` to provide a +different `QueryClient` instance for a part of the application. + +## Parameters + +• **value**: `QueryClient` \| () => `QueryClient` + +## Returns + +`Provider` + +## Defined in + +[inject-query-client.ts:25](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L25) diff --git a/docs/zh-hans/framework/angular/reference/functions/queryoptions.md b/docs/zh-hans/framework/angular/reference/functions/queryoptions.md new file mode 100644 index 00000000000..7cbd9551e26 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/functions/queryoptions.md @@ -0,0 +1,146 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.626Z' +id: queryOptions +title: queryOptions +--- + +# Function: queryOptions() + +Allows to share and re-use query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** + +```ts +const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise +}) + +const queryClient = new QueryClient() +const data = queryClient.getQueryData(queryKey) +// ^? number | undefined +``` + +## Param + +The query options to tag with the type from `queryFn`. + +## queryOptions(options) + +```ts +function queryOptions( + options, +): UndefinedInitialDataOptions & object +``` + +Allows to share and re-use query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** + +```ts +const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise +}) + +const queryClient = new QueryClient() +const data = queryClient.getQueryData(queryKey) +// ^? number | undefined +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +The query options to tag with the type from `queryFn`. + +### Returns + +[`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +The tagged query options. + +The tagged query options. + +### Param + +The query options to tag with the type from `queryFn`. + +### Defined in + +[query-options.ts:52](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L52) + +## queryOptions(options) + +```ts +function queryOptions( + options, +): DefinedInitialDataOptions & object +``` + +Allows to share and re-use query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** + +```ts +const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise +}) + +const queryClient = new QueryClient() +const data = queryClient.getQueryData(queryKey) +// ^? number | undefined +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +The query options to tag with the type from `queryFn`. + +### Returns + +[`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +The tagged query options. + +The tagged query options. + +### Param + +The query options to tag with the type from `queryFn`. + +### Defined in + +[query-options.ts:85](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L85) diff --git a/docs/zh-hans/framework/angular/reference/index.md b/docs/zh-hans/framework/angular/reference/index.md new file mode 100644 index 00000000000..e6ff8f4cc06 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/index.md @@ -0,0 +1,52 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T04:49:47.023Z' +id: '@tanstack/angular-query-experimental' +title: '@tanstack/angular-query-experimental' +--- + +# @tanstack/angular-query-experimental + +## Interfaces + +- [BaseMutationNarrowing](interfaces/basemutationnarrowing.md) +- [BaseQueryNarrowing](interfaces/basequerynarrowing.md) +- [CreateBaseQueryOptions](interfaces/createbasequeryoptions.md) +- [CreateInfiniteQueryOptions](interfaces/createinfinitequeryoptions.md) +- [CreateMutationOptions](interfaces/createmutationoptions.md) +- [CreateQueryOptions](interfaces/createqueryoptions.md) +- [InjectMutationStateOptions](interfaces/injectmutationstateoptions.md) + +## Type Aliases + +- [CreateBaseMutationResult](type-aliases/createbasemutationresult.md) +- [CreateBaseQueryResult](type-aliases/createbasequeryresult.md) +- [CreateInfiniteQueryResult](type-aliases/createinfinitequeryresult.md) +- [CreateMutateAsyncFunction](type-aliases/createmutateasyncfunction.md) +- [CreateMutateFunction](type-aliases/createmutatefunction.md) +- [CreateMutationResult](type-aliases/createmutationresult.md) +- [CreateQueryResult](type-aliases/createqueryresult.md) +- [DefinedCreateInfiniteQueryResult](type-aliases/definedcreateinfinitequeryresult.md) +- [DefinedCreateQueryResult](type-aliases/definedcreatequeryresult.md) +- [DefinedInitialDataInfiniteOptions](type-aliases/definedinitialdatainfiniteoptions.md) +- [DefinedInitialDataOptions](type-aliases/definedinitialdataoptions.md) +- [NonUndefinedGuard](type-aliases/nonundefinedguard.md) +- [QueriesOptions](type-aliases/queriesoptions.md) +- [QueriesResults](type-aliases/queriesresults.md) +- [UndefinedInitialDataInfiniteOptions](type-aliases/undefinedinitialdatainfiniteoptions.md) +- [UndefinedInitialDataOptions](type-aliases/undefinedinitialdataoptions.md) + +## Functions + +- [infiniteQueryOptions](functions/infinitequeryoptions.md) +- [injectInfiniteQuery](functions/injectinfinitequery.md) +- [injectIsFetching](functions/injectisfetching.md) +- [injectIsMutating](functions/injectismutating.md) +- [injectMutation](functions/injectmutation.md) +- [injectMutationState](functions/injectmutationstate.md) +- [injectQueries](functions/injectqueries.md) +- [injectQuery](functions/injectquery.md) +- [injectQueryClient](functions/injectqueryclient.md) +- [provideAngularQuery](functions/provideangularquery.md) +- [provideQueryClient](functions/providequeryclient.md) +- [queryOptions](functions/queryoptions.md) diff --git a/docs/zh-hans/framework/angular/reference/interfaces/basemutationnarrowing.md b/docs/zh-hans/framework/angular/reference/interfaces/basemutationnarrowing.md new file mode 100644 index 00000000000..487b6d6f5a1 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/basemutationnarrowing.md @@ -0,0 +1,98 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.568Z' +id: BaseMutationNarrowing +title: BaseMutationNarrowing +--- + +# Interface: BaseMutationNarrowing\ + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Properties + +### isError() + +```ts +isError: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:248](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L248) + +--- + +### isIdle() + +```ts +isIdle: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:278](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L278) + +--- + +### isPending() + +```ts +isPending: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:263](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L263) + +--- + +### isSuccess() + +```ts +isSuccess: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:233](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L233) diff --git a/docs/zh-hans/framework/angular/reference/interfaces/basequerynarrowing.md b/docs/zh-hans/framework/angular/reference/interfaces/basequerynarrowing.md new file mode 100644 index 00000000000..9ccdeab0dc0 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/basequerynarrowing.md @@ -0,0 +1,74 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.532Z' +id: BaseQueryNarrowing +title: BaseQueryNarrowing +--- + +# Interface: BaseQueryNarrowing\ + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Properties + +### isError() + +```ts +isError: (this) => this is CreateBaseQueryResult>; +``` + +#### Parameters + +• **this**: [`CreateBaseQueryResult`](../type-aliases/createbasequeryresult.md)\<`TData`, `TError`, `QueryObserverResult`\<`TData`, `TError`\>\> + +#### Returns + +`this is CreateBaseQueryResult>` + +#### Defined in + +[types.ts:75](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L75) + +--- + +### isPending() + +```ts +isPending: (this) => this is CreateBaseQueryResult>; +``` + +#### Parameters + +• **this**: [`CreateBaseQueryResult`](../type-aliases/createbasequeryresult.md)\<`TData`, `TError`, `QueryObserverResult`\<`TData`, `TError`\>\> + +#### Returns + +`this is CreateBaseQueryResult>` + +#### Defined in + +[types.ts:82](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L82) + +--- + +### isSuccess() + +```ts +isSuccess: (this) => this is CreateBaseQueryResult>; +``` + +#### Parameters + +• **this**: [`CreateBaseQueryResult`](../type-aliases/createbasequeryresult.md)\<`TData`, `TError`, `QueryObserverResult`\<`TData`, `TError`\>\> + +#### Returns + +`this is CreateBaseQueryResult>` + +#### Defined in + +[types.ts:68](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L68) diff --git a/docs/zh-hans/framework/angular/reference/interfaces/createbasequeryoptions.md b/docs/zh-hans/framework/angular/reference/interfaces/createbasequeryoptions.md new file mode 100644 index 00000000000..66b913b6683 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/createbasequeryoptions.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.494Z' +id: CreateBaseQueryOptions +title: CreateBaseQueryOptions +--- + +# Interface: CreateBaseQueryOptions\ + +## Extends + +- `QueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryData`, `TQueryKey`\> + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` diff --git a/docs/zh-hans/framework/angular/reference/interfaces/createinfinitequeryoptions.md b/docs/zh-hans/framework/angular/reference/interfaces/createinfinitequeryoptions.md new file mode 100644 index 00000000000..552cb10bb1a --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/createinfinitequeryoptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.431Z' +id: CreateInfiniteQueryOptions +title: CreateInfiniteQueryOptions +--- + +# Interface: CreateInfiniteQueryOptions\ + +## Extends + +- `OmitKeyof`\<`InfiniteQueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryData`, `TQueryKey`, `TPageParam`\>, `"suspense"`\> + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` diff --git a/docs/zh-hans/framework/angular/reference/interfaces/createmutationoptions.md b/docs/zh-hans/framework/angular/reference/interfaces/createmutationoptions.md new file mode 100644 index 00000000000..884be525fdf --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/createmutationoptions.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.390Z' +id: CreateMutationOptions +title: CreateMutationOptions +--- + +# Interface: CreateMutationOptions\ + +## Extends + +- `OmitKeyof`\<`MutationObserverOptions`\<`TData`, `TError`, `TVariables`, `TContext`\>, `"_defaulted"`\> + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` diff --git a/docs/zh-hans/framework/angular/reference/interfaces/createqueryoptions.md b/docs/zh-hans/framework/angular/reference/interfaces/createqueryoptions.md new file mode 100644 index 00000000000..2fbb5728381 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/createqueryoptions.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.336Z' +id: CreateQueryOptions +title: CreateQueryOptions +--- + +# Interface: CreateQueryOptions\ + +## Extends + +- `OmitKeyof`\<[`CreateBaseQueryOptions`](createbasequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`\>, `"suspense"`\> + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` diff --git a/docs/zh-hans/framework/angular/reference/interfaces/injectmutationstateoptions.md b/docs/zh-hans/framework/angular/reference/interfaces/injectmutationstateoptions.md new file mode 100644 index 00000000000..4e8c8f9ce6a --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/interfaces/injectmutationstateoptions.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:02:49.273Z' +id: InjectMutationStateOptions +title: InjectMutationStateOptions +--- + +# Interface: InjectMutationStateOptions + +## Properties + +### injector? + +```ts +optional injector: Injector; +``` + +#### Defined in + +[inject-mutation-state.ts:43](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-mutation-state.ts#L43) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createbasemutationresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/createbasemutationresult.md new file mode 100644 index 00000000000..b0e3c1580f5 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createbasemutationresult.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.487Z' +id: CreateBaseMutationResult +title: CreateBaseMutationResult +--- + +# Type Alias: CreateBaseMutationResult\ + +```ts +type CreateBaseMutationResult: Override, object> & object; +``` + +## Type declaration + +### mutateAsync + +```ts +mutateAsync: CreateMutateAsyncFunction +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Defined in + +[types.ts:198](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L198) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createbasequeryresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/createbasequeryresult.md new file mode 100644 index 00000000000..d62b9cbe05f --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createbasequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.445Z' +id: CreateBaseQueryResult +title: CreateBaseQueryResult +--- + +# Type Alias: CreateBaseQueryResult\ + +```ts +type CreateBaseQueryResult: BaseQueryNarrowing & MapToSignals>; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TState** = `QueryObserverResult`\<`TData`, `TError`\> + +## Defined in + +[types.ts:116](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L116) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createinfinitequeryresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/createinfinitequeryresult.md new file mode 100644 index 00000000000..6b8232f92e1 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createinfinitequeryresult.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.402Z' +id: CreateInfiniteQueryResult +title: CreateInfiniteQueryResult +--- + +# Type Alias: CreateInfiniteQueryResult\ + +```ts +type CreateInfiniteQueryResult: MapToSignals>; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[types.ts:143](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L143) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createmutateasyncfunction.md b/docs/zh-hans/framework/angular/reference/type-aliases/createmutateasyncfunction.md new file mode 100644 index 00000000000..9020dbbe714 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createmutateasyncfunction.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.361Z' +id: CreateMutateAsyncFunction +title: CreateMutateAsyncFunction +--- + +# Type Alias: CreateMutateAsyncFunction\ + +```ts +type CreateMutateAsyncFunction: MutateFunction; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Defined in + +[types.ts:188](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L188) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createmutatefunction.md b/docs/zh-hans/framework/angular/reference/type-aliases/createmutatefunction.md new file mode 100644 index 00000000000..f62f32d1dc1 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createmutatefunction.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.324Z' +id: CreateMutateFunction +title: CreateMutateFunction +--- + +# Type Alias: CreateMutateFunction()\ + +```ts +type CreateMutateFunction: (...args) => void; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Parameters + +• ...**args**: `Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TContext`\>\> + +## Returns + +`void` + +## Defined in + +[types.ts:176](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L176) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createmutationresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/createmutationresult.md new file mode 100644 index 00000000000..06e175f805a --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createmutationresult.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.291Z' +id: CreateMutationResult +title: CreateMutationResult +--- + +# Type Alias: CreateMutationResult\ + +```ts +type CreateMutationResult: BaseMutationNarrowing & MapToSignals>; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +• **TState** = `CreateStatusBasedMutationResult`\<[`CreateBaseMutationResult`](createbasemutationresult.md)\[`"status"`\], `TData`, `TError`, `TVariables`, `TContext`\> + +## Defined in + +[types.ts:292](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L292) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/createqueryresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/createqueryresult.md new file mode 100644 index 00000000000..b56b6830688 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/createqueryresult.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.245Z' +id: CreateQueryResult +title: CreateQueryResult +--- + +# Type Alias: CreateQueryResult\ + +```ts +type CreateQueryResult: CreateBaseQueryResult; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[types.ts:126](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L126) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md new file mode 100644 index 00000000000..1753cd2401b --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.202Z' +id: DefinedCreateInfiniteQueryResult +title: DefinedCreateInfiniteQueryResult +--- + +# Type Alias: DefinedCreateInfiniteQueryResult\ + +```ts +type DefinedCreateInfiniteQueryResult: MapToSignals; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TDefinedInfiniteQueryObserver** = `DefinedInfiniteQueryObserverResult`\<`TData`, `TError`\> + +## Defined in + +[types.ts:151](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L151) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/definedcreatequeryresult.md b/docs/zh-hans/framework/angular/reference/type-aliases/definedcreatequeryresult.md new file mode 100644 index 00000000000..e73bb0d6531 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/definedcreatequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.165Z' +id: DefinedCreateQueryResult +title: DefinedCreateQueryResult +--- + +# Type Alias: DefinedCreateQueryResult\ + +```ts +type DefinedCreateQueryResult: MapToSignals; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TDefinedQueryObserver** = `DefinedQueryObserverResult`\<`TData`, `TError`\> + +## Defined in + +[types.ts:134](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L134) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md b/docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md new file mode 100644 index 00000000000..252ca425bb1 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.128Z' +id: DefinedInitialDataInfiniteOptions +title: DefinedInitialDataInfiniteOptions +--- + +# Type Alias: DefinedInitialDataInfiniteOptions\ + +```ts +type DefinedInitialDataInfiniteOptions: CreateInfiniteQueryOptions & object; +``` + +## Type declaration + +### initialData + +```ts +initialData: NonUndefinedGuard> | () => NonUndefinedGuard>; +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `DefaultError` + +• **TData** = `InfiniteData`\<`TQueryFnData`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Defined in + +[infinite-query-options.ts:32](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L32) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdataoptions.md b/docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdataoptions.md new file mode 100644 index 00000000000..9d2f1d23bd8 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/definedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.081Z' +id: DefinedInitialDataOptions +title: DefinedInitialDataOptions +--- + +# Type Alias: DefinedInitialDataOptions\ + +```ts +type DefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData + +```ts +initialData: NonUndefinedGuard | () => NonUndefinedGuard; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[query-options.ts:19](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L19) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/nonundefinedguard.md b/docs/zh-hans/framework/angular/reference/type-aliases/nonundefinedguard.md new file mode 100644 index 00000000000..57bb866538b --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/nonundefinedguard.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:18.042Z' +id: NonUndefinedGuard +title: NonUndefinedGuard +--- + +# Type Alias: NonUndefinedGuard\ + +```ts +type NonUndefinedGuard: T extends undefined ? never : T; +``` + +## Type Parameters + +• **T** + +## Defined in + +[types.ts:316](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L316) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/queriesoptions.md b/docs/zh-hans/framework/angular/reference/type-aliases/queriesoptions.md new file mode 100644 index 00000000000..628ebb19a09 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/queriesoptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.997Z' +id: QueriesOptions +title: QueriesOptions +--- + +# Type Alias: QueriesOptions\ + +```ts +type QueriesOptions: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResult, GetOptions] : T extends [infer Head, ...(infer Tail)] ? QueriesOptions<[...Tail], [...TResult, GetOptions], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; +``` + +QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResult** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[inject-queries.ts:108](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-queries.ts#L108) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/queriesresults.md b/docs/zh-hans/framework/angular/reference/type-aliases/queriesresults.md new file mode 100644 index 00000000000..962374fe49f --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/queriesresults.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.962Z' +id: QueriesResults +title: QueriesResults +--- + +# Type Alias: QueriesResults\ + +```ts +type QueriesResults: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResult, GetResults] : T extends [infer Head, ...(infer Tail)] ? QueriesResults<[...Tail], [...TResult, GetResults], [...TDepth, 1]> : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverResult[] : QueryObserverResult[]; +``` + +QueriesResults reducer recursively maps type param to results + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResult** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[inject-queries.ts:151](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-queries.ts#L151) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md b/docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md new file mode 100644 index 00000000000..3d66a922419 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.913Z' +id: UndefinedInitialDataInfiniteOptions +title: UndefinedInitialDataInfiniteOptions +--- + +# Type Alias: UndefinedInitialDataInfiniteOptions\ + +```ts +type UndefinedInitialDataInfiniteOptions: CreateInfiniteQueryOptions & object; +``` + +## Type declaration + +### initialData? + +```ts +optional initialData: undefined; +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `DefaultError` + +• **TData** = `InfiniteData`\<`TQueryFnData`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Defined in + +[infinite-query-options.ts:12](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L12) diff --git a/docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md b/docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md new file mode 100644 index 00000000000..3d49d27cf86 --- /dev/null +++ b/docs/zh-hans/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:03:17.860Z' +id: UndefinedInitialDataOptions +title: UndefinedInitialDataOptions +--- + +# Type Alias: UndefinedInitialDataOptions\ + +```ts +type UndefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData? + +```ts +optional initialData: undefined; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[query-options.ts:7](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L7) diff --git a/docs/zh-hans/framework/angular/typescript.md b/docs/zh-hans/framework/angular/typescript.md new file mode 100644 index 00000000000..b9b385a7dbf --- /dev/null +++ b/docs/zh-hans/framework/angular/typescript.md @@ -0,0 +1,292 @@ +--- +source-updated-at: '2024-11-20T12:58:00.000Z' +translation-updated-at: '2025-05-06T04:55:35.083Z' +id: typescript +title: TypeScript +--- +TanStack Query 现已采用 **TypeScript** 编写,确保库与您的项目具备类型安全! + +注意事项: + +- 当前类型系统要求使用 TypeScript **v4.7** 或更高版本 +- 本仓库中的类型变更视为**非破坏性变更**,通常以 **patch** 版本号发布(否则每个类型增强都会导致主版本号变更!) +- **强烈建议您将 angular-query-experimental 包版本锁定到特定 patch 版本**,并在升级时预期类型可能在任意版本间被修复或升级 +- TanStack Query 的非类型相关公共 API 及实验阶段结束后的 angular-query 包仍严格遵循语义化版本规范 + +## 类型推断 + +TanStack Query 的类型系统通常能完美流转,您无需自行添加类型注解 + +```angular-ts +@Component({ + // ... + template: `@let data = query.data();`, + // ^? data: number | undefined +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + })) +} +``` + +```angular-ts +@Component({ + // ... + template: `@let data = query.data();`, + // ^? data: string | undefined +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), + })) +} +``` + +当您的 `queryFn` 具有明确定义的返回类型时效果最佳。请注意大多数数据获取库默认返回 `any` 类型,因此请确保将其提取到具有正确类型的函数中。 + +以下示例中我们将 Group[] 传递给 HttpClient `get` 方法的类型参数: + +```angular-ts +@Component({ + template: `@let data = query.data();`, + // ^? data: Group[] | undefined +}) +class MyComponent { + http = inject(HttpClient) + + query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: () => lastValueFrom(this.http.get('/groups')), + })) +} +``` + +## 类型收窄 + +TanStack Query 使用[可辨识联合类型](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)作为查询结果,通过 `status` 字段和派生的状态布尔值进行区分。这允许您检查如 `isSuccess()` 状态来确保 `data` 已定义: + +```angular-ts +@Component({ + // ... + template: ` + @if (query.isSuccess()) { + @let data = query.data(); + // ^? data: number + } + `, +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + })) +} +``` + +> TypeScript 当前不支持对象方法的可辨识联合。在如查询结果这样的对象上对信号字段进行类型收窄仅适用于返回布尔值的信号。建议优先使用 `isSuccess()` 等布尔状态信号而非 `status() === 'success'`。 + +## 错误字段类型标注 + +错误类型默认为 `Error`,因为这符合大多数用户的预期: + +```angular-ts +@Component({ + // ... + template: `@let error = query.error();`, + // ^? error: Error | null +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups + })) +} +``` + +如需抛出自定义错误或非 `Error` 对象,可指定错误字段类型: + +```angular-ts +@Component({ + // ... + template: `@let error = query.error();`, + // ^? error: string | null +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, + })) +} +``` + +但此方法会导致 `injectQuery` 其他泛型参数的类型推断失效。通常不建议抛出非 `Error` 对象,若您有如 `AxiosError` 的子类,可使用类型收窄使错误字段更具体: + +```ts +import axios from 'axios' + +query = injectQuery(() => ({ queryKey: ['groups'], queryFn: fetchGroups })) + +computed(() => { + const error = query.error() + // ^? error: Error | null + + if (axios.isAxiosError(error)) { + error + // ^? const error: AxiosError + } +}) +``` + +### 注册全局错误类型 + +TanStack Query v5 支持通过扩展 `Register` 接口设置全局错误类型,无需在调用处指定泛型参数,同时保证类型推断仍有效: + +```ts +import '@tanstack/angular-query-experimental' + +declare module '@tanstack/angular-query-experimental' { + interface Register { + defaultError: AxiosError + } +} + +const query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +computed(() => { + const error = query.error() + // ^? error: AxiosError | null +}) +``` + +## 元数据 (meta) 类型标注 + +### 注册全局元数据类型 + +类似于注册[全局错误类型](#registering-a-global-error),您也可以注册全局 `Meta` 类型。这确保[查询](./reference/injectQuery.md)和[变更](./reference/injectMutation.md)中的可选 `meta` 字段保持类型安全且一致。注意注册类型必须扩展 `Record` 以保证 `meta` 始终为对象。 + +```ts +import '@tanstack/angular-query-experimental' + +interface MyMeta extends Record { + // 您的元类型定义 +} + +declare module '@tanstack/angular-query-experimental' { + interface Register { + queryMeta: MyMeta + mutationMeta: MyMeta + } +} +``` + +## 查询与变更键 (key) 类型标注 + +### 注册查询与变更键类型 + +同样类似于注册[全局错误类型](#registering-a-global-error),您可注册全局 `QueryKey` 和 `MutationKey` 类型。这允许您为键提供更符合应用层次结构的类型约束,并在整个库中保持类型安全。注意注册类型必须扩展 `Array` 类型以保证键始终为数组。 + +```ts +import '@tanstack/angular-query-experimental' + +type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray] + +declare module '@tanstack/angular-query-experimental' { + interface Register { + queryKey: QueryKey + mutationKey: QueryKey + } +} +``` + +## 查询选项 (Query Options) 类型标注 + +若在 `injectQuery` 内联查询选项,将获得自动类型推断。但若需将查询选项提取到独立函数中以在 `injectQuery` 和 `prefetchQuery` 间共享,或将其托管在服务中,则会丢失类型推断。此时可使用 `queryOptions` 辅助工具恢复类型推断: + +```ts +@Injectable({ + providedIn: 'root', +}) +export class QueriesService { + private http = inject(HttpClient) + + post(postId: number) { + return queryOptions({ + queryKey: ['post', postId], + queryFn: () => { + return lastValueFrom( + this.http.get( + `https://jsonplaceholder.typicode.com/posts/${postId}`, + ), + ) + }, + }) + } +} + +@Component({ + // ... +}) +export class Component { + queryClient = inject(QueryClient) + + postId = signal(1) + + queries = inject(QueriesService) + optionsSignal = computed(() => this.queries.post(this.postId())) + + postQuery = injectQuery(() => this.queries.post(1)) + postQuery = injectQuery(() => this.queries.post(this.postId())) + + // 也可传递返回查询选项的信号 + postQuery = injectQuery(this.optionsSignal) + + someMethod() { + this.queryClient.prefetchQuery(this.queries.post(23)) + } +} +``` + +此外,`queryOptions` 返回的 `queryKey` 知晓其关联的 `queryFn`,我们可以利用此类型信息使如 `queryClient.getQueryData` 等方法也能感知这些类型: + +```ts +data = this.queryClient.getQueryData(groupOptions().queryKey) +// ^? data: Post | undefined +``` + +若不使用 `queryOptions`,data 类型将为 unknown,除非传递类型参数: + +```ts +data = queryClient.getQueryData(['post', 1]) +``` + +## 变更选项 (Mutation Options) 类型标注 + +类似于 `queryOptions`,您可使用 `mutationOptions` 将变更选项提取到独立函数: + +```ts +export class QueriesService { + private http = inject(HttpClient) + + updatePost(id: number) { + return mutationOptions({ + mutationFn: (post: Post) => Promise.resolve(post), + mutationKey: ['updatePost', id], + onSuccess: (newPost) => { + // ^? newPost: Post + this.queryClient.setQueryData(['posts', id], newPost) + }, + }) + } +} +``` + +## 使用 `skipToken` 实现类型安全的查询禁用 + +若使用 TypeScript,可通过 `skipToken` 禁用查询。这在需要基于条件禁用查询但仍需保持类型安全时非常有用。更多信息请参阅[禁用查询](./guides/disabling-queries.md)指南。 diff --git a/docs/zh-hans/framework/angular/zoneless.md b/docs/zh-hans/framework/angular/zoneless.md new file mode 100644 index 00000000000..ab2b2560ca4 --- /dev/null +++ b/docs/zh-hans/framework/angular/zoneless.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2024-06-20T22:57:31.000Z' +translation-updated-at: '2025-05-06T04:49:56.860Z' +id: zoneless +title: 无 Zone +--- +由于 TanStack Query 的 Angular 适配器基于信号机制构建,因此它能完全支持无区域(Zoneless)模式! + +无区域模式的优势包括提升性能和改进调试体验。具体细节请参阅 [Angular 官方文档](https://angular.dev/guide/experimental/zoneless)。 + +> 请注意,Angular 无区域模式的 API 目前仍处于实验阶段,可能会在 Angular 的补丁版本中发生变更。 +> 除无区域模式外,该适配器同样完整支持 ZoneJS 变更检测。 diff --git a/docs/zh-hans/framework/react/community/community-projects.md b/docs/zh-hans/framework/react/community/community-projects.md new file mode 100644 index 00000000000..6a2857b0fc6 --- /dev/null +++ b/docs/zh-hans/framework/react/community/community-projects.md @@ -0,0 +1,160 @@ +--- +source-updated-at: '2025-03-30T12:50:39.000Z' +translation-updated-at: '2025-05-06T03:59:00.632Z' +id: community-projects +title: 社区项目 +--- +有许多社区项目基于 React Query 构建,并利用它提供额外功能或增强开发者体验。以下项目按字母顺序排列。如果您有希望添加到列表中的项目,请提交 PR! + +> 请注意,这些项目完全由社区维护。如有相关问题,请联系项目维护者。 + +## Atomic CRM + +一款功能齐全的 CRM,基于 React、react-admin 和 Supabase 构建。 + +链接:https://marmelab.com/atomic-crm/ + +## batshit + +批处理管理器,可对指定时间窗口内相同数据类型的请求进行去重和批量处理 + +链接:https://github.com/yornaath/batshit + +## Blitz + +Next.js 的全栈工具包 + +链接:https://blitzjs.com/ + +## Connect + +用于构建浏览器兼容和 gRPC 兼容 HTTP API 的系列库 + +链接:https://connectrpc.com/docs + +## GraphQL Code Generator + +根据 GraphQL 模式生成 React Query 钩子 + +链接:https://the-guild.dev/graphql/codegen + +## Http-wizard + +✨ 基于 Fastify 的端到端类型安全 API,搭配 TypeScript 魔法 ✨ + +链接:https://http-wizard.com + +## Kubb + +为所有 API 生成 SDK + +链接:https://www.kubb.dev/ + +## NgQuery + +Angular 的查询适配器 + +链接:https://ngneat.github.io/query/ + +## Normy + +为数据获取库提供自动标准化和数据更新 + +链接:https://github.com/klis87/normy + +## OpenAPI codegen + +基于 OpenAPI 模式生成代码的工具 + +链接:https://github.com/fabien0102/openapi-codegen + +## OpenAPI Qraft React + +直接从 OpenAPI 文档生成类型安全的 API 客户端和 TanStack Query 钩子。 +零运行时开销、基于代理的设计、无缝 SSR 支持,完整 TanStack Query 功能。 + +链接:https://github.com/OpenAPI-Qraft/openapi-qraft + +## OpenAPI React Query codegen + +基于 OpenAPI 规范文件生成 TanStack Query 钩子 + +链接:https://github.com/7nohe/openapi-react-query-codegen + +### OpenAPI zod client + +根据 OpenAPI 规范生成 zodios 客户端 + +链接:https://github.com/astahmer/openapi-zod-client + +## openapi-fetch + +仅 2KB 的轻量类型安全 fetch 封装器,使用静态 TypeScript 类型推断且无需运行时检查 + +链接:https://openapi-ts.dev/openapi-react-query/ + +## Orval + +根据 OpenAPI 规范生成 TypeScript 客户端 + +链接:https://orval.dev/ + +## Query Key factory + +创建类型安全标准化查询键的库,适用于 `@tanstack/query` 的缓存管理 + +链接:https://github.com/lukemorales/query-key-factory + +## Rapini + +🥬 OpenAPI 转 React Query(或 SWR)& Axios + +链接:https://github.com/rametta/rapini + +## React Query Kit + +🕊️ ReactQuery 工具包,使 ReactQuery 钩子可复用且类型安全 + +链接:https://github.com/liaoliao666/react-query-kit + +## React Query Rewind + +开发期间实现状态时间旅行与可视化 + +链接:https://reactqueryrewind.com/ + +## React Query Swagger + +基于 Swagger API 定义生成 React Query 钩子 + +链接:https://github.com/Shaddix/react-query-swagger + +## Suspensive React Query + +为 React Query 增强 Suspense 支持,实现更简单声明式的数据获取 + +链接:https://suspensive.org/docs/react-query/motivation + +## tRPC + +轻松实现端到端类型安全 API + +链接:https://trpc.io/ + +## ts-rest + +为新旧 API 提供渐进式可采用的类型安全方案 + +链接:https://ts-rest.com/ + +## wagmi + +基于 `@tanstack/react-query` 的以太坊 React 钩子 + +链接:https://wagmi.sh/ + +## zodios + +端到端类型安全 REST API 工具箱 + +链接:https://www.zodios.org/ diff --git a/docs/zh-hans/framework/react/community/tkdodos-blog.md b/docs/zh-hans/framework/react/community/tkdodos-blog.md new file mode 100644 index 00000000000..8795e5b0903 --- /dev/null +++ b/docs/zh-hans/framework/react/community/tkdodos-blog.md @@ -0,0 +1,87 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T03:58:18.522Z' +id: tkdodos-blog +title: TkDodo 的博客 +--- +TanStack Query 的维护者 [TkDodo](https://bsky.app/profile/tkdodo.eu) 撰写了一系列关于该库使用与实践的博客文章。部分文章展示了通用最佳实践,但大多数内容都带有鲜明的个人观点。 + +## [#1: 实战 React Query](https://tkdodo.eu/blog/practical-react-query) + +> 一篇超越官方文档的高级指南,涵盖实用技巧:解析默认配置(`staleTime` 与 `gcTime` 的区别)、保持服务端与客户端状态分离的理念、处理依赖项与创建自定义钩子,并深入剖析 `enabled` 选项的强大之处。[阅读全文...](https://tkdodo.eu/blog/practical-react-query) + +## [#2: React Query 数据转换](https://tkdodo.eu/blog/react-query-data-transformations) + +> 探索使用 React Query 实现数据转换的多种方案。从在 `queryFn` 中转换到运用 `select` 选项,本文对比了不同方法的优劣。[阅读全文...](https://tkdodo.eu/blog/react-query-data-transformations) + +## [#3: React Query 渲染优化](https://tkdodo.eu/blog/react-query-render-optimizations) + +> 当组件因 React Query 频繁重渲染时该如何应对?本文详解库内置的优化机制(如 `tracked queries` 避免 `isFetching` 触发渲染),并解析 `structural sharing` 的深层含义。[阅读全文...](https://tkdodo.eu/blog/react-query-render-optimizations) + +## [#4: React Query 状态检查](https://tkdodo.eu/blog/status-checks-in-react-query) + +> 通常我们会先检查 `isPending` 再判断 `isError`,但有时优先验证 `data` 是否存在更能提升用户体验。本文揭示错误的状态检查顺序如何带来负面影响。[阅读全文...](https://tkdodo.eu/blog/status-checks-in-react-query) + +## [#5: 测试 React Query](https://tkdodo.eu/blog/testing-react-query) + +> 除了官方文档的测试指南,本文提供额外技巧:关闭 `retries`、静默 `console` 等自定义钩子测试策略,并附赠基于 `mock-service-worker` 的[示例仓库](https://github.com/TkDodo/testing-react-query)(含成功/错误状态测试)。[阅读全文...](https://tkdodo.eu/blog/testing-react-query) + +## [#6: React Query 与 TypeScript](https://tkdodo.eu/blog/react-query-and-type-script) + +> 深度解析 TypeScript 泛型在 React Query 中的应用:如何利用类型推断避免显式声明 `useQuery`、处理 `unknown` 错误、实现类型收窄等高级技巧。[阅读全文...](https://tkdodo.eu/blog/react-query-and-type-script) + +## [#7: 在 React Query 中使用 WebSockets](https://tkdodo.eu/blog/using-web-sockets-with-react-query) + +> 逐步指导如何实现实时通知:涵盖事件订阅与全量数据推送两种模式,适配浏览器原生 WebSocket API、Firebase 及 GraphQL 订阅。[阅读全文...](https://tkdodo.eu/blog/using-web-sockets-with-react-query) + +## [#8: 高效的 React Query Key 设计](https://tkdodo.eu/blog/effective-react-query-keys) + +> 超越简单的字符串键值:当应用复杂度超越待办列表时,如何通过协同定位(co-location)与 Query Key 工厂实现高效管理。[阅读全文...](https://tkdodo.eu/blog/effective-react-query-keys) + +## [#8a: 活用 Query 函数上下文](https://tkdodo.eu/blog/leveraging-the-query-function-context) + +> 前文补充篇:探讨如何结合 Query 函数上下文与对象键值,为成长型应用提供极致安全性。[阅读全文...](https://tkdodo.eu/blog/leveraging-the-query-function-context) + +## [#9: React Query 中的占位与初始数据](https://tkdodo.eu/blog/placeholder-and-initial-data-in-react-query) + +> 对比占位数据(Placeholder Data)与初始数据(Initial Data)的异同,解析二者在替代加载动画、提升 UX 时的适用场景。[阅读全文...](https://tkdodo.eu/blog/placeholder-and-initial-data-in-react-query) + +## [#10: 将 React Query 作为状态管理器](https://tkdodo.eu/blog/react-query-as-a-state-manager) + +> 揭秘如何让 React Query 成为异步状态的单一数据源:理解其数据同步本质,掌握通过定制 `staleTime` 释放威力的方法。[阅读全文...](https://tkdodo.eu/blog/react-query-as-a-state-manager) + +## [#11: React Query 错误处理](https://tkdodo.eu/blog/react-query-error-handling) + +> 全面应对异步数据错误:从 error 属性、Error Boundaries 到 onError 回调,为"出错了"的场景构建防御体系。[阅读全文...](https://tkdodo.eu/blog/react-query-error-handling) + +## [#12: 精通 React Query 数据变更](https://tkdodo.eu/blog/mastering-mutations-in-react-query) + +> 深度解析数据变更(mutations):区分与查询(queries)的本质差异,比较 `mutate` 与 `mutateAsync`,实现查询与变更的联动。[阅读全文...](https://tkdodo.eu/blog/mastering-mutations-in-react-query) + +## [#13: React Query 离线策略](https://tkdodo.eu/blog/offline-react-query) + +> 移动端网络不可靠?本文详解 React Query 在离线状态下的多种应对方案。[阅读全文...](https://tkdodo.eu/blog/offline-react-query) + +## [#14: React Query 与表单处理](https://tkdodo.eu/blog/react-query-and-forms) + +> 模糊服务端与客户端状态的边界:展示两种集成方案,提供表单与 React Query 协同使用的实用技巧。[阅读全文...](https://tkdodo.eu/blog/react-query-and-forms) + +## [#15: React Query 常见问题解答](https://tkdodo.eu/blog/react-query-fa-qs) + +> 集中解答关于 React Query 的高频疑问。[阅读全文...](https://tkdodo.eu/blog/react-query-fa-qs) + +## [#16: React Query 邂逅 React Router](https://tkdodo.eu/blog/react-query-meets-react-router) + +> 当 Remix 和 React Router 革新数据加载时机,为何说它们与 React Query 是天作之合?[阅读全文...](https://tkdodo.eu/blog/react-query-meets-react-router) + +## [#17: 预填充查询缓存](https://tkdodo.eu/blog/seeding-the-query-cache) + +> 多管齐下减少加载动画:从服务端预取、路由预加载到通过 `setQueryData` 注入缓存数据。[阅读全文...](https://tkdodo.eu/blog/seeding-the-query-cache) + +## [#18: React Query 内部机制](https://tkdodo.eu/blog/inside-react-query) + +> 图解 React Query 架构:从框架无关的 Query Core 到特定框架适配器的通信原理。[阅读全文...](https://tkdodo.eu/blog/inside-react-query) + +## [#19: 类型安全的 React Query](https://tkdodo.eu/blog/type-safe-react-query) + +> "拥有类型"与"类型安全"存在本质差异。本文演示如何结合 TypeScript 实现最高级别的类型安全。[阅读全文...](https://tkdodo.eu/blog/type-safe-react-query) diff --git a/docs/zh-hans/framework/react/comparison.md b/docs/zh-hans/framework/react/comparison.md new file mode 100644 index 00000000000..3fabeb4beec --- /dev/null +++ b/docs/zh-hans/framework/react/comparison.md @@ -0,0 +1,112 @@ +--- +source-updated-at: '2024-07-11T12:20:14.000Z' +translation-updated-at: '2025-05-06T04:32:05.206Z' +id: comparison +title: 对比 +--- +> 本对比表格力求准确公正。若您使用过其中任一库并认为信息有待完善,欢迎通过页面底部的 "Edit this page on Github" 链接提交修改建议(需附说明或证据)。 + +功能/能力说明: + +- ✅ 开箱即用,无需额外配置或代码 +- 🟡 支持,但需通过第三方或社区库实现 +- 🔶 官方支持且文档完备,但需用户自行编写实现代码 +- 🛑 无官方支持或文档 + +| | React Query | SWR [_(官网)_][swr] | Apollo Client [_(官网)_][apollo] | RTK-Query [_(官网)_][rtk-query] | React Router [_(官网)_][react-router] | +| -------------------------------------------------- | ---------------------------------------- | ---------------------------------------- | ------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------- | +| GitHub 仓库/星标数 | [![][stars-react-query]][gh-react-query] | [![][stars-swr]][gh-swr] | [![][stars-apollo]][gh-apollo] | [![][stars-rtk-query]][gh-rtk-query] | [![][stars-react-router]][gh-react-router] | +| 平台要求 | React | React | React, GraphQL | Redux | React | +| 官方对比 | | (无) | (无) | [对比][rtk-query-comparison] | (无) | +| 支持的查询语法 | Promise, REST, GraphQL | Promise, REST, GraphQL | GraphQL, 任意 (响应式变量) | Promise, REST, GraphQL | Promise, REST, GraphQL | +| 支持的框架 | React | React | React + 其他 | 任意 | React | +| 缓存策略 | 层级键值对 | 唯一键值对 | 规范化 Schema | 唯一键值对 | 嵌套路由 -> 值 | +| 缓存键策略 | JSON | JSON | GraphQL 查询 | JSON | 路由路径 | +| 缓存变更检测 | 深度比较键值(稳定序列化) | 深度比较键值(稳定序列化) | 深度比较键值(非稳定序列化) | 键值引用相等 (===) | 路由变更 | +| 数据变更检测 | 深度比较 + 结构共享 | 深度比较 (通过 `stable-hash`) | 深度比较(非稳定序列化) | 键值引用相等 (===) | 加载器执行 | +| 数据记忆化 | 完整结构共享 | 标识相等 (===) | 规范化标识 | 标识相等 (===) | 标识相等 (===) | +| 包体积 | [![][bp-react-query]][bpl-react-query] | [![][bp-swr]][bpl-swr] | [![][bp-apollo]][bpl-apollo] | [![][bp-rtk-query]][bpl-rtk-query] | [![][bp-react-router]][bpl-react-router] + [![][bp-history]][bpl-history] | +| API 定义位置 | 组件内,外部配置 | 组件内 | GraphQL Schema | 外部配置 | 路由树配置 | +| 查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 缓存持久化 | ✅ | ✅ | ✅ | ✅ | 🛑 仅活跃路由 8 | +| 开发者工具 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 轮询/间隔查询 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 并行查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 依赖查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 分页查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 无限查询 | ✅ | ✅ | ✅ | 🛑 | 🛑 | +| 双向无限查询 | ✅ | 🔶 | 🔶 | 🛑 | 🛑 | +| 无限查询重获取 | ✅ | ✅ | 🛑 | 🛑 | 🛑 | +| 滞后查询数据1 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 选择器 | ✅ | 🛑 | ✅ | ✅ | N/A | +| 初始数据 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 滚动恢复 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 缓存操作 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 过时查询丢弃 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 渲染批处理与优化2 | ✅ | ✅ | 🛑 | ✅ | ✅ | +| 自动垃圾回收 | ✅ | 🛑 | 🛑 | ✅ | N/A | +| 变更钩子 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 离线变更支持 | ✅ | 🛑 | 🟡 | 🛑 | 🛑 | +| 预获取 API | ✅ | ✅ | ✅ | ✅ | ✅ | +| 查询取消 | ✅ | 🛑 | 🛑 | 🛑 | ✅ | +| 部分查询匹配3 | ✅ | 🔶 | ✅ | ✅ | N/A | +| 后台重新验证 (Stale While Revalidate) | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 过期时间配置 | ✅ | 🛑7 | 🛑 | ✅ | 🛑 | +| 预使用查询/变更配置4 | ✅ | 🛑 | ✅ | ✅ | ✅ | +| 窗口聚焦重获取 | ✅ | ✅ | 🛑 | ✅ | 🛑 | +| 网络状态重获取 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 通用缓存脱水/再水合 | ✅ | 🛑 | ✅ | ✅ | ✅ | +| 离线缓存 | ✅ | 🛑 | ✅ | 🔶 | 🛑 | +| React Suspense 支持 | ✅ | ✅ | ✅ | 🛑 | ✅ | +| 抽象化/框架无关核心 | ✅ | 🛑 | ✅ | ✅ | 🛑 | +| 变更后自动重获取5 | 🔶 | 🔶 | ✅ | ✅ | ✅ | +| 规范化缓存6 | 🛑 | 🛑 | ✅ | 🛑 | 🛑 | + +### 注解 + +> **1 滞后查询数据** - React Query 允许在加载新查询时继续显示现有查询数据(类似于 Suspense 即将原生提供的 UX)。这在编写分页 UI 或无限加载 UI 时至关重要,可避免每次请求新查询时出现生硬的加载状态。其他库不具备此能力,会在新查询加载时呈现生硬加载状态(除非已预获取)。 + +> **2 渲染优化** - React Query 具有卓越的渲染性能。默认会跟踪访问字段,仅在这些字段变化时重新渲染。若需禁用此优化,将 `notifyOnChangeProps` 设为 `'all'` 会在查询更新时重新渲染组件(例如获取新数据时)。React Query 还会批量更新,确保多组件使用同一查询时应用仅重新渲染一次。若仅关注 `data` 或 `error` 属性,可通过设置 `notifyOnChangeProps` 为 `['data', 'error']` 进一步减少渲染次数。 + +> **3 部分查询匹配** - 因 React Query 使用确定性查询键序列化,可操作变量组查询而无需知晓每个具体查询键。例如:可重新获取键以 `todos` 开头的所有查询(无论变量如何),或精准定位带(或不带)变量的特定查询,甚至使用过滤函数匹配符合特定条件的查询。 + +> **4 预使用查询配置** - 指在查询/变更被使用前预先配置其行为的能力。例如:可预先为查询设置完整默认配置,使用时仅需 `useQuery({ queryKey })`,而无需每次传递获取器或选项。SWR 通过允许配置全局默认获取器部分实现此功能,但不支持按查询配置,也不支持变更操作。 + +> **5 变更后自动重获取** - 要实现变更后真正自动重获取,需具备模式(如 GraphQL 提供的)及帮助库识别该模式中实体类型和单个实体的启发式方法。 + +> **6 规范化缓存** - React Query、SWR 和 RTK-Query 目前不支持自动规范化缓存(即通过扁平化存储实体避免高层级数据重复)。 + +> **7 SWR 的不可变模式** - SWR 提供 "immutable" 模式允许查询在缓存生命周期内仅获取一次,但仍不具备过期时间概念或有条件的自动重新验证能力。 + +> **8 React Router 缓存持久化** - React Router 不会缓存当前匹配路由之外的数据。离开路由后,其数据即丢失。 + +[bpl-react-query]: https://bundlephobia.com/result?p=react-query +[bp-react-query]: https://badgen.net/bundlephobia/minzip/react-query?label=💾 +[gh-react-query]: https://github.com/tannerlinsley/react-query +[stars-react-query]: https://img.shields.io/github/stars/tannerlinsley/react-query?label=%F0%9F%8C%9F +[swr]: https://github.com/vercel/swr +[bp-swr]: https://badgen.net/bundlephobia/minzip/swr?label=💾 +[gh-swr]: https://github.com/vercel/swr +[stars-swr]: https://img.shields.io/github/stars/vercel/swr?label=%F0%9F%8C%9F +[bpl-swr]: https://bundlephobia.com/result?p=swr +[apollo]: https://github.com/apollographql/apollo-client +[bp-apollo]: https://badgen.net/bundlephobia/minzip/@apollo/client?label=💾 +[gh-apollo]: https://github.com/apollographql/apollo-client +[stars-apollo]: https://img.shields.io/github/stars/apollographql/apollo-client?label=%F0%9F%8C%9F +[bpl-apollo]: https://bundlephobia.com/result?p=@apollo/client +[rtk-query]: https://redux-toolkit.js.org/rtk-query/overview +[rtk-query-comparison]: https://redux-toolkit.js.org/rtk-query/comparison +[rtk-query-bundle-size]: https://redux-toolkit.js.org/rtk-query/comparison#bundle-size +[bp-rtk]: https://badgen.net/bundlephobia/minzip/@reduxjs/toolkit?label=💾 +[bp-rtk-query]: https://badgen.net/bundlephobia/minzip/@reduxjs/toolkit?label=💾 +[gh-rtk-query]: https://github.com/reduxjs/redux-toolkit +[stars-rtk-query]: https://img.shields.io/github/stars/reduxjs/redux-toolkit?label=🌟 +[bpl-rtk]: https://bundlephobia.com/result?p=@reduxjs/toolkit +[bpl-rtk-query]: https://bundlephobia.com/package/@reduxjs/toolkit +[react-router]: https://github.com/remix-run/react-router +[bp-react-router]: https://badgen.net/bundlephobia/minzip/react-router-dom?label=💾 +[gh-react-router]: https://github.com/remix-run/react-router +[stars-react-router]: https://img.shields.io/github/stars/remix-run/react-router?label=%F0%9F%8C%9F +[bpl-react-router]: https://bundlephobia.com/result?p=react-router-dom +[bp-history]: https://badgen.net/bundlephobia/minzip/history?label=💾 +[bpl-history]: https://bundlephobia.com/result?p=history diff --git a/docs/zh-hans/framework/react/devtools.md b/docs/zh-hans/framework/react/devtools.md new file mode 100644 index 00000000000..96a59607710 --- /dev/null +++ b/docs/zh-hans/framework/react/devtools.md @@ -0,0 +1,197 @@ +--- +source-updated-at: '2024-11-04T06:38:47.000Z' +translation-updated-at: '2025-05-06T04:27:37.043Z' +id: devtools +title: 开发者工具 +--- +# Devtools + +欢呼雀跃吧,因为 React Query 配备了专属开发者工具!🥳 + +当你开始 React Query 之旅时,这些开发者工具将成为得力助手。它们能可视化 React Query 的内部运作机制,在你遇到棘手问题时,很可能为你节省数小时的调试时间! + +> 请注意:目前开发者工具 **暂不支持 React Native**。若您有意协助我们实现跨平台支持,请随时告知! + +> 激动消息:我们现已推出独立的 React Native React Query DevTools 包!这一新增功能提供原生支持,让你可以直接在 React Native 项目中集成开发者工具。立即查看并参与贡献:[react-native-react-query-devtools](https://github.com/LovesWorking/react-native-react-query-devtools) + +> 另有一款外部工具可通过仪表盘使用 React Query 开发者工具。了解更多并参与贡献:[react-query-external-sync](https://github.com/LovesWorking/react-query-external-sync) + +> 注意:自第 5 版起,开发者工具已支持观察变更 (mutations)。 + +## 安装与导入开发者工具 + +开发者工具是独立包,需单独安装: + +```bash +npm i @tanstack/react-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/react-query-devtools +``` + +或 + +```bash +yarn add @tanstack/react-query-devtools +``` + +或 + +```bash +bun add @tanstack/react-query-devtools +``` + +对于 Next 13+ App 目录,必须将其作为开发依赖安装才能正常工作。 + +导入方式如下: + +```tsx +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +``` + +默认情况下,React Query 开发者工具仅在 `process.env.NODE_ENV === 'development'` 时包含在构建包中,因此无需担心生产环境构建时的排除问题。 + +## 浮动模式 + +浮动模式会将开发者工具作为固定浮动元素挂载在应用中,并在屏幕角落提供显示/隐藏开关。该开关状态会通过 localStorage 保存,并在页面刷新后保持记忆。 + +请将以下代码尽可能放置在 React 应用的顶层。越接近页面根节点,效果越好! + +```tsx +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' + +function App() { + return ( + + {/* 应用其他内容 */} + + + ) +} +``` + +### 配置项 + +- `initialIsOpen: Boolean` + - 设为 `true` 可使开发者工具默认展开 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` + - 默认为 `bottom-right` + - 控制 React Query 徽标按钮的位置,用于展开/收起面板 + - 设为 `relative` 时,按钮将出现在开发者工具的渲染位置 +- `position?: "top" | "bottom" | "left" | "right"` + - 默认为 `bottom` + - 开发者工具面板的展开位置 +- `client?: QueryClient`, + - 使用自定义 QueryClient。未指定时使用最近上下文中的实例 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}[]` + - 预定义可触发的错误类型。当从 UI 切换错误时,初始化器(传入特定查询)将被调用,必须返回一个 Error 对象 +- `styleNonce?: string` + - 向文档头部的 style 标签传递 nonce 值,适用于需要内容安全策略 (CSP) nonce 的场景 +- `shadowDOMTarget?: ShadowRoot` + - 默认行为是将样式应用到 DOM 的 head 标签 + - 传入 shadow DOM 目标可使样式在 shadow DOM 内生效而非 light DOM 的 head 标签 + +## 嵌入式模式 + +嵌入式模式将开发者工具作为固定元素显示在应用中,便于你在自有开发工具中使用我们的面板。 + +请将以下代码尽可能放置在 React 应用的顶层: + +```tsx +import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools' + +function App() { + const [isOpen, setIsOpen] = React.useState(false) + + return ( + + {/* 应用其他内容 */} + + {isOpen && setIsOpen(false)} />} + + ) +} +``` + +### 配置项 + +- `style?: React.CSSProperties` + - 面板自定义样式 + - 默认值:`{ height: '500px' }` + - 示例:`{ height: '100%' }` + - 示例:`{ height: '100%', width: '100%' }` +- `onClose?: () => unknown` + - 面板关闭时的回调函数 +- `client?: QueryClient`, + - 使用自定义 QueryClient。未指定时使用最近上下文中的实例 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}[]` + - 预定义可触发的错误类型 +- `styleNonce?: string` + - 向文档头部 style 标签传递 nonce 值 +- `shadowDOMTarget?: ShadowRoot` + - 控制样式作用域为 shadow DOM 而非 light DOM + +## 生产环境使用开发者工具 + +开发者工具默认排除在生产构建外。但可通过懒加载方式在生产环境使用: + +```tsx +import * as React from 'react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { Example } from './Example' + +const queryClient = new QueryClient() + +const ReactQueryDevtoolsProduction = React.lazy(() => + import('@tanstack/react-query-devtools/build/modern/production.js').then( + (d) => ({ + default: d.ReactQueryDevtools, + }), + ), +) + +function App() { + const [showDevtools, setShowDevtools] = React.useState(false) + + React.useEffect(() => { + // @ts-expect-error + window.toggleDevtools = () => setShowDevtools((old) => !old) + }, []) + + return ( + + + + {showDevtools && ( + + + + )} + + ) +} + +export default App +``` + +调用 `window.toggleDevtools()` 将下载开发者工具包并显示。 + +### 现代打包工具 + +若打包工具支持包导出,可使用以下导入路径: + +```tsx +const ReactQueryDevtoolsProduction = React.lazy(() => + import('@tanstack/react-query-devtools/production').then((d) => ({ + default: d.ReactQueryDevtools, + })), +) +``` + +TypeScript 用户需在 tsconfig 中设置 `moduleResolution: 'nodenext'`,这要求 TypeScript 版本至少为 v4.7。 diff --git a/docs/zh-hans/framework/react/graphql.md b/docs/zh-hans/framework/react/graphql.md new file mode 100644 index 00000000000..03e221ef74d --- /dev/null +++ b/docs/zh-hans/framework/react/graphql.md @@ -0,0 +1,56 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:24:54.134Z' +id: graphql +title: GraphQL +--- +由于 React Query 的获取机制是基于 Promise 无感知构建的,你可以将 React Query 与任何异步数据获取客户端一起使用,包括 GraphQL! + +> 请注意,React Query 不支持规范化缓存。虽然绝大多数用户实际上并不需要规范化缓存,甚至从中获得的收益比他们想象的要少得多,但在极少数情况下可能需要它。因此,请务必先与我们确认,以确保这确实是您所需要的功能! + +[//]: # 'Codegen' + +## 类型安全与代码生成 + +React Query 结合 `graphql-request^5` 和 [GraphQL Code Generator](https://graphql-code-generator.com/) 使用,可以提供完全类型化的 GraphQL 操作: + +```tsx +import request from 'graphql-request' +import { useQuery } from '@tanstack/react-query' + +import { graphql } from './gql/gql' + +const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ ` + query allFilmsWithVariablesQuery($first: Int!) { + allFilms(first: $first) { + edges { + node { + id + title + } + } + } + } +`) + +function App() { + // `data` 是完全类型化的! + const { data } = useQuery({ + queryKey: ['films'], + queryFn: async () => + request( + 'https://swapi-graphql.netlify.app/.netlify/functions/index', + allFilmsWithVariablesQueryDocument, + // 变量也会进行类型检查! + { first: 10 }, + ), + }) + // ... +} +``` + +_你可以在 [代码仓库中找到完整示例](https://github.com/dotansimha/graphql-code-generator/tree/7c25c4eeb77f88677fd79da557b7b5326e3f3950/examples/front-end/react/tanstack-react-query)_ + +请参考 [GraphQL Code Generator 文档中的专用指南](https://www.the-guild.dev/graphql/codegen/docs/guides/react-vue) 开始使用。 + +[//]: # 'Codegen' diff --git a/docs/zh-hans/framework/react/guides/advanced-ssr.md b/docs/zh-hans/framework/react/guides/advanced-ssr.md new file mode 100644 index 00000000000..70a8f29277c --- /dev/null +++ b/docs/zh-hans/framework/react/guides/advanced-ssr.md @@ -0,0 +1,414 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:16:25.641Z' +id: advanced-ssr +title: 高级服务端渲染 +--- +欢迎阅读高级服务端渲染 (Advanced Server Rendering) 指南,这里您将全面了解如何在流式渲染、服务器组件 (Server Components) 和 Next.js 应用路由 (app router) 中使用 React Query。 + +建议先阅读[服务端渲染与注水 (Server Rendering & Hydration)](./ssr.md) 指南了解 SSR 基础,以及[性能与请求瀑布流 (Performance & Request Waterfalls)](./request-waterfalls.md) 和[预取与路由集成 (Prefetching & Router Integration)](./prefetching.md) 获取背景知识。 + +首先请注意,虽然 SSR 指南中提到的 `initialData` 方式也适用于服务器组件,但本指南将重点介绍注水 (hydration) API。 + +## 服务器组件与 Next.js 应用路由 + +我们不会深入探讨服务器组件,简而言之它们是**保证仅在服务端运行**的组件,既在初始页面加载时,也**在页面切换时**运行。这与 Next.js 的 `getServerSideProps`/`getStaticProps` 和 Remix 的 `loader` 类似,但服务器组件不仅能返回数据,还能执行更多操作。不过数据部分仍是 React Query 的核心关注点。 + +如何将服务端渲染指南中[通过框架加载器预取数据](./ssr.md#using-the-hydration-apis)的知识应用到服务器组件和 Next.js 应用路由?最佳思路是将服务器组件视为"另一种"框架加载器。 + +### 术语说明 + +目前我们一直使用_服务端_和_客户端_这两个术语。需要注意的是,这与_服务器组件_和_客户端组件_并非一一对应。服务器组件保证只在服务端运行,但客户端组件实际上可以在两端运行,因为它们也会在初始_服务端渲染_阶段执行。 + +可以理解为:服务器组件的渲染发生在"加载阶段"(始终在服务端),而客户端组件运行在"应用阶段"。这个应用阶段既可能在 SSR 时运行于服务端,也可能在浏览器中运行。具体运行位置和是否参与 SSR 取决于不同框架的实现。 + +### 初始设置 + +React Query 的第一步总是创建 `queryClient` 并用 `QueryClientProvider` 包裹应用。在服务器组件中的设置各框架大体相似,主要区别在于文件命名约定: + +```tsx +// Next.js 中该文件名为: app/providers.tsx +'use client' + +// 由于 QueryClientProvider 底层依赖 useContext,必须在顶部声明 'use client' +import { + isServer, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // SSR 时通常需要设置默认 staleTime + // 大于 0 以避免客户端立即重新获取 + staleTime: 60 * 1000, + }, + }, + }) +} + +let browserQueryClient: QueryClient | undefined = undefined + +function getQueryClient() { + if (isServer) { + // 服务端:总是创建新 query client + return makeQueryClient() + } else { + // 浏览器:如果没有则创建新 query client + // 这非常重要,避免在初始渲染时 React 挂起导致重复创建 + if (!browserQueryClient) browserQueryClient = makeQueryClient() + return browserQueryClient + } +} + +export default function Providers({ children }: { children: React.ReactNode }) { + // 注意:如果没有 suspense 边界,应避免使用 useState 初始化 query client + // 因为 React 会在初始挂起渲染时丢弃该 client + const queryClient = getQueryClient() + + return ( + {children} + ) +} +``` + +```tsx +// Next.js 中该文件名为: app/layout.tsx +import Providers from './providers' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + {children} + + + ) +} +``` + +这与 SSR 指南中的做法非常相似,只是需要将代码拆分到两个文件中。 + +### 数据预取与脱水/注水 + +接下来看看如何在**Next.js 页面路由**中预取数据并进行脱水/注水: + +```tsx +// pages/posts.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, + useQuery, +} from '@tanstack/react-query' + +// 这里也可以是 getServerSideProps +export async function getStaticProps() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} + +function Posts() { + // 这个 useQuery 也可以放在 的深层子组件中 + // 注意这里使用 useQuery 而非 useSuspenseQuery + const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts }) + + // 这个查询未在服务端预取,会在客户端才开始获取 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} + +export default function PostsRoute({ dehydratedState }) { + return ( + + + + ) +} +``` + +转换为应用路由后代码非常相似,只需稍作调整。首先创建服务器组件处理预取: + +```tsx +// app/posts/page.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Posts from './posts' + +export default async function PostsPage() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + // 注:HydrationBoundary 是客户端组件,注水将在那里发生 + + + + ) +} +``` + +客户端组件部分如下: + +```tsx +// app/posts/posts.tsx +'use client' + +export default function Posts() { + // 这个 useQuery 也可以放在深层子组件中 + const { data } = useQuery({ + queryKey: ['posts'], + queryFn: () => getPosts(), + }) + + // 这个查询未在服务端预取,会在客户端才开始获取 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} +``` + +这些示例的巧妙之处在于,除了文件名外其他代码在任何支持服务器组件的框架中都通用。 + +在 SSR 指南中我们提到可以省略每个路由中的 `` 样板代码,但这在服务器组件中不可行。 + +> 注意:如果在 TypeScript 低于 `5.1.3` 或 `@types/react` 低于 `18.2.8` 版本中使用异步服务器组件遇到类型错误,建议升级到最新版本。临时解决方案是在调用组件时添加 `{/* @ts-expect-error Server Component */}`。详见 Next.js 13 文档中的[异步服务器组件类型错误](https://nextjs.org/docs/app/building-your-application/configuring/typescript#async-server-component-typescript-error)。 + +> 注意:如果遇到错误 `Only plain objects, and a few built-ins, can be passed to Server Actions. Classes or null prototypes are not supported.`,请确保没有向 queryFn 传递函数引用,而应调用函数,因为 queryFn 参数包含许多不可序列化的属性。参见[服务器操作仅在 queryFn 非引用时有效](https://github.com/TanStack/query/issues/6264)。 + +### 嵌套服务器组件 + +服务器组件的优势在于可以嵌套在 React 树的多个层级,使得数据预取更接近实际使用位置。简单示例如下(省略客户端组件): + +```tsx +// app/posts/page.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Posts from './posts' +import CommentsServerComponent from './comments-server' + +export default async function PostsPage() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + + + + + ) +} + +// app/posts/comments-server.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Comments from './comments' + +export default async function CommentsServerComponent() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + return ( + + + + ) +} +``` + +可以多次使用 `` 并创建多个 `queryClient` 进行预取。注意由于在渲染 `CommentsServerComponent` 前等待 `getPosts`,会导致服务端瀑布流: + +``` +1. |> getPosts() +2. |> getComments() +``` + +在 Next.js 中,除了在 `page.tsx` 预取数据,还可以在 `layout.tsx` 和[并行路由](https://nextjs.org/docs/app/building-your-application/routing/parallel-routes)中进行,Next.js 会自动并行获取这些路由数据。 + +### 替代方案:使用单一 queryClient + +虽然推荐为每个服务器组件创建新的 `queryClient`,但也可以选择复用单个实例: + +```tsx +// app/getQueryClient.tsx +import { QueryClient } from '@tanstack/react-query' +import { cache } from 'react' + +// cache() 按请求作用域,避免请求间数据泄漏 +const getQueryClient = cache(() => new QueryClient()) +export default getQueryClient +``` + +这样可以在任何服务器组件调用的地方获取该客户端,但每次 `dehydrate()` 都会序列化整个 `queryClient`,包括已序列化的无关查询。如果框架不会自动去重请求,这种方式可能更合适。 + +未来可能引入 `dehydrateNew()` 函数仅序列化新增查询,欢迎参与贡献。 + +### 数据所有权与重新验证 + +在服务器组件中使用时需注意数据所有权问题。例如: + +```tsx +// app/posts/page.tsx +export default async function PostsPage() { + const queryClient = new QueryClient() + const posts = await queryClient.fetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + +
    文章数: {posts.length}
    + +
    + ) +} +``` + +当客户端重新验证数据时,服务组件中的 `文章数` 不会更新。如果设置 `staleTime: Infinity` 可以避免此问题,但这样就失去了使用 React Query 的意义。 + +适合使用 React Query 与服务器组件的场景: +- 已有 React Query 应用需要迁移到服务器组件 +- 需要结合服务器组件优势的特定用例 +- 框架提供的数据获取工具无法满足需求 + +**新建服务器组件应用时,建议先使用框架提供的数据获取工具,确有需要再引入 React Query。** + +## 服务器组件的流式渲染 + +Next.js 应用路由会自动流式传输已准备好的内容。使用上述预取模式时,React Query 完全兼容这种流式渲染。从 v5.40.0 开始,甚至可以脱水待处理查询,实现数据流式传输。 + +需要调整 `queryClient` 配置以包含待处理查询: + +```tsx +// app/get-query-client.ts +import { QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query' + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + dehydrate: { + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === 'pending', + }, + }, + }) +} +``` + +然后在页面组件中无需等待预取: + +```tsx +// app/posts/page.tsx +export default function PostsPage() { + const queryClient = getQueryClient() + queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + + + + ) +} +``` + +客户端组件可以使用 `useSuspenseQuery` 获取这些数据: + +```tsx +// app/posts/posts.tsx +'use client' + +export default function Posts() { + const { data } = useSuspenseQuery({ queryKey: ['posts'], queryFn: getPosts }) + // ... +} +``` + +如需处理非 JSON 数据类型,可以配置序列化/反序列化选项: + +```tsx +// 配置序列化选项 +dehydrate: { + serializeData: serialize, +}, +hydrate: { + deserializeData: deserialize, +} +``` + +## Next.js 实验性无预取流式渲染 + +虽然推荐上述预取方案,但也可以通过实验性包 `@tanstack/react-query-next-experimental` 实现无预取的流式 SSR。使用 `ReactQueryStreamedHydration` 包裹应用: + +```tsx +// app/providers.tsx +'use client' + +import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental' + +export function Providers({ children }) { + const queryClient = getQueryClient() + return ( + + + {children} + + + ) +} +``` + +这种方式简化了代码但会导致页面导航时的请求瀑布流。适合更注重开发体验而非性能的场景。 + +## 结语 + +服务器组件和流式渲染仍是较新的概念,我们仍在探索如何优化 React Query 的集成。欢迎提供建议和反馈!同样,这份指南也在不断完善中,如果您发现遗漏或有改进建议,欢迎通过 GitHub 提交修改。 diff --git a/docs/zh-hans/framework/react/guides/background-fetching-indicators.md b/docs/zh-hans/framework/react/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..98a88b04b22 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/background-fetching-indicators.md @@ -0,0 +1,63 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:14:14.853Z' +id: background-fetching-indicators +title: 后台获取指示器 +--- +# 后台获取指示器 + +查询的 `status === 'pending'` 状态足以显示查询的初始硬加载状态,但有时您可能希望额外显示一个指示器,表明查询正在后台重新获取。为此,查询还提供了一个 `isFetching` 布尔值,您可以用它来显示查询正处于获取状态,而无论 `status` 变量的状态如何: + +[//]: # 'Example' + +```tsx +function Todos() { + const { + status, + data: todos, + error, + isFetching, + } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) + + return status === 'pending' ? ( + Loading... + ) : status === 'error' ? ( + Error: {error.message} + ) : ( + <> + {isFetching ?
    Refreshing...
    : null} + +
    + {todos.map((todo) => ( + + ))} +
    + + ) +} +``` + +[//]: # 'Example' + +## 显示全局后台获取加载状态 + +除了单个查询的加载状态外,如果您希望在**任何**查询获取时(包括在后台)显示全局加载指示器,可以使用 `useIsFetching` 钩子: + +[//]: # 'Example2' + +```tsx +import { useIsFetching } from '@tanstack/react-query' + +function GlobalLoadingIndicator() { + const isFetching = useIsFetching() + + return isFetching ? ( +
    Queries are fetching in the background...
    + ) : null +} +``` + +[//]: # 'Example2' diff --git a/docs/zh-hans/framework/react/guides/caching.md b/docs/zh-hans/framework/react/guides/caching.md new file mode 100644 index 00000000000..3c2e26d76f1 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/caching.md @@ -0,0 +1,40 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:14:25.884Z' +id: caching +title: 缓存 +--- +> 在阅读本指南前,请务必先通读[重要默认配置](./important-defaults.md) + +## 基础示例 + +这个缓存示例演示了以下场景及其生命周期: + +- 包含缓存数据与不包含缓存数据的查询实例 (Query Instances) +- 后台重新获取 (Background Refetching) +- 非活跃查询 (Inactive Queries) +- 垃圾回收 (Garbage Collection) + +假设我们使用默认的 `gcTime`(**5分钟**)和默认的 `staleTime`(`0`)。 + +1. 首次挂载 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })`: + - 由于此前没有使用 `['todos']` 查询键 (query key) 发起过其他查询,该查询会显示硬加载状态 (hard loading state) 并通过网络请求获取数据。 + - 当网络请求完成时,返回的数据会缓存在 `['todos']` 键下。 + - 在配置的 `staleTime`(默认为 `0`,即立即)后,数据会被标记为过时 (stale)。 + +2. 在其他位置挂载第二个 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例: + - 由于缓存中已存在第一个查询的 `['todos']` 键数据,会立即从缓存返回该数据。 + - 新实例会使用其查询函数触发新的网络请求。 + - 注意:无论两个 `fetchTodos` 查询函数是否相同,只要查询键相同,两个查询的 [`status`](../reference/useQuery.md)(包括 `isFetching`、`isPending` 等相关值)都会同步更新。 + - 当请求成功完成时,`['todos']` 键下的缓存数据会更新,两个实例都会接收到新数据。 + +3. 当两个 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例都卸载且不再使用时: + - 由于该查询没有活跃实例,会使用 `gcTime`(默认为 **5分钟**)设置垃圾回收超时,之后该查询将被删除并回收。 + +4. 在缓存超时完成前,再次挂载 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })`: + - 查询会立即返回可用的缓存数据,同时在后台执行 `fetchTodos` 函数。成功完成后会用新数据更新缓存。 + +5. 最后一个 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例卸载。 + +6. 在 **5分钟** 内没有出现新的 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例: + - `['todos']` 键下的缓存数据会被删除并进行垃圾回收。 diff --git a/docs/zh-hans/framework/react/guides/default-query-function.md b/docs/zh-hans/framework/react/guides/default-query-function.md new file mode 100644 index 00000000000..3a6a6b7930a --- /dev/null +++ b/docs/zh-hans/framework/react/guides/default-query-function.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:13:52.792Z' +id: default-query-function +title: 默认查询函数 +--- +如果你出于任何原因,希望在整个应用中共享同一个查询函数,仅通过查询键 (query key) 来标识应该获取的数据,可以通过为 TanStack Query 提供一个 **默认查询函数 (default query function)** 来实现: + +[//]: # '示例' + +```tsx +// 定义一个默认查询函数,它将接收查询键 +const defaultQueryFn = async ({ queryKey }) => { + const { data } = await axios.get( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + ) + return data +} + +// 通过 defaultOptions 将默认查询函数提供给整个应用 +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, + }, +}) + +function App() { + return ( + + + + ) +} + +// 现在你只需要传递一个键即可! +function Posts() { + const { status, data, error, isFetching } = useQuery({ queryKey: ['/posts'] }) + + // ... +} + +// 你甚至可以省略 queryFn,直接传入选项 +function Post({ postId }) { + const { status, data, error, isFetching } = useQuery({ + queryKey: [`/posts/${postId}`], + enabled: !!postId, + }) + + // ... +} +``` + +[//]: # '示例' + +如果你想覆盖默认的 queryFn,只需像平常那样提供自己的查询函数即可。 diff --git a/docs/zh-hans/framework/react/guides/dependent-queries.md b/docs/zh-hans/framework/react/guides/dependent-queries.md new file mode 100644 index 00000000000..41b74eba0c2 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/dependent-queries.md @@ -0,0 +1,96 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:14:02.726Z' +id: dependent-queries +title: 依赖查询 +--- +## useQuery 依赖查询 + +依赖(或串行)查询需要等待前一个查询完成才能执行。实现这一功能非常简单,只需使用 `enabled` 选项来指定查询何时可以运行: + +[//]: # '示例' + +```tsx +// 获取用户信息 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, +}) + +const userId = user?.id + +// 然后获取该用户的项目列表 +const { + status, + fetchStatus, + data: projects, +} = useQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + // 只有当 userId 存在时才会执行查询 + enabled: !!userId, +}) +``` + +[//]: # '示例' + +`projects` 查询的初始状态为: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'idle' +``` + +当 `user` 数据就绪后,`projects` 查询会被 `enabled` 并转为: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'fetching' +``` + +当项目数据获取完成后,状态将变为: + +```tsx +status: 'success' +isPending: false +fetchStatus: 'idle' +``` + +## useQueries 依赖查询 + +动态并行查询 - `useQueries` 也可以依赖前一个查询,实现方式如下: + +[//]: # '示例2' + +```tsx +// 获取用户ID列表 +const { data: userIds } = useQuery({ + queryKey: ['users'], + queryFn: getUsersData, + select: (users) => users.map((user) => user.id), +}) + +// 然后获取各用户的消息 +const usersMessages = useQueries({ + queries: userIds + ? userIds.map((id) => { + return { + queryKey: ['messages', id], + queryFn: () => getMessagesByUsers(id), + } + }) + : [], // 如果users未定义,则返回空数组 +}) +``` + +[//]: # '示例2' + +**注意** `useQueries` 返回的是**查询结果数组** + +## 性能说明 + +依赖查询本质上构成了[请求瀑布流](./request-waterfalls.md),会影响性能。假设两个查询耗时相同,串行执行总是比并行执行多花一倍时间,这在客户端高延迟环境下尤其不利。如果可能,最好重构后端API使两个查询能并行获取,尽管这并非总是可行。 + +在上面的例子中,与其先获取`getUserByEmail`才能执行`getProjectsByUser`,不如新增一个`getProjectsByUserEmail`查询来消除瀑布流。 diff --git a/docs/zh-hans/framework/react/guides/disabling-queries.md b/docs/zh-hans/framework/react/guides/disabling-queries.md new file mode 100644 index 00000000000..b3ae549bac9 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/disabling-queries.md @@ -0,0 +1,129 @@ +--- +source-updated-at: '2025-01-10T12:49:47.000Z' +translation-updated-at: '2025-05-06T04:14:23.656Z' +id: disabling-queries +title: 禁用/暂停查询 +--- +若需阻止查询自动执行,可通过设置 `enabled = false` 选项实现。该选项也支持传入返回布尔值的回调函数。 + +当 `enabled` 为 `false` 时: + +- 若查询存在缓存数据,则初始化状态为 `status === 'success'` 或 `isSuccess` +- 若查询无缓存数据,则初始化状态为 `status === 'pending'` 且 `fetchStatus === 'idle'` +- 查询不会在挂载时自动触发 +- 查询不会在后台自动重新获取 +- 查询会忽略 query client 的 `invalidateQueries` 和 `refetchQueries` 调用(这些调用通常会导致查询重新获取) +- 通过 `useQuery` 返回的 `refetch` 可手动触发查询,但无法与 `skipToken` 配合使用 + +> TypeScript 用户可选用 [skipToken](#typesafe-disabling-of-queries-using-skiptoken) 作为 `enabled = false` 的替代方案 + +[//]: # '示例' + +```tsx +function Todos() { + const { isLoading, isError, data, error, refetch, isFetching } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + enabled: false, + }) + + return ( +
    + + + {data ? ( + <> +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + + ) : isError ? ( + Error: {error.message} + ) : isLoading ? ( + Loading... + ) : ( + Not ready ... + )} + +
    {isFetching ? 'Fetching...' : null}
    +
    + ) +} +``` + +[//]: # '示例' + +永久禁用查询会使你失去 TanStack Query 的许多优秀特性(如后台重新获取),这也不符合惯用模式。这会让你从声明式模式(定义查询执行依赖)退化为命令式模式(点击按钮时才获取)。此外也无法通过 `refetch` 传递参数。多数情况下,你需要的只是一个延迟初始获取的惰性查询: + +## 惰性查询 + +`enabled` 选项不仅能永久禁用查询,还能实现动态启用/禁用。典型场景是筛选表单——仅当用户输入筛选值后才发起首次请求: + +[//]: # '示例2' + +```tsx +function Todos() { + const [filter, setFilter] = React.useState('') + + const { data } = useQuery({ + queryKey: ['todos', filter], + queryFn: () => fetchTodos(filter), + // ⬇️ 当筛选值为空时禁用查询 + enabled: !!filter, + }) + + return ( +
    + // 🚀 应用筛选条件将启用并执行查询 + + {data && } +
    + ) +} +``` + +[//]: # '示例2' + +### isLoading (原 `isInitialLoading`) + +惰性查询会始终处于 `status: 'pending'` 状态,因为 `pending` 表示尚无数据。虽然技术上是正确的,但由于当前并未获取数据(查询未被 _启用_),这个标志位通常不能用于显示加载指示器。 + +若使用禁用或惰性查询,可改用 `isLoading` 标志位。这是一个衍生标志,由以下公式计算: + +`isPending && isFetching` + +因此仅当查询首次获取数据时才会为 `true` + +## 使用 `skipToken` 实现类型安全的查询禁用 + +TypeScript 用户可使用 `skipToken` 禁用查询。适用于需要基于条件禁用查询,同时保持类型安全的场景。 + +> 重要提示:`useQuery` 返回的 `refetch` 无法与 `skipToken` 配合使用。除此之外,`skipToken` 与 `enabled: false` 行为一致 + +[//]: # '示例3' + +```tsx +import { skipToken, useQuery } from '@tanstack/react-query' + +function Todos() { + const [filter, setFilter] = React.useState() + + const { data } = useQuery({ + queryKey: ['todos', filter], + // ⬇️ 当 filter 为 undefined 或空时禁用查询 + queryFn: filter ? () => fetchTodos(filter) : skipToken, + }) + + return ( +
    + // 🚀 应用筛选条件将启用并执行查询 + + {data && } +
    + ) +} +``` + +[//]: # '示例3' diff --git a/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..a6076c6f51c --- /dev/null +++ b/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:14:00.417Z' +id: does-this-replace-client-state +title: '这会取代 [Redux, MobX 等] 吗?' +--- +首先,我们需要明确几个关键点: + +- TanStack Query 是一个**服务端状态 (server-state)** 库,负责管理服务器与客户端之间的异步操作 +- Redux、MobX、Zustand 等属于**客户端状态 (client-state)** 库,它们_虽然可以用来存储异步数据,但与 TanStack Query 这类工具相比效率较低_ + +基于以上认知,简短的答案是:TanStack Query **取代了那些用于管理客户端状态中缓存数据的样板代码和相关连接逻辑,仅需寥寥数行代码即可实现相同功能**。 + +对于绝大多数应用而言,当把所有异步代码迁移到 TanStack Query 后,剩余的真正**需要全局访问的客户端状态**通常非常少。 + +> 当然也存在特殊情况:某些应用可能确实拥有大量同步的纯客户端状态(例如可视化设计工具或音乐制作软件),这种情况下您可能仍需要客户端状态管理工具。此时需要注意——**TanStack Query 并不能替代本地/客户端状态管理**。不过您可以毫无障碍地将其与大多数客户端状态管理器配合使用。 + +## 示例说明 + +假设我们有以下通过全局状态库管理的"全局"状态: + +```tsx +const globalState = { + projects, + teams, + tasks, + users, + themeMode, + sidebarStatus, +} +``` + +当前全局状态管理器缓存了4类服务端状态:`projects`、`teams`、`tasks` 和 `users`。如果将这些服务端状态迁移到 TanStack Query,剩余的全局状态将简化为: + +```tsx +const globalState = { + themeMode, + sidebarStatus, +} +``` + +这意味着通过调用 `useQuery` 和 `useMutation` 等钩子,我们还能移除所有用于管理服务端状态的样板代码,例如: + +- 连接器 (Connectors) +- 动作创建器 (Action Creators) +- 中间件 (Middlewares) +- 归约器 (Reducers) +- 加载中/错误/结果状态 (Loading/Error/Result states) +- 上下文 (Contexts) + +移除这些内容后,您可能会思考:**"是否还有必要为这点儿全局状态继续使用客户端状态管理器?"** + +**这完全取决于您!** + +但 TanStack Query 的定位非常明确——它能消除应用中所有异步连接的样板代码,仅需少量代码即可替代。 + +还在等什么?赶快试试看吧! diff --git a/docs/zh-hans/framework/react/guides/filters.md b/docs/zh-hans/framework/react/guides/filters.md new file mode 100644 index 00000000000..4f5bc799520 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/filters.md @@ -0,0 +1,91 @@ +--- +source-updated-at: '2025-01-26T18:24:42.000Z' +translation-updated-at: '2025-05-06T04:14:12.171Z' +id: filters +title: 过滤器 +--- +TanStack Query 中的部分方法接受 `QueryFilters` 或 `MutationFilters` 对象作为参数。 + +## `查询过滤器 (Query Filters)` + +查询过滤器 (Query Filter) 是一个包含特定匹配条件的对象,用于筛选查询: + +```tsx +// 取消所有查询 +await queryClient.cancelQueries() + +// 移除所有键名以 `posts` 开头的非活跃 (inactive) 查询 +queryClient.removeQueries({ queryKey: ['posts'], type: 'inactive' }) + +// 重新获取所有活跃 (active) 查询 +await queryClient.refetchQueries({ type: 'active' }) + +// 重新获取所有键名以 `posts` 开头的活跃 (active) 查询 +await queryClient.refetchQueries({ queryKey: ['posts'], type: 'active' }) +``` + +查询过滤器对象支持以下属性: + +- `queryKey?: QueryKey` + - 设置此属性可定义需要匹配的查询键 (query key)。 +- `exact?: boolean` + - 若不需要通过查询键 (query key) 进行包容性搜索,可传递 `exact: true` 选项,仅返回与指定查询键完全匹配的查询。 +- `type?: 'active' | 'inactive' | 'all'` + - 默认值为 `all` + - 设置为 `active` 时匹配活跃 (active) 查询。 + - 设置为 `inactive` 时匹配非活跃 (inactive) 查询。 +- `stale?: boolean` + - 设置为 `true` 时匹配过时 (stale) 查询。 + - 设置为 `false` 时匹配新鲜 (fresh) 查询。 +- `fetchStatus?: FetchStatus` + - 设置为 `fetching` 时匹配当前正在获取 (fetching) 的查询。 + - 设置为 `paused` 时匹配希望获取但被 `暂停 (paused)` 的查询。 + - 设置为 `idle` 时匹配未在获取数据的查询。 +- `predicate?: (query: Query) => boolean` + - 此谓词函数将作为对所有匹配查询的最终筛选条件。如果未指定其他过滤器,该函数将针对缓存中的每个查询进行评估。 + +## `变更过滤器 (Mutation Filters)` + +变更过滤器 (Mutation Filter) 是一个包含特定匹配条件的对象,用于筛选变更: + +```tsx +// 获取所有正在获取的变更数量 +await queryClient.isMutating() + +// 通过变更键 (mutationKey) 筛选变更 +await queryClient.isMutating({ mutationKey: ['post'] }) + +// 使用谓词函数筛选变更 +await queryClient.isMutating({ + predicate: (mutation) => mutation.state.variables?.id === 1, +}) +``` + +变更过滤器对象支持以下属性: + +- `mutationKey?: MutationKey` + - 设置此属性可定义需要匹配的变更键 (mutation key)。 +- `exact?: boolean` + - 若不需要通过变更键 (mutation key) 进行包容性搜索,可传递 `exact: true` 选项,仅返回与指定变更键完全匹配的变更。 +- `status?: MutationStatus` + - 允许根据变更状态进行筛选。 +- `predicate?: (mutation: Mutation) => boolean` + - 此谓词函数将作为对所有匹配变更的最终筛选条件。如果未指定其他过滤器,该函数将针对缓存中的每个变更进行评估。 + +## 工具函数 (Utils) + +### `matchQuery` + +```tsx +const isMatching = matchQuery(filters, query) +``` + +返回一个布尔值,指示查询是否匹配提供的查询过滤器集合。 + +### `matchMutation` + +```tsx +const isMatching = matchMutation(filters, mutation) +``` + +返回一个布尔值,指示变更是否匹配提供的变更过滤器集合。 diff --git a/docs/zh-hans/framework/react/guides/important-defaults.md b/docs/zh-hans/framework/react/guides/important-defaults.md new file mode 100644 index 00000000000..c477560dca5 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/important-defaults.md @@ -0,0 +1,43 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:12:55.170Z' +id: important-defaults +title: 重要默认设置 +--- +开箱即用,TanStack Query 采用了**激进但合理**的默认配置。**这些默认设置有时会让新用户措手不及,如果用户不了解它们,可能会增加学习或调试的难度。**在继续学习和使用 TanStack Query 时,请牢记以下要点: + +- 通过 `useQuery` 或 `useInfiniteQuery` 创建的查询实例默认**将缓存数据视为过时数据**。 + +> 若要修改此行为,可通过全局配置或单查询配置使用 `staleTime` 选项。设置较长的 `staleTime` 意味着查询不会频繁重新获取数据。 + +- 当满足以下条件时,过时查询会在后台自动重新获取数据: + - 新的查询实例挂载 + - 窗口重新获得焦点 + - 网络重新连接 + - 查询配置了可选的 refetch 间隔时间 + +> 可通过 `refetchOnMount`、`refetchOnWindowFocus`、`refetchOnReconnect` 和 `refetchInterval` 等选项调整此功能。 + +- 当 `useQuery`、`useInfiniteQuery` 或查询观察者不再有活跃实例时,对应的查询结果会被标记为"非活跃"状态,并保留在缓存中供后续使用。 +- 默认情况下,"非活跃"查询会在 **5 分钟后**被垃圾回收。 + + > 可通过修改默认的 `gcTime`(当前为 `1000 * 60 * 5` 毫秒)来调整此设置。 + +- 失败的查询会**静默重试 3 次**,采用指数退避延迟策略,之后才会捕获错误并显示到用户界面。 + + > 可通过修改默认的 `retry`(重试次数)和 `retryDelay`(退避延迟函数)选项来调整此行为。 + +- 默认情况下,查询结果会进行**结构共享比较以检测数据是否实际变化**。如果数据未变化,则**保持数据引用不变**,从而更好地配合 useMemo 和 useCallback 实现值稳定性。如果这个概念听起来陌生,无需担心!99.9% 的情况下您不需要禁用此功能,它能零成本提升应用性能。 + + > 结构共享仅适用于 JSON 兼容值,其他值类型始终会被视为已变化。例如,若因响应数据过大导致性能问题,可通过 `config.structuralSharing` 标志禁用此功能。若查询响应包含非 JSON 兼容值但仍需检测数据变化,可提供自定义函数作为 `config.structuralSharing`,根据新旧响应计算值并按需保持引用。 + +[//]: # 'Materials' + +## 扩展阅读 + +查看以下社区资源文章,深入了解默认配置的原理: + +- [React Query 实战指南](../community/tkdodos-blog.md#1-practical-react-query) +- [作为状态管理器的 React Query](../community/tkdodos-blog.md#10-react-query-as-a-state-manager) + +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/react/guides/infinite-queries.md b/docs/zh-hans/framework/react/guides/infinite-queries.md new file mode 100644 index 00000000000..14e85967558 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/infinite-queries.md @@ -0,0 +1,262 @@ +--- +source-updated-at: '2024-11-18T17:22:25.000Z' +translation-updated-at: '2025-05-06T04:13:56.367Z' +id: infinite-queries +title: 无限查询 +--- +## 无限查询 (Infinite Queries) + +能够以增量方式"加载更多"数据到现有数据集或实现"无限滚动"的列表渲染,是一种非常常见的 UI 模式。TanStack Query 为此提供了一个名为 `useInfiniteQuery` 的特殊版本 `useQuery` 来查询这类列表。 + +使用 `useInfiniteQuery` 时,您会注意到以下几点不同: + +- `data` 现在是一个包含无限查询数据的对象: + - `data.pages` 数组包含已获取的页面 + - `data.pageParams` 数组包含用于获取页面的参数 +- 现在可以使用 `fetchNextPage` 和 `fetchPreviousPage` 函数(`fetchNextPage` 是必需的) +- 新增了 `initialPageParam` 选项(且必需)用于指定初始页面参数 +- 新增了 `getNextPageParam` 和 `getPreviousPageParam` 选项,用于确定是否有更多数据要加载以及获取这些数据所需的信息。这些信息会作为查询函数的附加参数提供 +- 新增了 `hasNextPage` 布尔值,当 `getNextPageParam` 返回值不是 `null` 或 `undefined` 时为 `true` +- 新增了 `hasPreviousPage` 布尔值,当 `getPreviousPageParam` 返回值不是 `null` 或 `undefined` 时为 `true` +- 新增了 `isFetchingNextPage` 和 `isFetchingPreviousPage` 布尔值,用于区分后台刷新状态和加载更多状态 + +> 注意:选项 `initialData` 或 `placeholderData` 需要遵循与包含 `data.pages` 和 `data.pageParams` 属性的对象相同的结构。 + +## 示例 + +假设我们有一个 API,它基于 `cursor` 索引每次返回 3 个 `projects` 页面,同时返回可用于获取下一组项目的游标: + +```tsx +fetch('/api/projects?cursor=0') +// { data: [...], nextCursor: 3} +fetch('/api/projects?cursor=3') +// { data: [...], nextCursor: 6} +fetch('/api/projects?cursor=6') +// { data: [...], nextCursor: 9} +fetch('/api/projects?cursor=9') +// { data: [...] } +``` + +利用这些信息,我们可以通过以下方式创建一个"加载更多"的 UI: + +- 默认等待 `useInfiniteQuery` 请求第一组数据 +- 在 `getNextPageParam` 中返回下一个查询的信息 +- 调用 `fetchNextPage` 函数 + +[//]: # '示例' + +```tsx +import { useInfiniteQuery } from '@tanstack/react-query' + +function Projects() { + const fetchProjects = async ({ pageParam }) => { + const res = await fetch('/api/projects?cursor=' + pageParam) + return res.json() + } + + const { + data, + error, + fetchNextPage, + hasNextPage, + isFetching, + isFetchingNextPage, + status, + } = useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + }) + + return status === 'pending' ? ( +

    加载中...

    + ) : status === 'error' ? ( +

    错误: {error.message}

    + ) : ( + <> + {data.pages.map((group, i) => ( + + {group.data.map((project) => ( +

    {project.name}

    + ))} +
    + ))} +
    + +
    +
    {isFetching && !isFetchingNextPage ? '获取中...' : null}
    + + ) +} +``` + +[//]: # '示例' + +必须理解的是,在正在进行获取时调用 `fetchNextPage` 会有覆盖后台数据刷新的风险。当同时渲染列表和触发 `fetchNextPage` 时,这种情况变得尤为关键。 + +请记住,一个 InfiniteQuery 只能有一个正在进行的获取操作。所有页面共享一个缓存条目,尝试同时进行两次获取可能会导致数据覆盖。 + +如果您希望启用同时获取,可以在 `fetchNextPage` 中使用 `{ cancelRefetch: false }` 选项(默认为 true)。 + +为了确保无冲突的顺畅查询过程,强烈建议验证查询是否不处于 `isFetching` 状态,特别是当用户不会直接控制该调用时。 + +[//]: # '示例1' + +```jsx + !isFetchingNextPage && fetchNextPage()} /> +``` + +[//]: # '示例1' + +## 当无限查询需要重新获取时会发生什么? + +当无限查询变为 `stale` 并需要重新获取时,每组数据会从第一组开始`顺序`获取。这确保了即使底层数据发生变更,我们也不会使用过期的游标,从而避免获取重复记录或跳过记录。如果无限查询的结果从 queryCache 中移除,分页将从初始状态重新开始,仅请求初始组。 + +## 如何实现双向无限列表? + +双向列表可以通过使用 `getPreviousPageParam`、`fetchPreviousPage`、`hasPreviousPage` 和 `isFetchingPreviousPage` 属性和函数来实现。 + +[//]: # '示例3' + +```tsx +useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, +}) +``` + +[//]: # '示例3' + +## 如何以倒序显示页面? + +有时您可能希望以倒序显示页面。这种情况下,可以使用 `select` 选项: + +[//]: # '示例4' + +```tsx +useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), +}) +``` + +[//]: # '示例4' + +## 如何手动更新无限查询? + +### 手动移除第一页: + +[//]: # '示例5' + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1), +})) +``` + +[//]: # '示例5' + +### 手动从单个页面中移除某个值: + +[//]: # '示例6' + +```tsx +const newPagesArray = + oldPagesArray?.pages.map((page) => + page.filter((val) => val.id !== updatedId), + ) ?? [] + +queryClient.setQueryData(['projects'], (data) => ({ + pages: newPagesArray, + pageParams: data.pageParams, +})) +``` + +[//]: # '示例6' + +### 仅保留第一页: + +[//]: # '示例7' + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(0, 1), + pageParams: data.pageParams.slice(0, 1), +})) +``` + +[//]: # '示例7' + +确保始终保持 pages 和 pageParams 的相同数据结构! + +## 如何限制页面数量? + +在某些用例中,您可能希望限制查询数据中存储的页面数量以提高性能和用户体验: + +- 当用户可以加载大量页面时(内存使用) +- 当需要重新获取包含数十个页面的无限查询时(网络使用:所有页面都会按顺序获取) + +解决方案是使用"有限无限查询"。这可以通过将 `maxPages` 选项与 `getNextPageParam` 和 `getPreviousPageParam` 结合使用来实现,以便在需要时双向获取页面。 + +在以下示例中,查询数据 pages 数组中仅保留 3 页。如果需要重新获取,只会按顺序重新获取 3 页。 + +[//]: # '示例8' + +```tsx +useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, + maxPages: 3, +}) +``` + +[//]: # '示例8' + +## 如果我的 API 不返回游标怎么办? + +如果您的 API 不返回游标,可以将 `pageParam` 用作游标。因为 `getNextPageParam` 和 `getPreviousPageParam` 也会获取当前页面的 `pageParam`,所以可以用它来计算下一个/上一个页面参数。 + +[//]: # '示例9' + +```tsx +return useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, allPages, lastPageParam) => { + if (lastPage.length === 0) { + return undefined + } + return lastPageParam + 1 + }, + getPreviousPageParam: (firstPage, allPages, firstPageParam) => { + if (firstPageParam <= 1) { + return undefined + } + return firstPageParam - 1 + }, +}) +``` + +[//]: # '示例9' diff --git a/docs/zh-hans/framework/react/guides/initial-query-data.md b/docs/zh-hans/framework/react/guides/initial-query-data.md new file mode 100644 index 00000000000..43194997a15 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/initial-query-data.md @@ -0,0 +1,175 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:13:31.868Z' +id: initial-query-data +title: 初始查询数据 +--- +在需要之前,有多种方式可以为查询缓存提供初始数据: + +- 声明式: + - 通过 `initialData` 为查询预填充空缓存 +- 命令式: + - [使用 `queryClient.prefetchQuery` 预取数据](./prefetching.md) + - [使用 `queryClient.setQueryData` 手动将数据存入缓存](./prefetching.md) + +## 使用 `initialData` 预填充查询 + +有时应用中已存在查询所需的初始数据,可直接提供给查询。此时可通过 `config.initialData` 选项设置查询初始数据,并跳过初始加载状态! + +> 重要提示:`initialData` 会持久化到缓存,因此不建议向该选项提供占位、部分或不完整数据,应改用 `placeholderData` + +[//]: # '示例' + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, +}) +``` + +[//]: # '示例' + +### `staleTime` 与 `initialDataUpdatedAt` + +默认情况下,`initialData` 被视为全新数据(如同刚获取的数据)。这会影响 `staleTime` 选项的解读方式: + +- 若为查询观察者配置 `initialData` 且未设置 `staleTime`(默认 `staleTime: 0`),挂载时会立即重新获取数据: + + [//]: # '示例2' + + ```tsx + // 立即显示 initialTodos,但挂载后会立即重新获取 todos + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + }) + ``` + + [//]: # '示例2' + +- 若配置 `initialData` 并设置 `staleTime` 为 `1000` 毫秒,数据在该时间段内将被视为新鲜数据(如同刚从查询函数获取): + + [//]: # '示例3' + + ```tsx + // 立即显示 initialTodos,但在 1000 毫秒后遇到交互事件前不会重新获取 + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 1000, + }) + ``` + + [//]: # '示例3' + +- 如果 `initialData` 并非完全新鲜?此时最准确的配置是使用 `initialDataUpdatedAt` 选项。该选项允许传入 JS 时间戳(毫秒)表示初始数据的最后更新时间(如 `Date.now()` 提供的值)。注意:若使用 Unix 时间戳需乘以 `1000` 转换为 JS 时间戳。 + + [//]: # '示例4' + + ```tsx + // 立即显示 initialTodos,但仅在 initialData 早于 staleTime 时挂载重新获取 + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 60 * 1000, // 1 分钟 + initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052 + }) + ``` + + [//]: # '示例4' + + 该选项使得 `staleTime` 能按其原始目的(确定数据需保持新鲜的时间)运作,同时允许在 `initialData` 早于 `staleTime` 时挂载重新获取。上例中数据需在 1 分钟内保持新鲜,通过提示初始数据的更新时间,查询能自行决定是否需要重新获取。 + + > 若希望将数据视为**预取数据**,建议使用 `prefetchQuery` 或 `fetchQuery` API 预先填充缓存,从而独立配置 `staleTime` 与初始数据 + +### 初始数据函数 + +若获取查询初始数据的操作开销较大或不想在每次渲染时执行,可传递函数作为 `initialData` 值。该函数仅在查询初始化时执行一次,节省内存和 CPU 资源: + +[//]: # '示例5' + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: () => getExpensiveTodos(), +}) +``` + +[//]: # '示例5' + +### 从缓存获取初始数据 + +某些情况下,可以从其他查询的缓存结果中提供初始数据。典型场景是从待办列表查询缓存中搜索单个待办项,并将其作为单个待办查询的初始数据: + +[//]: # '示例6' + +```tsx +const result = useQuery({ + queryKey: ['todo', todoId], + queryFn: () => fetch('/todos'), + initialData: () => { + // 使用 'todos' 查询中的待办项作为本查询的初始数据 + return queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId) + }, +}) +``` + +[//]: # '示例6' + +### 使用 `initialDataUpdatedAt` 从缓存获取初始数据 + +从缓存获取初始数据意味着源查询的数据可能已过时。建议将源查询的 `dataUpdatedAt` 传递给 `initialDataUpdatedAt`,而非使用人为的 `staleTime` 防止立即重新获取。这为查询实例提供了判断是否需要重新获取的全部信息: + +[//]: # '示例7' + +```tsx +const result = useQuery({ + queryKey: ['todos', todoId], + queryFn: () => fetch(`/todos/${todoId}`), + initialData: () => + queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId), + initialDataUpdatedAt: () => + queryClient.getQueryState(['todos'])?.dataUpdatedAt, +}) +``` + +[//]: # '示例7' + +### 有条件地从缓存获取初始数据 + +若用于查找初始数据的源查询已过时,可能希望完全不使用缓存数据而直接从服务器获取。可通过 `queryClient.getQueryState` 获取源查询的详细信息(包括 `state.dataUpdatedAt` 时间戳),据此判断数据是否足够新鲜: + +[//]: # '示例8' + +```tsx +const result = useQuery({ + queryKey: ['todo', todoId], + queryFn: () => fetch(`/todos/${todoId}`), + initialData: () => { + // 获取查询状态 + const state = queryClient.getQueryState(['todos']) + + // 如果查询存在且数据不早于 10 秒... + if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) { + // 返回单个待办项 + return state.data.find((d) => d.id === todoId) + } + + // 否则返回 undefined 并从硬加载状态获取! + }, +}) +``` + +[//]: # '示例8' +[//]: # '材料' + +## 延伸阅读 + +关于 `Initial Data` 与 `Placeholder Data` 的对比,请参阅[社区资源](../community/tkdodos-blog.md#9-placeholder-and-initial-data-in-react-query)。 + +[//]: # '材料' diff --git a/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..cc49cf5d950 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md @@ -0,0 +1,42 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:12:43.101Z' +id: invalidations-from-mutations +title: 从变更中失效 +--- +## 变更触发的失效机制 + +仅使查询失效只是成功的一半,而明确**何时**触发失效才是关键。通常,当应用中的某个变更 (mutation) 成功执行时,极有可能存在与之关联的查询需要被标记为失效状态,甚至重新获取数据以反映变更带来的新变化。 + +例如,假设我们有一个用于提交新待办事项的变更操作: + +[//]: # '示例' + +```tsx +const mutation = useMutation({ mutationFn: postTodo }) +``` + +[//]: # '示例' + +当 `postTodo` 变更成功执行后,我们通常希望所有 `todos` 查询都失效并重新获取,以展示新增的待办事项。为此,可以利用 `useMutation` 的 `onSuccess` 配置项配合查询客户端 (query client) 的 `invalidateQueries` 方法实现: + +[//]: # '示例2' + +```tsx +import { useMutation, useQueryClient } from '@tanstack/react-query' + +const queryClient = useQueryClient() + +// 当此变更成功时,使所有包含 `todos` 或 `reminders` 查询键的查询失效 +const mutation = useMutation({ + mutationFn: addTodo, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['todos'] }) + queryClient.invalidateQueries({ queryKey: ['reminders'] }) + }, +}) +``` + +[//]: # '示例2' + +您可以通过 [`useMutation` 钩子](./mutations.md) 提供的任意回调函数来配置失效触发逻辑。 diff --git a/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md b/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md new file mode 100644 index 00000000000..ddf589495b7 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md @@ -0,0 +1,541 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:15:18.467Z' +id: migrating-to-react-query-3 +title: 迁移到 v3 +--- +以下是翻译内容: + +React Query 的早期版本已经非常出色,为库带来了许多令人惊叹的新特性、更多魔法以及整体上更优的体验。它们也推动了该库的大规模采用,同时为库带来了大量优化建议(问题/贡献),并揭示了一些需要进一步打磨以使库更完善的地方。v3 版本正是这些打磨的成果。 + +## 概述 + +- 更具扩展性和可测试性的缓存配置 +- 更好的服务端渲染 (SSR) 支持 +- 随处可用的数据延迟(原 usePaginatedQuery)功能 +- 双向无限查询 (Infinite Queries) +- 查询数据选择器 (Query data selectors)! +- 使用前可完全配置查询和/或变更的默认选项 +- 更细粒度的可选渲染优化 +- 新增 `useQueries` 钩子!(支持可变长度的并行查询执行) +- `useIsFetching()` 钩子支持查询过滤器! +- 变更操作的重试/离线/重放支持 +- 在 React 外部观察查询/变更 +- 可在任意位置使用 React Query 的核心逻辑! +- 通过 `react-query/devtools` 集成的开发者工具 +- 缓存持久化至 web 存储(实验性功能,通过 `react-query/persistQueryClient-experimental` 和 `react-query/createWebStoragePersistor-experimental` 实现) + +## 重大变更 + +### `QueryCache` 已被拆分为 `QueryClient` 和底层的 `QueryCache` 及 `MutationCache` 实例 + +`QueryCache` 包含所有查询,`MutationCache` 包含所有变更,而 `QueryClient` 可用于设置配置并与它们交互。 + +这带来了一些优势: + +- 允许不同类型的缓存 +- 不同配置的多个客户端可以共享同一个缓存 +- 客户端可用于跟踪查询,这在 SSR 上实现共享缓存时非常有用 +- 客户端 API 更专注于通用场景 +- 更容易测试各个组件 + +创建 `new QueryClient()` 时,如果没有提供,则会自动创建 `QueryCache` 和 `MutationCache`。 + +```tsx +import { QueryClient } from 'react-query' + +const queryClient = new QueryClient() +``` + +### `ReactQueryConfigProvider` 和 `ReactQueryCacheProvider` 已被 `QueryClientProvider` 取代 + +现在可以在 `QueryClient` 中指定查询和变更的默认选项: + +**注意:现在使用 defaultOptions 而非 defaultConfig** + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // 查询选项 + }, + mutations: { + // 变更选项 + }, + }, +}) +``` + +`QueryClientProvider` 组件现在用于将 `QueryClient` 连接到你的应用: + +```tsx +import { QueryClient, QueryClientProvider } from 'react-query' + +const queryClient = new QueryClient() + +function App() { + return ... +} +``` + +### 默认的 `QueryCache` 已移除。这次是真的! + +如之前弃用通知所述,主包中不再创建或导出默认的 `QueryCache`。你必须通过 `new QueryClient()` 或 `new QueryCache()`(然后可以传递给 `new QueryClient({ queryCache })`)自行创建。 + +### 已移除废弃的 `makeQueryCache` 工具函数 + +虽然等待已久,但它终于被移除了 :) + +### `QueryCache.prefetchQuery()` 已移至 `QueryClient.prefetchQuery()` + +新的 `QueryClient.prefetchQuery()` 函数是异步的,但不会返回查询的数据。如果需要数据,请使用新的 `QueryClient.fetchQuery()` 函数。 + +```tsx +// 预取查询: +await queryClient.prefetchQuery('posts', fetchPosts) + +// 获取查询: +try { + const data = await queryClient.fetchQuery('posts', fetchPosts) +} catch (error) { + // 错误处理 +} +``` + +### `ReactQueryErrorResetBoundary` 和 `QueryCache.resetErrorBoundaries()` 已被 `QueryErrorResetBoundary` 和 `useQueryErrorResetBoundary()` 取代。 + +这些新功能提供了与之前相同的体验,但增加了选择要重置的组件树的控制能力。更多信息请参阅: + +- [QueryErrorResetBoundary](../reference/QueryErrorResetBoundary.md) +- [useQueryErrorResetBoundary](../reference/useQueryErrorResetBoundary.md) + +### `QueryCache.getQuery()` 已被 `QueryCache.find()` 取代 + +现在应使用 `QueryCache.find()` 从缓存中查找单个查询。 + +### `QueryCache.getQueries()` 已移至 `QueryCache.findAll()` + +现在应使用 `QueryCache.findAll()` 从缓存中查找多个查询。 + +### `QueryCache.isFetching` 已移至 `QueryClient.isFetching()` + +**注意:现在是一个函数而非属性** + +### `useQueryCache` 钩子已被 `useQueryClient` 钩子取代 + +它返回组件树中提供的 `queryClient`,除了重命名外基本无需调整。 + +### 查询键的部分不再自动展开到查询函数中 + +现在推荐使用内联函数将参数传递给查询函数: + +```tsx +// 旧方式 +useQuery(['post', id], (_key, id) => fetchPost(id)) + +// 新方式 +useQuery(['post', id], () => fetchPost(id)) +``` + +如果仍坚持不使用内联函数,可以使用新传递的 `QueryFunctionContext`: + +```tsx +useQuery(['post', id], (context) => fetchPost(context.queryKey[1])) +``` + +### 无限查询的页面参数现在通过 `QueryFunctionContext.pageParam` 传递 + +之前它们是作为查询函数的最后一个查询键参数添加的,但这在某些模式下被证明存在问题。 + +```tsx +// 旧方式 +useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam)) + +// 新方式 +useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam)) +``` + +### usePaginatedQuery() 已被移除,改用 `keepPreviousData` 选项 + +新的 `keepPreviousData` 选项可用于 `useQuery` 和 `useInfiniteQuery`,将对数据产生相同的"滞后"效果: + +```tsx +import { useQuery } from 'react-query' + +function Page({ page }) { + const { data } = useQuery(['page', page], fetchPage, { + keepPreviousData: true, + }) +} +``` + +### useInfiniteQuery() 现在支持双向操作 + +`useInfiniteQuery()` 接口已变更,完全支持双向无限列表。 + +- `options.getFetchMore` 已重命名为 `options.getNextPageParam` +- `queryResult.canFetchMore` 已重命名为 `queryResult.hasNextPage` +- `queryResult.fetchMore` 已重命名为 `queryResult.fetchNextPage` +- `queryResult.isFetchingMore` 已重命名为 `queryResult.isFetchingNextPage` +- 新增 `options.getPreviousPageParam` 选项 +- 新增 `queryResult.hasPreviousPage` 属性 +- 新增 `queryResult.fetchPreviousPage` 属性 +- 新增 `queryResult.isFetchingPreviousPage` +- 无限查询的 `data` 现在是一个包含 `pages` 和用于获取这些页面的 `pageParams` 的对象:`{ pages: [data, data, data], pageParams: [...]}` + +单向操作: + +```tsx +const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = + useInfiniteQuery( + 'projects', + ({ pageParam = 0 }) => fetchProjects(pageParam), + { + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + }, + ) +``` + +双向操作: + +```tsx +const { + data, + fetchNextPage, + fetchPreviousPage, + hasNextPage, + hasPreviousPage, + isFetchingNextPage, + isFetchingPreviousPage, +} = useInfiniteQuery( + 'projects', + ({ pageParam = 0 }) => fetchProjects(pageParam), + { + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, + }, +) +``` + +反向单向操作: + +```tsx +const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = + useInfiniteQuery( + 'projects', + ({ pageParam = 0 }) => fetchProjects(pageParam), + { + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + }, + ) +``` + +### 无限查询数据现在包含用于获取这些页面的页面数组和 pageParams + +这使得操作数据和页面参数更加容易,例如,移除第一页数据及其参数: + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1), +})) +``` + +### useMutation 现在返回一个对象而非数组 + +虽然旧方式让我们想起了初次发现 `useState` 时的温暖感觉,但这种感觉并未持续太久。现在变更返回的是一个单独的对象。 + +```tsx +// 旧方式: +const [mutate, { status, reset }] = useMutation() + +// 新方式: +const { mutate, status, reset } = useMutation() +``` + +### `mutation.mutate` 不再返回 promise + +- `[mutate]` 变量已变更为 `mutation.mutate` 函数 +- 新增 `mutation.mutateAsync` 函数 + +我们收到了很多关于此行为的疑问,因为用户期望 promise 能像常规 promise 一样工作。 + +因此,`mutate` 函数现在被拆分为 `mutate` 和 `mutateAsync` 函数。 + +`mutate` 函数可在使用回调时使用: + +```tsx +const { mutate } = useMutation({ mutationFn: addTodo }) + +mutate('todo', { + onSuccess: (data) => { + console.log(data) + }, + onError: (error) => { + console.error(error) + }, + onSettled: () => { + console.log('settled') + }, +}) +``` + +`mutateAsync` 函数可在使用 async/await 时使用: + +```tsx +const { mutateAsync } = useMutation({ mutationFn: addTodo }) + +try { + const data = await mutateAsync('todo') + console.log(data) +} catch (error) { + console.error(error) +} finally { + console.log('settled') +} +``` + +### useQuery 的对象语法现在使用简化的配置: + +```tsx +// 旧方式: +useQuery({ + queryKey: 'posts', + queryFn: fetchPosts, + config: { staleTime: Infinity }, +}) + +// 新方式: +useQuery({ + queryKey: 'posts', + queryFn: fetchPosts, + staleTime: Infinity, +}) +``` + +### 如果设置,QueryOptions.enabled 选项必须是布尔值(`true`/`false`) + +`enabled` 查询选项现在仅在值为 `false` 时禁用查询。 +如果需要,可以使用 `!!userId` 或 `Boolean(userId)` 进行转换,如果传递了非布尔值,将抛出错误。 + +### 已移除 QueryOptions.initialStale 选项 + +`initialStale` 查询选项已被移除,初始数据现在被视为常规数据。 +这意味着如果提供了 `initialData`,查询默认会在挂载时重新获取。 +如果不希望立即重新获取,可以定义 `staleTime`。 + +### `QueryOptions.forceFetchOnMount` 选项已被 `refetchOnMount: 'always'` 取代 + +老实说,我们积累了太多 `refetchOn____` 选项,这应该能清理一下。 + +### `QueryOptions.refetchOnMount` 选项现在仅适用于其父组件而非所有查询观察者 + +当 `refetchOnMount` 设置为 `false` 时,会阻止任何额外组件在挂载时重新获取。 +在版本 3 中,仅设置了该选项的组件不会在挂载时重新获取。 + +### 已移除 `QueryOptions.queryFnParamsFilter`,改用新的 `QueryFunctionContext` 对象 + +`queryFnParamsFilter` 选项已被移除,因为查询函数现在接收 `QueryFunctionContext` 对象而非查询键。 + +由于 `QueryFunctionContext` 也包含查询键,参数仍可在查询函数内部进行过滤。 + +### `QueryOptions.notifyOnStatusChange` 选项已被新的 `notifyOnChangeProps` 和 `notifyOnChangePropsExclusions` 选项取代 + +通过这些新选项,可以更精细地配置组件应在何时重新渲染。 + +仅在 `data` 或 `error` 属性变更时重新渲染: + +```tsx +import { useQuery } from 'react-query' + +function User() { + const { data } = useQuery(['user'], fetchUser, { + notifyOnChangeProps: ['data', 'error'], + }) + return
    用户名: {data.username}
    +} +``` + +防止 `isStale` 属性变更时重新渲染: + +```tsx +import { useQuery } from 'react-query' + +function User() { + const { data } = useQuery(['user'], fetchUser, { + notifyOnChangePropsExclusions: ['isStale'], + }) + return
    用户名: {data.username}
    +} +``` + +### `QueryResult.clear()` 函数已重命名为 `QueryResult.remove()` + +虽然它被称为 `clear`,但实际上只是将查询从缓存中移除。新名称更符合其功能。 + +### `QueryResult.updatedAt` 属性已拆分为 `QueryResult.dataUpdatedAt` 和 `QueryResult.errorUpdatedAt` 属性 + +因为数据和错误可以同时存在,`updatedAt` 属性被拆分为 `dataUpdatedAt` 和 `errorUpdatedAt`。 + +### `setConsole()` 已被新的 `setLogger()` 函数取代 + +```tsx +import { setLogger } from 'react-query' + +// 使用 Sentry 记录 +setLogger({ + error: (error) => { + Sentry.captureException(error) + }, +}) + +// 使用 Winston 记录 +setLogger(winston.createLogger()) +``` + +### React Native 不再需要覆盖记录器 + +为了防止查询失败时在 React Native 中显示错误屏幕,之前需要手动更改控制台: + +```tsx +import { setConsole } from 'react-query' + +setConsole({ + log: console.log, + warn: console.warn, + error: console.warn, +}) +``` + +在版本 3 中,当在 React Native 中使用 React Query 时会自动完成此操作。 + +### TypeScript + +#### `QueryStatus` 已从 [枚举](https://www.typescriptlang.org/docs/handbook/enums.html#string-enums) 变更为 [联合类型](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) + +因此,如果你曾将查询或变更的 status 属性与 QueryStatus 枚举属性进行比较,现在需要将其与枚举先前为每个属性保存的字符串字面量进行比较。 + +因此,你需要将枚举属性更改为它们等效的字符串字面量,如下所示: + +- `QueryStatus.Idle` -> `'idle'` +- `QueryStatus.Loading` -> `'loading'` +- `QueryStatus.Error` -> `'error'` +- `QueryStatus.Success` -> `'success'` + +以下是你需要进行的更改示例: + +```tsx +- import { useQuery, QueryStatus } from 'react-query'; // [!code --] ++ import { useQuery } from 'react-query'; // [!code ++] + +const { data, status } = useQuery(['post', id], () => fetchPost(id)) + +- if (status === QueryStatus.Loading) { // [!code --] ++ if (status === 'loading') { // [!code ++] + ... +} + +- if (status === QueryStatus.Error) { // [!code --] ++ if (status === 'error') { // [!code ++] + ... +} +``` + +## 新功能 + +#### 查询数据选择器 (Query Data Selectors) + +`useQuery` 和 `useInfiniteQuery` 钩子现在具有 `select` 选项,可以选择或转换查询结果的部分内容。 + +```tsx +import { useQuery } from 'react-query' + +function User() { + const { data } = useQuery(['user'], fetchUser, { + select: (user) => user.username, + }) + return
    用户名: {data}
    +} +``` + +将 `notifyOnChangeProps` 选项设置为 `['data', 'error']`,以仅在所选数据变更时重新渲染。 + +#### useQueries() 钩子,用于可变长度的并行查询执行 + +希望能在循环中运行 `useQuery`?钩子规则说不可以,但有了新的 `useQueries()` 钩子,你可以! + +```tsx +import { useQueries } from 'react-query' + +function Overview() { + const results = useQueries([ + { queryKey: ['post', 1], queryFn: fetchPost }, + { queryKey: ['post', 2], queryFn: fetchPost }, + ]) + return ( +
      + {results.map(({ data }) => data &&
    • {data.title})
    • )} +
    + ) +} +``` + +#### 重试/离线变更 + +默认情况下,React Query 不会在错误时重试变更,但可以通过 `retry` 选项实现: + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + retry: 3, +}) +``` + +如果因设备离线导致变更失败,它们将在设备重新连接时按相同顺序重试。 + +#### 持久化变更 + +变更现在可以持久化到存储中,并在稍后恢复。更多信息请参阅变更文档。 + +#### QueryObserver + +`QueryObserver` 可用于创建和/或观察查询: + +```tsx +const observer = new QueryObserver(queryClient, { queryKey: 'posts' }) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +#### InfiniteQueryObserver + +`InfiniteQueryObserver` 可用于创建和/或观察无限查询: + +```tsx +const observer = new InfiniteQueryObserver(queryClient, { + queryKey: 'posts', + queryFn: fetchPosts, + getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor, +}) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +#### QueriesObserver + +`QueriesObserver` 可用于创建和/或观察多个查询: + +```tsx +const observer = new QueriesObserver(queryClient, [ + { queryKey: ['post', 1], queryFn: fetch diff --git a/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md b/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md new file mode 100644 index 00000000000..e6dcb20493f --- /dev/null +++ b/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md @@ -0,0 +1,384 @@ +--- +source-updated-at: '2025-03-31T09:06:11.000Z' +translation-updated-at: '2025-05-06T04:15:18.700Z' +id: migrating-to-react-query-4 +title: 迁移到 v4 +--- +## 重大变更 + +v4 是一个主要版本,需要注意以下破坏性变更: + +### react-query 现已更名为 @tanstack/react-query + +需要卸载/安装依赖并更改导入路径: + +``` +npm uninstall react-query +npm install @tanstack/react-query +npm install @tanstack/react-query-devtools +``` + +```tsx +- import { useQuery } from 'react-query' // [!code --] +- import { ReactQueryDevtools } from 'react-query/devtools' // [!code --] + ++ import { useQuery } from '@tanstack/react-query' // [!code ++] ++ import { ReactQueryDevtools } from '@tanstack/react-query-devtools' // [!code ++] +``` + +#### 代码迁移工具 (Codemod) + +为简化导入迁移,v4 提供了代码迁移工具。 + +> 该工具会尽力协助迁移破坏性变更,但请仔细检查生成代码!某些边缘情况可能无法被工具识别,请留意日志输出。 + +可通过以下命令之一运行迁移: + +针对 `.js` 或 `.jsx` 文件: +``` +npx jscodeshift ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js +``` + +针对 `.ts` 或 `.tsx` 文件: +``` +npx jscodeshift ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js +``` + +注意:对于 `TypeScript` 必须使用 `tsx` 作为解析器,否则迁移可能无法正确应用! + +**注意:** 迁移后可能会破坏代码格式,请记得运行 `prettier` 和/或 `eslint` 进行格式化。 + +**注意:** 该工具仅修改导入语句——仍需手动安装独立的开发工具包。 + +### 查询键 (Query Keys) 和变更键 (Mutation Keys) 必须为数组 + +v3 中查询键和变更键可以是字符串或数组。React Query 内部始终使用数组键,有时会暴露给使用者。例如在 `queryFn` 中,为便于使用[默认查询函数](./default-query-function.md),获取的键始终是数组。 + +但此设计未在所有 API 中贯彻。例如使用[查询过滤器](./filters.md)的 `predicate` 函数时,获取的是原始查询键。若混合使用数组和字符串键,此类函数将难以工作。全局回调也存在同样问题。 + +为统一所有 API,现强制要求所有键必须为数组: + +```tsx +;-useQuery('todos', fetchTodos) + // [!code --] + useQuery(['todos'], fetchTodos) // [!code ++] +``` + +#### 代码迁移工具 + +为简化迁移,提供了专用迁移工具。 + +> 该工具会尽力协助迁移,但请仔细检查生成代码!某些边缘情况可能无法被工具识别,请留意日志输出。 + +可通过以下命令之一运行迁移: + +针对 `.js` 或 `.jsx` 文件: +``` +npx jscodeshift ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js +``` + +针对 `.ts` 或 `.tsx` 文件: +``` +npx jscodeshift ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js +``` + +注意:对于 `TypeScript` 必须使用 `tsx` 作为解析器。 + +**注意:** 迁移后可能会破坏代码格式,请记得运行 `prettier` 和/或 `eslint` 进行格式化。 + +### 移除 idle 状态 + +为支持更好的离线功能引入新的[获取状态 (fetchStatus)](./queries.md#fetchstatus)后,`idle` 状态变得冗余,因为 `fetchStatus: 'idle'` 能更准确地描述相同状态。详见[为何需要两种状态](./queries.md#why-two-different-states)。 + +主要影响尚未获取任何 `data` 的 `disabled` 查询,这些查询之前处于 `idle` 状态: + +```tsx +- status: 'idle' // [!code --] ++ status: 'loading' // [!code ++] ++ fetchStatus: 'idle' // [!code ++] +``` + +另请参阅[依赖查询指南](./dependent-queries.md) + +#### 禁用查询 (disabled queries) + +因此变更,禁用查询(包括临时禁用)将初始处于 `loading` 状态。为简化迁移,特别是需要显示加载指示器时,可检查 `isInitialLoading` 而非 `isLoading`: + +```tsx +;-isLoading + // [!code --] + isInitialLoading // [!code ++] +``` + +参见[禁用查询指南](./disabling-queries.md#isInitialLoading) + +### `useQueries` 新 API + +`useQueries` 钩子现在接受包含 `queries` 属性的对象作为输入。`queries` 属性值是一个查询数组(该数组与 v3 中直接传入 `useQueries` 的数组相同)。 + +```tsx +;-useQueries([ + { queryKey1, queryFn1, options1 }, + { queryKey2, queryFn2, options2 }, +]) + // [!code --] + useQueries({ + queries: [ + { queryKey1, queryFn1, options1 }, + { queryKey2, queryFn2, options2 }, + ], + }) // [!code ++] +``` + +### 成功查询中 undefined 不再作为合法缓存值 + +为实现通过返回 `undefined` 中止更新,现规定 `undefined` 不是合法缓存值。这与 React Query 其他设计一致,例如从[初始数据函数](./initial-query-data.md#initial-data-function)返回 `undefined` 也不会设置数据。 + +此外,在 `queryFn` 中添加日志容易产生 `Promise`: + +```tsx +useQuery(['key'], () => + axios.get(url).then((result) => console.log(result.data)), +) +``` + +现在类型层面已禁止此操作;运行时 `undefined` 会转换为 _失败的 Promise_,意味着会收到 `error`,开发模式下还会在控制台记录此错误。 + +### 默认情况下查询和变更需要网络连接 + +请阅读关于在线/离线支持的[新功能公告](#proper-offline-support)及专用[网络模式](./network-mode.md)页面。 + +虽然 React Query 是可用于任何 Promise 的异步状态管理器,但最常用于数据获取场景。因此默认情况下,若无网络连接,查询和变更将处于 `paused` 状态。如需恢复之前行为,可全局设置 `networkMode: offlineFirst`: + +```tsx +new QueryClient({ + defaultOptions: { + queries: { + networkMode: 'offlineFirst', + }, + mutations: { + networkMode: 'offlineFirst', + }, + }, +}) +``` + +### `notifyOnChangeProps` 不再接受 `"tracked"` 值 + +`notifyOnChangeProps` 选项不再接受 `"tracked"` 值,改为默认启用属性跟踪。所有使用 `notifyOnChangeProps: "tracked"` 的查询应移除此选项。 + +如需模拟 v3 默认行为(查询变化时重新渲染),现可通过设置 `notifyOnChangeProps: "all"` 来禁用智能跟踪优化。 + +### 移除 `notifyOnChangePropsExclusion` + +v4 中 `notifyOnChangeProps` 默认采用 v3 的 `"tracked"` 行为而非 `undefined`。由于 `"tracked"` 现为默认行为,此配置选项已无存在必要。 + +### `cancelRefetch` 行为统一 + +`cancelRefetch` 选项现在可传递给所有主动获取查询的方法: + +- `queryClient.refetchQueries` +- `queryClient.invalidateQueries` +- `queryClient.resetQueries` +- `useQuery` 返回的 `refetch` +- `useInfiniteQuery` 返回的 `fetchNextPage` 和 `fetchPreviousPage` + +除 `fetchNextPage` 和 `fetchPreviousPage` 外,此标志原默认值为 `false`,这会导致不一致问题:若已有缓慢请求在进行中,调用 `refetchQueries` 或 `invalidateQueries` 可能无法获取最新结果,因为此次重获取会被跳过。 + +我们认为代码主动触发的查询重获取应默认重启请求。 + +因此现在所有相关方法的此标志默认值改为 _true_。这也意味着连续两次调用 `refetchQueries` 而不等待时,会取消第一次请求并重启第二次请求: + +``` +queryClient.refetchQueries({ queryKey: ['todos'] }) +// 这将中止前次重获取并启动新请求 +queryClient.refetchQueries({ queryKey: ['todos'] }) +``` + +可通过显式传递 `cancelRefetch:false` 禁用此行为: + +``` +queryClient.refetchQueries({ queryKey: ['todos'] }) +// 前次重获取不会被中止——此次调用会被忽略 +queryClient.refetchQueries({ queryKey: ['todos'] }, { cancelRefetch: false }) +``` + +> 注意:自动触发的获取(如查询挂载或窗口聚焦重获取)行为不变。 + +### 查询过滤器 (Query Filters) + +[查询过滤器](./filters.md)是匹配查询的条件对象。历史上过滤器选项多为布尔标志组合,但这可能导致矛盾状态。例如: + +``` +active?: boolean + - true 时匹配活跃查询 + - false 时匹配非活跃查询 +inactive?: boolean + - true 时匹配非活跃查询 + - false 时匹配活跃查询 +``` + +这些标志组合使用时效果不佳,因为它们互斥。双 `false` 配置理论上应匹配所有查询或没有查询,语义不明确。 + +v4 中将这些过滤器合并为单一选项以明确意图: + +```tsx +- active?: boolean // [!code --] +- inactive?: boolean // [!code --] ++ type?: 'active' | 'inactive' | 'all' // [!code ++] +``` + +默认值为 `all`,可选择仅匹配 `active` 或 `inactive` 查询。 + +#### refetchActive / refetchInactive + +[queryClient.invalidateQueries](../../../reference/QueryClient.md#queryclientinvalidatequeries) 原有两个类似标志: + +``` +refetchActive: Boolean + - 默认 true + - false 时,匹配重获取条件且正通过 useQuery 等渲染的活跃查询不会在后台重获取,仅标记为失效 +refetchInactive: Boolean + - 默认 false + - true 时,匹配重获取条件且未渲染的非活跃查询会标记为失效并在后台重获取 +``` + +出于相同原因,这些标志也被合并: + +```tsx +- refetchActive?: boolean // [!code --] +- refetchInactive?: boolean // [!code --] ++ refetchType?: 'active' | 'inactive' | 'all' | 'none' // [!code ++] +``` + +此标志默认 `active`(因 `refetchActive` 原默认 `true`)。为支持完全不重获取的场景,新增 `none` 选项。 + +### `setQueryData` 不再触发 `onSuccess` + +此前设计令许多人困惑:若在 `onSuccess` 内调用 `setQueryData` 会导致无限循环。与 `staleTime` 结合使用时也常引发问题——若数据仅从缓存读取,`onSuccess` 不会被调用。 + +与 `onError` 和 `onSettled` 类似,`onSuccess` 回调现与请求绑定。无请求 -> 无回调。 + +如需监听 `data` 字段变化,建议使用 `useEffect` 并将 `data` 加入依赖数组。React Query 通过结构共享确保数据稳定,效果函数不会在每次后台重获取时执行,仅在数据实际变化时触发: + +``` +const { data } = useQuery({ queryKey, queryFn }) +React.useEffect(() => mySideEffectHere(data), [data]) +``` + +### `persistQueryClient` 及相关持久化插件结束实验状态并更名 + +插件 `createWebStoragePersistor` 和 `createAsyncStoragePersistor` 分别更名为 [`createSyncStoragePersister`](../plugins/createSyncStoragePersister.md) 和 [`createAsyncStoragePersister`](../plugins/createAsyncStoragePersister.md)。`persistQueryClient` 中的 `Persistor` 接口也更名为 `Persister`。更名动机参见[此讨论](https://english.stackexchange.com/questions/206893/persister-or-persistor)。 + +由于这些插件已结束实验状态,导入路径也已更新: + +```tsx +- import { persistQueryClient } from 'react-query/persistQueryClient-experimental' // [!code --] +- import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental' // [!code --] +- import { createAsyncStoragePersistor } from 'react-query/createAsyncStoragePersistor-experimental' // [!code --] + ++ import { persistQueryClient } from '@tanstack/react-query-persist-client' // [!code ++] ++ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' // [!code ++] ++ import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' // [!code ++] +``` + +### 不再支持 Promise 的 `cancel` 方法 + +[旧版 `cancel` 方法](./query-cancellation.md#old-cancel-function)允许在 Promise 上定义 `cancel` 函数以实现查询取消,现已被移除。建议使用[新版 API](./query-cancellation.md)(v3.30.0 引入),其内部使用 [`AbortController` API](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) 并提供 [`AbortSignal` 实例](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)支持查询取消。 + +### TypeScript + +现在需要 TypeScript v4.1 或更高版本 + +### 支持的浏览器 + +v4 起 React Query 针对现代浏览器优化。我们已更新 browserslist 配置以生成更现代、高效且更小的包。具体要求参见[安装说明](../../installation#requirements)。 + +### 移除 `setLogger` + +原先可通过 `setLogger` 全局修改日志记录器。v4 中此功能改为在创建 `QueryClient` 时通过可选字段配置。 + +```tsx +- import { QueryClient, setLogger } from 'react-query'; // [!code --] ++ import { QueryClient } from '@tanstack/react-query'; // [!code ++] + +- setLogger(customLogger) // [!code --] +- const queryClient = new QueryClient(); // [!code --] ++ const queryClient = new QueryClient({ logger: customLogger }) // [!code ++] +``` + +### 服务端默认禁用手动垃圾回收 + +v3 中 React Query 默认缓存查询结果 5 分钟,然后手动垃圾回收。此默认行为也应用于服务端 React Query。 + +这导致高内存消耗和进程挂起等待垃圾回收完成。v4 中服务端 `cacheTime` 默认改为 `Infinity`,相当于禁用手动垃圾回收(NodeJS 进程会在请求完成后自动清理)。 + +此变更仅影响服务端 React Query 用户(如 Next.js)。若手动设置 `cacheTime` 则不受影响(但建议保持行为一致)。 + +### 生产环境日志记录 + +v4 起,生产环境下 react-query 不再将错误(如获取失败)记录到控制台,因之前常引起困惑。开发模式下错误仍会显示。 + +### ESM 支持 + +React Query 现在支持 [package.json `"exports"`](https://nodejs.org/api/packages.html#exports),完全兼容 Node 的原生 CommonJS 和 ESM 解析。预计对多数用户无破坏性影响,但此变更限制只能导入官方支持的入口文件。 + +### 统一通知事件 (NotifyEvents) + +手动订阅 `QueryCache` 始终会收到 `QueryCacheNotifyEvent`,但 `MutationCache` 之前无此设计。现统一行为并调整事件名称: + +#### QueryCacheNotifyEvent + +```tsx +- type: 'queryAdded' // [!code --] ++ type: 'added' // [!code ++] +- type: 'queryRemoved' // [!code --] ++ type: 'removed' // [!code ++] +- type: 'queryUpdated' // [!code --] ++ type: 'updated' // [!code ++] +``` + +#### MutationCacheNotifyEvent + +`MutationCacheNotifyEvent` 使用与 `QueryCacheNotifyEvent` 相同的类型。 + +> 注意:仅当通过 `queryCache.subscribe` 或 `mutationCache.subscribe` 手动订阅缓存时相关 + +### 移除独立的水合 (hydration) 导出 + +自 [3.22.0](https://github.com/tannerlinsley/react-query/releases/tag/v3.22.0) 版本起,水合工具已移至 React Query 核心。v3 中仍可从 `react-query/hydration` 导入旧导出,但 v4 中已移除。 + +```tsx +- import { dehydrate, hydrate, useHydrate, Hydrate } from 'react-query/hydration' // [!code --] ++ import { dehydrate, hydrate, useHydrate, Hydrate } from '@tanstack/react-query' // [!code ++] +``` + +### 移除 `queryClient`、`query` 和 `mutation` 的未公开方法 + +`QueryClient` 上的 `cancelMutations` 和 `executeMutation` 方法未在文档中说明且内部未使用,故已移除。由于这只是对 `mutationCache` 方法的封装,仍可通过 `executeMutation` 的功能: + +```tsx +- executeMutation< // [!code --] +- TData = unknown, // [!code --] +- TError = unknown, // [!code --] +- TVariables = void, // [!code --] +- TContext = unknown // [!code --] +- >( // [!code --] +- options: MutationOptions // [!code --] +- ): Promise { // [!code --] +- return this.mutationCache.build(this, options).execute() // [!code --] +- } // [!code --] +``` + +此外,移除未使用的 `query.setDefaultOptions`。移除 `mutation.cancel` 因其实际未取消请求。 + +### `src/react` 目录更名为 `src/reactjs` + +原先包含从 `react` 模块导入的 `react` 目录在某些 Jest 配置下会导致测试报 diff --git a/docs/zh-hans/framework/react/guides/migrating-to-v5.md b/docs/zh-hans/framework/react/guides/migrating-to-v5.md new file mode 100644 index 00000000000..6e949340842 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/migrating-to-v5.md @@ -0,0 +1,355 @@ +--- +source-updated-at: '2025-03-19T08:29:27.000Z' +translation-updated-at: '2025-05-06T04:15:15.834Z' +id: migrating-to-tanstack-query-5 +title: 迁移到 v5 +--- +## 重大变更 + +v5 是一个主要版本,因此需要注意以下重大变更: + +### 仅支持单一对象参数签名 + +`useQuery` 及相关函数在 TypeScript 中曾有多种重载形式——即函数可以通过不同方式调用。这不仅在类型维护上困难,还需要运行时检查第一和第二参数的类型以正确创建选项。 + +现在仅支持对象格式: + +```tsx +useQuery(key, fn, options) // [!code --] +useQuery({ queryKey, queryFn, ...options }) // [!code ++] +useInfiniteQuery(key, fn, options) // [!code --] +useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +useMutation(fn, options) // [!code --] +useMutation({ mutationFn, ...options }) // [!code ++] +useIsFetching(key, filters) // [!code --] +useIsFetching({ queryKey, ...filters }) // [!code ++] +useIsMutating(key, filters) // [!code --] +useIsMutating({ mutationKey, ...filters }) // [!code ++] +``` + +```tsx +queryClient.isFetching(key, filters) // [!code --] +queryClient.isFetching({ queryKey, ...filters }) // [!code ++] +queryClient.ensureQueryData(key, filters) // [!code --] +queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++] +queryClient.getQueriesData(key, filters) // [!code --] +queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++] +queryClient.setQueriesData(key, updater, filters, options) // [!code --] +queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++] +queryClient.removeQueries(key, filters) // [!code --] +queryClient.removeQueries({ queryKey, ...filters }) // [!code ++] +queryClient.resetQueries(key, filters, options) // [!code --] +queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.cancelQueries(key, filters, options) // [!code --] +queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.invalidateQueries(key, filters, options) // [!code --] +queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.refetchQueries(key, filters, options) // [!code --] +queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.fetchQuery(key, fn, options) // [!code --] +queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchQuery(key, fn, options) // [!code --] +queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.fetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +``` + +```tsx +queryCache.find(key, filters) // [!code --] +queryCache.find({ queryKey, ...filters }) // [!code ++] +queryCache.findAll(key, filters) // [!code --] +queryCache.findAll({ queryKey, ...filters }) // [!code ++] +``` + +### `queryClient.getQueryData` 现在仅接受 `queryKey` 作为参数 + +`queryClient.getQueryData` 的参数改为仅接受 `queryKey`: + +```tsx +queryClient.getQueryData(queryKey, filters) // [!code --] +queryClient.getQueryData(queryKey) // [!code ++] +``` + +### `queryClient.getQueryState` 现在仅接受 `queryKey` 作为参数 + +`queryClient.getQueryState` 的参数改为仅接受 `queryKey`: + +```tsx +queryClient.getQueryState(queryKey, filters) // [!code --] +queryClient.getQueryState(queryKey) // [!code ++] +``` + +#### 代码迁移工具 (Codemod) + +为简化重载移除的迁移工作,v5 提供了代码迁移工具。 + +> 该工具会尽力协助迁移重大变更,但请仔细检查生成的代码!此外,某些边缘情况可能无法被工具识别,请留意日志输出。 + +如需针对 `.js` 或 `.jsx` 文件运行,请使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +如需针对 `.ts` 或 `.tsx` 文件运行,请使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +注意:对于 `TypeScript` 文件,必须使用 `tsx` 作为解析器,否则代码迁移工具可能无法正确应用! + +**注意:** 应用代码迁移工具可能会破坏代码格式,完成后请务必运行 `prettier` 和/或 `eslint`! + +关于代码迁移工具工作原理的说明: + +- 一般情况下,我们会寻找理想情况:当第一个参数是对象表达式且包含 "queryKey" 或 "mutationKey" 属性(取决于正在转换的钩子/方法调用)。如果符合此条件,则代码已匹配新签名,工具不会修改。🎉 +- 如果不符合上述条件,工具会检查第一个参数是否为数组表达式或引用数组表达式的标识符。如果是,则将其放入对象表达式中作为第一个参数。 +- 如果可以推断对象参数,工具会尝试将现有属性复制到新创建的对象中。 +- 如果工具无法推断用法,则会在控制台输出消息,包含文件名和代码行号。此时需要手动迁移。 +- 如果转换导致错误,控制台也会显示消息,提示发生意外情况,请手动迁移。 + +### 移除了 `useQuery`(及 `QueryObserver`)中的回调函数 + +`onSuccess`、`onError` 和 `onSettled` 已从查询中移除(突变中仍保留)。请参阅 [此 RFC](https://github.com/TanStack/query/discussions/5279) 了解变更动机及替代方案。 + +### `refetchInterval` 回调函数现在仅接收 `query` 参数 + +这统一了回调调用方式(`refetchOnWindowFocus`、`refetchOnMount` 和 `refetchOnReconnect` 回调也仅接收查询参数),并修复了当回调获取通过 `select` 转换的数据时的一些类型问题。 + +```tsx +- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --] ++ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++] +``` + +仍可通过 `query.state.data` 访问数据,但不会是通过 `select` 转换后的数据。如需访问转换后的数据,可对 `query.state.data` 再次调用转换函数。 + +### 从 `useQuery` 中移除了 `remove` 方法 + +此前,`remove` 方法用于从 `queryCache` 中移除查询而不通知观察者。通常用于强制移除不再需要的数据,例如用户注销时。 + +但当查询仍处于活动状态时这样做没有意义,因为下次重新渲染会触发硬加载状态。 + +如需移除查询,可使用 `queryClient.removeQueries({queryKey: key})`: + +```tsx +const queryClient = useQueryClient() +const query = useQuery({ queryKey, queryFn }) + +query.remove() // [!code --] +queryClient.removeQueries({ queryKey }) // [!code ++] +``` + +### 最低要求的 TypeScript 版本现为 4.7 + +主要因为修复了一个重要的类型推断问题。详情请参阅 [TypeScript issue](https://github.com/microsoft/TypeScript/issues/43371)。 + +### 从 `useQuery` 中移除了 `isDataEqual` 选项 + +此前,该函数用于指示是使用先前的 `data`(`true`)还是新数据(`false`)作为查询的解析数据。 + +可通过向 `structuralSharing` 传递函数实现相同功能: + +```tsx + import { replaceEqualDeep } from '@tanstack/react-query' + +- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --] ++ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++] +``` + +### 移除了已弃用的自定义日志记录器 + +自定义日志记录器在 v4 中已弃用,在此版本中移除。日志记录仅在开发模式下有效,而在此模式下传递自定义日志记录器并无必要。 + +### 支持的浏览器 + +我们更新了 browserslist 以生成更现代、性能更好且体积更小的包。要求详见 [此处](../../installation#requirements)。 + +### 私有类字段和方法 + +TanStack Query 始终在类中有私有字段和方法,但它们并非真正的私有——仅在 `TypeScript` 中是私有的。现在我们使用 [ECMAScript 私有类特性](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields),意味着这些字段在运行时真正私有,无法从外部访问。 + +### 将 `cacheTime` 重命名为 `gcTime` + +几乎每个人都误解了 `cacheTime`。它听起来像是"数据缓存的时间",但这是错误的。 + +只要查询仍在使用,`cacheTime` 就不起作用。仅在查询不再使用时生效。超时后,数据会被"垃圾回收"以避免缓存增长。 + +`gc` 指"垃圾回收"时间。这更技术性,但在计算机科学中是 [众所周知的缩写]()。 + +```tsx +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, // [!code --] ++ gcTime: 10 * MINUTE, // [!code ++] + }, + }, +}) +``` + +### `useErrorBoundary` 选项已重命名为 `throwOnError` + +为使 `useErrorBoundary` 选项更框架无关,并避免与 React 钩子的 "`use`" 前缀和 "ErrorBoundary" 组件名称混淆,现重命名为 `throwOnError` 以更准确反映其功能。 + +### TypeScript:`Error` 现在是错误的默认类型而非 `unknown` + +尽管在 JavaScript 中可以 `throw` 任何内容(这使得 `unknown` 是最正确的类型),但几乎总是抛出 `Error`(或其子类)。此变更使在 TypeScript 中处理 `error` 字段对大多数情况更简单。 + +如需抛出非 Error 的内容,现在需自行设置泛型: + +```ts +useQuery({ + queryKey: ['some-query'], + queryFn: async () => { + if (Math.random() > 0.5) { + throw 'some error' + } + return 42 + }, +}) +``` + +如需全局设置不同类型的错误,请参阅 [TypeScript 指南](../typescript.md#registering-a-global-error)。 + +### 移除了 eslint `prefer-query-object-syntax` 规则 + +由于现在唯一支持的语法是对象语法,此规则不再需要。 + +### 用 `placeholderData` 恒等函数替代 `keepPreviousData` + +我们移除了 `keepPreviousData` 选项和 `isPreviousData` 标志,因为它们的功能与 `placeholderData` 和 `isPlaceholderData` 标志基本相同。 + +为实现与 `keepPreviousData` 相同的功能,我们向 `placeholderData` 添加了先前查询 `data` 作为参数,它接受一个恒等函数。因此只需向 `placeholderData` 提供恒等函数或使用 Tanstack Query 包含的 `keepPreviousData` 函数。 + +> 注意:`useQueries` 不会在 `placeholderData` 函数中接收 `previousData` 作为参数。这是由于传入数组的查询的动态性质可能导致占位符和 `queryFn` 的结果形状不同。 + +```tsx +import { + useQuery, ++ keepPreviousData // [!code ++] +} from "@tanstack/react-query"; + +const { + data, +- isPreviousData, // [!code --] ++ isPlaceholderData, // [!code ++] +} = useQuery({ + queryKey, + queryFn, +- keepPreviousData: true, // [!code --] ++ placeholderData: keepPreviousData // [!code ++] +}); +``` + +在 Tanstack Query 的上下文中,恒等函数指始终返回其提供的参数(即数据)而不做更改的函数。 + +```ts +useQuery({ + queryKey, + queryFn, + placeholderData: (previousData, previousQuery) => previousData, // 与 `keepPreviousData` 行为相同的恒等函数 +}) +``` + +但此变更有一些注意事项: + +- `placeholderData` 会始终进入 `success` 状态,而 `keepPreviousData` 给出先前查询的状态。如果数据成功获取后遇到后台刷新错误,状态可能是 `error`。但由于错误本身未共享,我们决定坚持 `placeholderData` 的行为。 +- `keepPreviousData` 提供先前数据的 `dataUpdatedAt` 时间戳,而使用 `placeholderData` 时 `dataUpdatedAt` 保持为 `0`。如需在屏幕上连续显示该时间戳可能会不便,但可通过 `useEffect` 解决: + + ```ts + const [updatedAt, setUpdatedAt] = useState(0) + + const { data, dataUpdatedAt } = useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + }) + + useEffect(() => { + if (dataUpdatedAt > updatedAt) { + setUpdatedAt(dataUpdatedAt) + } + }, [dataUpdatedAt]) + ``` + +### 窗口焦点重新获取不再监听 `focus` 事件 + +现在仅使用 `visibilitychange` 事件。这是可行的,因为我们仅支持支持 `visibilitychange` 事件的浏览器。这修复了 [此处列出](https://github.com/TanStack/query/pull/4805) 的一系列问题。 + +### 网络状态不再依赖 `navigator.onLine` 属性 + +`navigator.onLine` 在基于 Chromium 的浏览器中效果不佳。存在 [许多问题](https://bugs.chromium.org/p/chromium/issues/list?q=navigator.online) 关于误报,导致查询被错误标记为 `offline`。 + +为避免此问题,我们现在始终以 `online: true` 开始,仅监听 `online` 和 `offline` 事件来更新状态。 + +这应减少误报的可能性,但对于通过 serviceWorkers 加载的离线应用,可能意味着误报,因为这些应用即使没有互联网连接也能工作。 + +### 移除自定义 `context` 属性,改用自定义 `queryClient` 实例 + +在 v4 中,我们引入了向所有 react-query 钩子传递自定义 `context` 的可能性。这在使用微前端时实现了适当的隔离。 + +然而,`context` 是仅 React 的特性。`context` 所做的只是让我们访问 `queryClient`。我们可以通过允许直接传入自定义 `queryClient` 实现相同的隔离。 +这反过来使其他框架能够以框架无关的方式拥有相同的功能。 + +```tsx +import { queryClient } from './my-client' + +const { data } = useQuery( + { + queryKey: ['users', id], + queryFn: () => fetch(...), +- context: customContext // [!code --] + }, ++ queryClient, // [!code ++] +) +``` + +### 移除 `refetchPage`,改用 `maxPages` + +在 v4 中,我们引入了通过 `refetchPage` 函数定义无限查询中要重新获取的页面的可能性。 + +然而,重新获取所有页面可能导致 UI 不一致。此外,此选项在例如 `queryClient.refetchQueries` 上可用,但仅对无限查询有效,而非"普通"查询。 + +v5 为无限查询引入了新的 `maxPages` 选项,以限制存储在查询数据中和重新获取的页面数量。此新功能处理了最初为 `refetchPage` 功能识别的用例,而无需相关的问题。 + +### 新的 `dehydrate` API + +传递给 `dehydrate` 的选项已简化。查询和突变始终根据默认函数实现进行脱水。要更改此行为,可以使用移除的布尔选项 `dehydrateMutations` 和 `dehydrateQueries` 的函数等效项 `shouldDehydrateQuery` 或 `shouldDehydrateMutation`。要完全不水合查询/突变,传入 `() => false`。 + +```tsx +- dehydrateMutations?: boolean // [!code --] +- dehydrateQueries?: boolean // [!code --] +``` + +### 无限查询现在需要 `initialPageParam` + +此前,我们向 `queryFn` 传递 `undefined` 作为 `pageParam`,并且可以在 `queryFn` 函数签名中为 `pageParam` 参数分配默认值。这导致在 `queryCache` 中存储不可序列化的 `undefined`。 + +现在,必须向无限查询选项显式传递 `initialPageParam`。这将用作第一页的 `pageParam`: + +```tsx +useInfiniteQuery({ + queryKey, +- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --] ++ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++] ++ initialPageParam: 0, // [!code ++] + getNextPageParam: (lastPage) => lastPage.next, +}) +``` + +### 移除了无限查询的手动模式 + +此前,我们允许通过直接向 `fetchNextPage` 或 `fetchPreviousPage` 传递 `pageParam` 值来覆盖从 `getNextPageParam` 或 `getPreviousPageParam` 返回的 `pageParams`。此功能在重新获取时完全无效,且不广为人知或使用。这也意味着无限查询现在必须定义 `getNextPageParam`。 + +### 从 `getNextPageParam` 或 `getPreviousPageParam` 返回 `null` 现在表示没有更多可用页面 + +在 v4 中,需要显式返回 `undefined` 表示没有更多可用页面。我们扩展了此检查以包括 `null diff --git a/docs/zh-hans/framework/react/guides/mutations.md b/docs/zh-hans/framework/react/guides/mutations.md new file mode 100644 index 00000000000..b0346baebdd --- /dev/null +++ b/docs/zh-hans/framework/react/guides/mutations.md @@ -0,0 +1,413 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:14:31.297Z' +id: mutations +title: 变更 +--- +与查询不同,变更 (mutations) 通常用于创建/更新/删除数据或执行服务端副作用。为此,TanStack Query 导出了 `useMutation` 钩子。 + +以下是一个向服务器添加新待办事项的变更示例: + +[//]: # 'Example' + +```tsx +function App() { + const mutation = useMutation({ + mutationFn: (newTodo) => { + return axios.post('/todos', newTodo) + }, + }) + + return ( +
    + {mutation.isPending ? ( + '添加待办中...' + ) : ( + <> + {mutation.isError ? ( +
    发生错误:{mutation.error.message}
    + ) : null} + + {mutation.isSuccess ?
    待办事项已添加!
    : null} + + + + )} +
    + ) +} +``` + +[//]: # 'Example' + +变更在任何时刻只能处于以下状态之一: + +- `isIdle` 或 `status === 'idle'` - 变更当前处于空闲或重置状态 +- `isPending` 或 `status === 'pending'` - 变更正在执行中 +- `isError` 或 `status === 'error'` - 变更遇到错误 +- `isSuccess` 或 `status === 'success'` - 变更成功完成且数据可用 + +除了这些主要状态外,根据变更状态还可获取更多信息: + +- `error` - 如果变更处于 `error` 状态,可通过 `error` 属性获取错误信息。 +- `data` - 如果变更处于 `success` 状态,可通过 `data` 属性获取数据。 + +在上面的示例中,您还看到可以通过调用 `mutate` 函数并传递**单个变量或对象**来向变更函数传递变量。 + +仅使用变量时,变更并不特殊,但当与 `onSuccess` 选项、[Query Client 的 `invalidateQueries` 方法](../../../reference/QueryClient.md#queryclientinvalidatequeries) 和 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 结合使用时,变更将成为非常强大的工具。 + +[//]: # 'Info1' + +> 重要提示:`mutate` 函数是一个异步函数,这意味着在 **React 16 及更早版本** 中不能直接在事件回调中使用它。如果需要在 `onSubmit` 中访问事件,必须将 `mutate` 包装在另一个函数中。这是由于 [React 事件池机制](https://reactjs.org/docs/legacy-event-pooling.html)。 + +[//]: # 'Info1' +[//]: # 'Example2' + +```tsx +// 在 React 16 及更早版本中,以下代码无法工作 +const CreateTodo = () => { + const mutation = useMutation({ + mutationFn: (event) => { + event.preventDefault() + return fetch('/api', new FormData(event.target)) + }, + }) + + return
    ...
    +} + +// 以下代码可以正常工作 +const CreateTodo = () => { + const mutation = useMutation({ + mutationFn: (formData) => { + return fetch('/api', formData) + }, + }) + const onSubmit = (event) => { + event.preventDefault() + mutation.mutate(new FormData(event.target)) + } + + return
    ...
    +} +``` + +[//]: # 'Example2' + +## 重置变更状态 + +有时需要清除变更请求的 `error` 或 `data`。为此,可以使用 `reset` 函数来处理: + +[//]: # 'Example3' + +```tsx +const CreateTodo = () => { + const [title, setTitle] = useState('') + const mutation = useMutation({ mutationFn: createTodo }) + + const onCreateTodo = (e) => { + e.preventDefault() + mutation.mutate({ title }) + } + + return ( +
    + {mutation.error && ( +
    mutation.reset()}>{mutation.error}
    + )} + setTitle(e.target.value)} + /> +
    + +
    + ) +} +``` + +[//]: # 'Example3' + +## 变更副作用 + +`useMutation` 提供了一些辅助选项,允许在变更生命周期的任何阶段快速简单地执行副作用。这些选项对于[变更后使查询失效并重新获取](./invalidations-from-mutations.md) 甚至[乐观更新](./optimistic-updates.md) 非常有用。 + +[//]: # 'Example4' + +```tsx +useMutation({ + mutationFn: addTodo, + onMutate: (variables) => { + // 变更即将执行! + // 可选返回一个包含数据的上下文,用于例如回滚操作 + return { id: 1 } + }, + onError: (error, variables, context) => { + // 发生错误! + console.log(`回滚乐观更新,ID:${context.id}`) + }, + onSuccess: (data, variables, context) => { + // 成功! + }, + onSettled: (data, error, variables, context) => { + // 无论错误还是成功都会执行! + }, +}) +``` + +[//]: # 'Example4' + +在任何回调函数中返回 Promise 时,会先等待该 Promise 完成再调用下一个回调: + +[//]: # 'Example5' + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: async () => { + console.log("我是第一个!") + }, + onSettled: async () => { + console.log("我是第二个!") + }, +}) +``` + +[//]: # 'Example5' + +您可能希望在调用 `mutate` 时**触发额外的回调**,而不仅限于 `useMutation` 中定义的回调。这可用于触发组件特定的副作用。为此,可以在变更变量之后向 `mutate` 函数提供相同的回调选项。支持的选项包括:`onSuccess`、`onError` 和 `onSettled`。请注意,如果组件在变更完成前卸载,这些额外的回调将不会执行。 + +[//]: # 'Example6' + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 我会先触发 + }, + onError: (error, variables, context) => { + // 我会先触发 + }, + onSettled: (data, error, variables, context) => { + // 我会先触发 + }, +}) + +mutate(todo, { + onSuccess: (data, variables, context) => { + // 我会第二个触发! + }, + onError: (error, variables, context) => { + // 我会第二个触发! + }, + onSettled: (data, error, variables, context) => { + // 我会第二个触发! + }, +}) +``` + +[//]: # 'Example6' + +### 连续变更 + +在处理连续变更时,`onSuccess`、`onError` 和 `onSettled` 回调的行为略有不同。当传递给 `mutate` 函数时,它们只会触发一次,并且仅在组件仍挂载时触发。这是因为每次调用 `mutate` 函数时,变更观察器会被移除并重新订阅。相反,`useMutation` 的处理程序会为每次 `mutate` 调用执行。 + +> 请注意,传递给 `useMutation` 的 `mutationFn` 很可能是异步的。在这种情况下,变更完成的顺序可能与 `mutate` 函数调用的顺序不同。 + +[//]: # 'Example7' + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 会被调用 3 次 + }, +}) + +const todos = ['待办 1', '待办 2', '待办 3'] +todos.forEach((todo) => { + mutate(todo, { + onSuccess: (data, variables, context) => { + // 只会执行一次,针对最后一次变更(待办 3), + // 无论哪次变更先完成 + }, + }) +}) +``` + +[//]: # 'Example7' + +## Promise + +使用 `mutateAsync` 替代 `mutate` 可以获取一个 Promise,该 Promise 在成功时解析或在出错时抛出异常。例如,这可用于组合副作用。 + +[//]: # 'Example8' + +```tsx +const mutation = useMutation({ mutationFn: addTodo }) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('完成') +} +``` + +[//]: # 'Example8' + +## 重试 + +默认情况下,TanStack Query 不会在出错时重试变更,但可以通过 `retry` 选项启用: + +[//]: # 'Example9' + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + retry: 3, +}) +``` + +[//]: # 'Example9' + +如果因设备离线导致变更失败,它们将在设备重新连接时按相同顺序重试。 + +## 持久化变更 + +如果需要,可以将变更持久化到存储中,并在稍后恢复。这可以通过 hydration 函数实现: + +[//]: # 'Example10' + +```tsx +const queryClient = new QueryClient() + +// 定义 "addTodo" 变更 +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables) => { + // 取消当前待办列表的查询 + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 创建乐观待办事项 + const optimisticTodo = { id: uuid(), title: variables.title } + + // 将乐观待办事项添加到待办列表 + queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // 返回包含乐观待办事项的上下文 + return { optimisticTodo } + }, + onSuccess: (result, variables, context) => { + // 用结果替换待办列表中的乐观待办事项 + queryClient.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === context.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, context) => { + // 从待办列表中移除乐观待办事项 + queryClient.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== context.optimisticTodo.id), + ) + }, + retry: 3, +}) + +// 在某个组件中启动变更: +const mutation = useMutation({ mutationKey: ['addTodo'] }) +mutation.mutate({ title: '标题' }) + +// 如果因设备离线等原因暂停了变更, +// 可以在应用退出时将暂停的变更脱水: +const state = dehydrate(queryClient) + +// 然后在应用启动时重新水合: +hydrate(queryClient, state) + +// 恢复暂停的变更: +queryClient.resumePausedMutations() +``` + +[//]: # 'Example10' + +### 持久化离线变更 + +如果使用 [persistQueryClient 插件](../plugins/persistQueryClient.md) 持久化离线变更,除非提供默认的变更函数,否则在页面重新加载时无法恢复变更。 + +这是一个技术限制。当持久化到外部存储时,只有变更的状态会被持久化,因为函数无法序列化。水合后,触发变更的组件可能未挂载,因此调用 `resumePausedMutations` 可能会导致错误:`未找到 mutationFn`。 + +[//]: # 'Example11' + +```tsx +const persister = createSyncStoragePersister({ + storage: window.localStorage, +}) +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小时 + }, + }, +}) + +// 需要一个默认的变更函数,以便页面重新加载后可以恢复暂停的变更 +queryClient.setMutationDefaults(['todos'], { + mutationFn: ({ id, data }) => { + return api.updateTodo(id, data) + }, +}) + +export default function App() { + return ( + { + // 从 localStorage 初始恢复成功后恢复变更 + queryClient.resumePausedMutations() + }} + > + + + ) +} +``` + +[//]: # 'Example11' + +我们还提供了一个全面的[离线示例](../examples/react/offline),涵盖了查询和变更。 + +## 变更作用域 + +默认情况下,所有变更并行运行——即使多次调用同一变更的 `.mutate()`。可以通过为变更指定带有 `id` 的 `scope` 来避免这种情况。具有相同 `scope.id` 的所有变更将串行运行,这意味着当它们被触发时,如果该作用域已有变更在进行中,它们将以 `isPaused: true` 状态开始。它们会被放入队列,并在轮到它们时自动恢复。 + +[//]: # 'ExampleScopes' + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +}) +``` + +[//]: # 'ExampleScopes' +[//]: # 'Materials' + +## 延伸阅读 + +有关变更的更多信息,请查看社区资源中的 [#12: 掌握 React Query 中的变更](../community/tkdodos-blog.md#12-mastering-mutations-in-react-query)。 + +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/react/guides/network-mode.md b/docs/zh-hans/framework/react/guides/network-mode.md new file mode 100644 index 00000000000..0e3bea25b64 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/network-mode.md @@ -0,0 +1,47 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:13:05.806Z' +id: network-mode +title: 网络模式 +--- +TanStack Query 提供了三种不同的网络模式,用于区分在无网络连接时[查询(Queries)](./queries.md)和[变更(Mutations)](./mutations.md)的行为。该模式可以针对每个查询/变更单独设置,也可以通过查询/变更的全局默认值进行配置。 + +由于 TanStack Query 最常与数据获取库配合使用,其默认网络模式为[在线模式(online)](#network-mode-online)。 + +## 网络模式:在线模式(online) + +在此模式下,只有存在网络连接时才会触发查询和变更。这是默认模式。如果因无网络连接导致查询请求无法发起,该查询将始终保持当前状态(`pending`、`error`、`success`)。此外,系统会额外暴露一个[获取状态(fetchStatus)](./queries.md#fetchstatus),其可能值为: + +- `fetching`:`queryFn` 正在真实执行——请求正在传输中 +- `paused`:查询未执行——将保持`paused`状态直至恢复网络连接 +- `idle`:查询既未在获取数据也未处于暂停状态 + +为方便使用,系统会根据此状态派生出`isFetching`和`isPaused`标志。 + +> 需注意:仅检查`pending`状态可能不足以显示加载动画。如果首次挂载查询时无网络连接,查询可能处于`state: 'pending'`但`fetchStatus: 'paused'`状态。 + +若查询因网络在线而启动,但在获取过程中断网,TanStack Query 也会暂停重试机制。暂停的查询将在恢复网络连接后继续执行。此行为与`refetchOnReconnect`(该模式默认值为`true`)无关,因为这不是"重新获取",而是"继续执行"。若查询期间被[取消(cancelled)](./query-cancellation.md),则不会继续。 + +## 网络模式:始终模式(always) + +在此模式下,TanStack Query 会忽略在线/离线状态始终执行获取。如果您在不需要活跃网络连接的环境中(例如仅从`AsyncStorage`读取数据,或`queryFn`直接返回`Promise.resolve(5)`)使用 TanStack Query,此模式是理想选择。 + +- 查询永远不会因断网进入`paused`状态 +- 重试也不会暂停——查询失败后将直接进入`error`状态 +- 此模式下`refetchOnReconnect`默认为`false`,因为网络重连不再意味着需要重新获取过期查询。您仍可手动开启该选项 + +## 网络模式:离线优先模式(offlineFirst) + +此模式是前两种模式的折中方案,TanStack Query 会执行一次`queryFn`,但暂停后续重试。这对于使用[Service Worker拦截请求实现缓存](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers)的[离线优先PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers),或通过[Cache-Control头](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#the_cache-control_header)实现HTTP缓存的场景非常实用。 + +在这些场景中,首次获取可能因离线存储/缓存而成功。若缓存未命中,网络请求将发出并失败,此时该模式的行为与`online`查询相同——暂停重试。 + +## 开发者工具(Devtools) + +[TanStack Query开发者工具](../devtools.md)会显示处于`paused`状态的查询——这些查询本应获取数据但因断网而暂停。工具还提供_模拟离线行为_的切换按钮。请注意该按钮不会实际干扰网络连接(您可在浏览器开发者工具中操作),而是会将[OnlineManager](../../../reference/onlineManager.md)设置为离线状态。 + +## 函数签名(Signature) + +- `networkMode: 'online' | 'always' | 'offlineFirst'` + - 可选参数 + - 默认值:`'online'` diff --git a/docs/zh-hans/framework/react/guides/optimistic-updates.md b/docs/zh-hans/framework/react/guides/optimistic-updates.md new file mode 100644 index 00000000000..30fdeeae1ff --- /dev/null +++ b/docs/zh-hans/framework/react/guides/optimistic-updates.md @@ -0,0 +1,187 @@ +--- +source-updated-at: '2025-03-26T09:27:36.000Z' +translation-updated-at: '2025-05-06T04:12:25.239Z' +id: optimistic-updates +title: 乐观更新 +--- +React Query 提供了两种在变更操作完成前乐观更新 UI 的方式。你可以直接使用 `onMutate` 选项更新缓存,或者利用 `useMutation` 返回的 `variables` 来更新 UI。 + +## 通过 UI 更新 + +这是更简单的实现方式,因为它不直接与缓存交互。 + +[//]: # 'ExampleUI1' + +```tsx +const addTodoMutation = useMutation({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + // 确保返回查询失效的 Promise + // 这样变更会保持 `pending` 状态直到重新获取完成 + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), +}) + +const { isPending, submittedAt, variables, mutate, isError } = addTodoMutation +``` + +[//]: # 'ExampleUI1' + +之后你可以访问 `addTodoMutation.variables` 获取新增的待办事项。在渲染查询结果的 UI 列表中,当变更处于 `isPending` 状态时,可以临时添加一项: + +[//]: # 'ExampleUI2' + +```tsx +
      + {todoQuery.items.map((todo) => ( +
    • {todo.text}
    • + ))} + {isPending &&
    • {variables}
    • } +
    +``` + +[//]: # 'ExampleUI2' + +我们通过不同的 `opacity` 样式渲染临时项,直到变更完成。成功后该项会自动消失。如果重新获取成功,列表中会显示为正常项。 + +若变更失败,该项同样会消失。但如需保留显示,可以通过检查变更的 `isError` 状态实现。出错时 `variables` 不会被清除,因此仍可访问,甚至显示重试按钮: + +[//]: # 'ExampleUI3' + +```tsx +{ + isError && ( +
  • + {variables} + +
  • + ) +} +``` + +[//]: # 'ExampleUI3' + +### 当变更与查询不在同一组件时 + +若变更与查询位于同一组件,此方案效果良好。但通过专用的 `useMutationState` 钩子,你也能在其他组件访问所有变更。最佳实践是配合 `mutationKey` 使用: + +[//]: # 'ExampleUI4' + +```tsx +// 应用某处 +const { mutate } = useMutation({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + mutationKey: ['addTodo'], +}) + +// 在其他位置访问 variables +const variables = useMutationState({ + filters: { mutationKey: ['addTodo'], status: 'pending' }, + select: (mutation) => mutation.state.variables, +}) +``` + +[//]: # 'ExampleUI4' + +`variables` 会是 `Array` 类型,因为可能同时存在多个进行中的变更。如需为项生成唯一键,还可选择 `mutation.state.submittedAt`。这让处理并发的乐观更新变得轻松。 + +## 通过缓存更新 + +在变更前乐观更新状态时,存在操作失败的可能。多数情况下只需重新获取乐观查询即可恢复至真实服务端状态。但某些场景下重新获取可能失效,此时需手动回滚更新。 + +为此,`useMutation` 的 `onMutate` 处理程序允许返回一个值,该值将作为末参数传递给 `onError` 和 `onSettled` 处理程序。通常传递回滚函数最为实用。 + +### 新增待办事项时更新列表 + +[//]: # 'Example' + +```tsx +const queryClient = useQueryClient() + +useMutation({ + mutationFn: updateTodo, + // 当 mutate 调用时: + onMutate: async (newTodo) => { + // 取消所有进行中的重新获取 + // (避免覆盖我们的乐观更新) + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 保存当前值的快照 + const previousTodos = queryClient.getQueryData(['todos']) + + // 乐观更新至新值 + queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) + + // 返回包含快照值的上下文对象 + return { previousTodos } + }, + // 若变更失败 + // 使用 onMutate 返回的上下文回滚 + onError: (err, newTodo, context) => { + queryClient.setQueryData(['todos'], context.previousTodos) + }, + // 无论成功失败都重新获取: + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), +}) +``` + +[//]: # 'Example' + +### 更新单个待办事项 + +[//]: # 'Example2' + +```tsx +useMutation({ + mutationFn: updateTodo, + // 当 mutate 调用时: + onMutate: async (newTodo) => { + // 取消相关重新获取 + await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] }) + + // 保存旧值快照 + const previousTodo = queryClient.getQueryData(['todos', newTodo.id]) + + // 乐观更新 + queryClient.setQueryData(['todos', newTodo.id], newTodo) + + // 返回包含新旧值的上下文 + return { previousTodo, newTodo } + }, + // 失败时使用上方返回的上下文 + onError: (err, newTodo, context) => { + queryClient.setQueryData( + ['todos', context.newTodo.id], + context.previousTodo, + ) + }, + // 总是重新获取: + onSettled: (newTodo) => + queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] }), +}) +``` + +[//]: # 'Example2' + +也可用 `onSettled` 替代单独的 `onError` 和 `onSuccess` 处理程序: + +[//]: # 'Example3' + +```tsx +useMutation({ + mutationFn: updateTodo, + // ... + onSettled: async (newTodo, error, variables, context) => { + if (error) { + // 错误处理 + } + }, +}) +``` + +[//]: # 'Example3' + +## 方案选择建议 + +若只需在单一位置显示乐观结果,使用 `variables` 直接更新 UI 的方案代码更少且更易理解。例如完全无需处理回滚。 + +但若界面有多个位置需要感知更新,直接操作缓存会自动同步所有相关位置。 diff --git a/docs/zh-hans/framework/react/guides/paginated-queries.md b/docs/zh-hans/framework/react/guides/paginated-queries.md new file mode 100644 index 00000000000..5fa353857f8 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/paginated-queries.md @@ -0,0 +1,94 @@ +--- +source-updated-at: '2024-07-18T12:50:17.000Z' +translation-updated-at: '2025-05-06T04:09:51.300Z' +id: paginated-queries +title: 分页查询 +--- +分页数据的渲染是一种非常常见的 UI 模式,在 TanStack Query 中,只需将页码信息包含在查询键 (query key) 中即可"开箱即用": + +[//]: # 'Example' + +```tsx +const result = useQuery({ + queryKey: ['projects', page], + queryFn: fetchProjects, +}) +``` + +[//]: # 'Example' + +然而,如果你运行这个简单示例,可能会注意到一个奇怪的现象: + +**UI 会在 `success` 和 `pending` 状态之间不断切换,因为每个新页面都被视为一个全新的查询。** + +这种体验并不理想,但不幸的是,这正是当今许多工具的工作方式。但 TanStack Query 不同!你可能已经猜到了,TanStack Query 提供了一个名为 `placeholderData` 的强大功能来解决这个问题。 + +## 使用 `placeholderData` 实现更好的分页查询 + +考虑以下示例,我们理想情况下希望递增查询的页码 (pageIndex) 或游标 (cursor)。如果使用 `useQuery`,**技术上仍然可以正常工作**,但随着为每个页面创建和销毁不同的查询,UI 会在 `success` 和 `pending` 状态之间跳跃。通过将 `placeholderData` 设置为 `(previousData) => previousData` 或使用 TanStack Query 导出的 `keepPreviousData` 函数,我们可以获得以下改进: + +- **即使查询键发生了变化,在请求新数据时,上次成功获取的数据仍然可用** +- 当新数据到达时,之前的 `data` 会无缝切换以显示新数据 +- 可以通过 `isPlaceholderData` 判断当前查询提供的是哪类数据 + +[//]: # 'Example2' + +```tsx +import { keepPreviousData, useQuery } from '@tanstack/react-query' +import React from 'react' + +function Todos() { + const [page, setPage] = React.useState(0) + + const fetchProjects = (page = 0) => + fetch('/api/projects?page=' + page).then((res) => res.json()) + + const { isPending, isError, error, data, isFetching, isPlaceholderData } = + useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + placeholderData: keepPreviousData, + }) + + return ( +
    + {isPending ? ( +
    Loading...
    + ) : isError ? ( +
    Error: {error.message}
    + ) : ( +
    + {data.projects.map((project) => ( +

    {project.name}

    + ))} +
    + )} + Current Page: {page + 1} + + + {isFetching ? Loading... : null} +
    + ) +} +``` + +[//]: # 'Example2' + +## 使用 `placeholderData` 延迟无限查询结果 + +虽然不太常见,但 `placeholderData` 选项与 `useInfiniteQuery` 钩子也能完美配合,因此你可以让用户在无限查询键随时间变化时,仍然无缝查看缓存数据。 diff --git a/docs/zh-hans/framework/react/guides/parallel-queries.md b/docs/zh-hans/framework/react/guides/parallel-queries.md new file mode 100644 index 00000000000..0a35d63a414 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/parallel-queries.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:09:37.356Z' +id: parallel-queries +title: 并行查询 +--- +"并行 (Parallel)" 查询是指同时执行多个查询,以最大化数据获取的并发性。 + +## 手动并行查询 + +当并行查询的数量固定不变时,使用并行查询**无需额外操作**。只需并列使用任意数量的 TanStack Query 提供的 `useQuery` 和 `useInfiniteQuery` 钩子即可! + +[//]: # '示例' + +```tsx +function App () { + // 以下查询将并行执行 + const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers }) + const teamsQuery = useQuery({ queryKey: ['teams'], queryFn: fetchTeams }) + const projectsQuery = useQuery({ queryKey: ['projects'], queryFn: fetchProjects }) + ... +} +``` + +[//]: # '示例' +[//]: # '提示' + +> 在 suspense 模式下使用 React Query 时,这种并行模式会失效,因为第一个查询会在内部抛出 promise 并挂起组件,导致其他查询无法执行。解决方案是使用 `useSuspenseQueries` 钩子(推荐方式),或通过为每个 `useSuspenseQuery` 实例创建独立组件来实现并行控制。 + +[//]: # '提示' + +## 使用 `useQueries` 实现动态并行查询 + +[//]: # '动态并行介绍' + +如果需要在每次渲染时动态调整查询数量,手动查询的方式会违反钩子规则。此时应使用 TanStack Query 提供的 `useQueries` 钩子,它可以动态执行任意数量的并行查询。 + +[//]: # '动态并行介绍' + +`useQueries` 接收一个包含 **queries 键**的**配置对象**,其值为**查询对象数组**。该钩子返回一个**查询结果数组**: + +[//]: # '示例2' + +```tsx +function App({ users }) { + const userQueries = useQueries({ + queries: users.map((user) => { + return { + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + } + }), + }) +} +``` + +[//]: # '示例2' diff --git a/docs/zh-hans/framework/react/guides/placeholder-query-data.md b/docs/zh-hans/framework/react/guides/placeholder-query-data.md new file mode 100644 index 00000000000..0de9edd1494 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/placeholder-query-data.md @@ -0,0 +1,102 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:09:43.190Z' +id: placeholder-query-data +title: 占位查询数据 +--- +## 什么是占位数据 (Placeholder Data)? + +占位数据允许查询表现得好像已经拥有数据,类似于 `initialData` 选项,但**这些数据不会被持久化到缓存中**。这在以下场景中非常有用:当实际数据还在后台获取时,你已经拥有部分(或模拟)数据可以成功渲染查询结果。 + +> 示例:一篇博客文章的查询可以从父级博客列表获取仅包含标题和正文片段缩略的“预览”数据。你可能不希望将这些部分数据持久化到独立查询的结果中,但它对于尽快展示内容布局非常有用,同时实际查询会继续获取完整数据。 + +有几种方式可以在需要之前为查询提供占位数据: + +- 声明式: + - 为查询提供 `placeholderData`,以便在缓存为空时预填充数据 +- 命令式: + - [使用 `queryClient` 和 `placeholderData` 选项预取或获取数据](./prefetching.md) + +当我们使用 `placeholderData` 时,查询不会处于 `pending` 状态——它会直接从 `success` 状态开始,因为我们有可以显示的 `data`,即使这些数据只是“占位”数据。为了将其与“真实”数据区分开,查询结果中还会将 `isPlaceholderData` 标志设为 `true`。 + +## 作为值的占位数据 + +[//]: # 'ExampleValue' + +```tsx +function Todos() { + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData: placeholderTodos, + }) +} +``` + +[//]: # 'ExampleValue' +[//]: # 'Memoization' + +### 占位数据记忆化 (Memoization) + +如果获取查询占位数据的过程计算密集,或者你不想在每次渲染时都执行,可以对值进行记忆化处理: + +```tsx +function Todos() { + const placeholderData = useMemo(() => generateFakeTodos(), []) + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData, + }) +} +``` + +[//]: # 'Memoization' + +## 作为函数的占位数据 + +`placeholderData` 也可以是一个函数,通过它你可以访问“先前”成功查询的数据和查询元信息。这在你想用一个查询的数据作为另一个查询的占位数据时非常有用。当查询键 (QueryKey) 发生变化时(例如从 `['todos', 1]` 变为 `['todos', 2]`),我们可以继续显示“旧”数据,而不是在数据从一个查询过渡到另一个查询时显示加载状态。更多信息请参阅[分页查询](./paginated-queries.md)。 + +[//]: # 'ExampleFunction' + +```tsx +const result = useQuery({ + queryKey: ['todos', id], + queryFn: () => fetch(`/todos/${id}`), + placeholderData: (previousData, previousQuery) => previousData, +}) +``` + +[//]: # 'ExampleFunction' + +### 从缓存获取占位数据 + +在某些情况下,你可以从另一个查询的缓存结果中获取占位数据。一个典型的例子是从博客文章列表查询的缓存数据中搜索文章的预览版本,然后将其用作独立文章查询的占位数据: + +[//]: # 'ExampleCache' + +```tsx +function Todo({ blogPostId }) { + const queryClient = useQueryClient() + const result = useQuery({ + queryKey: ['blogPost', blogPostId], + queryFn: () => fetch(`/blogPosts/${blogPostId}`), + placeholderData: () => { + // 使用 'blogPosts' 查询中的小型/预览版博客文章数据 + // 作为当前博客文章查询的占位数据 + return queryClient + .getQueryData(['blogPosts']) + ?.find((d) => d.id === blogPostId) + }, + }) +} +``` + +[//]: # 'ExampleCache' +[//]: # 'Materials' + +## 扩展阅读 + +要比较 `占位数据 (Placeholder Data)` 和 `初始数据 (Initial Data)` 的区别,请查看[社区资源](../community/tkdodos-blog.md#9-placeholder-and-initial-data-in-react-query)。 + +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/react/guides/prefetching.md b/docs/zh-hans/framework/react/guides/prefetching.md new file mode 100644 index 00000000000..db929dcb7e5 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/prefetching.md @@ -0,0 +1,435 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:11:22.321Z' +id: prefetching +title: 预获取与路由集成 +--- +当您预知或推测某块数据即将被需要时,可以通过预取 (prefetching) 提前将该数据填充到缓存中,从而获得更快的用户体验。 + +预取存在几种不同的实现模式: + +1. 在事件处理器中预取 +2. 在组件中预取 +3. 通过路由集成预取 +4. 在服务端渲染时预取(路由集成的另一种形式) + +本指南将探讨前三种模式,而第四种模式将在[《服务端渲染与注水指南》](./ssr.md)和[《高级服务端渲染指南》](./advanced-ssr.md)中深入讲解。 + +预取的一个典型应用场景是避免请求瀑布流 (Request Waterfalls),相关背景和原理详见[《性能与请求瀑布流指南》](./request-waterfalls.md)。 + +## prefetchQuery 与 prefetchInfiniteQuery + +在深入具体预取模式前,我们先了解 `prefetchQuery` 和 `prefetchInfiniteQuery` 函数的基础特性: + +- 默认情况下,这些函数会使用 `queryClient` 配置的默认 `staleTime` 来判断缓存中的现有数据是否新鲜或需要重新获取 +- 也可指定自定义的 `staleTime`:`prefetchQuery({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` + - 此 `staleTime` 仅作用于预取操作,仍需为 `useQuery` 调用单独设置 + - 若需忽略 `staleTime` 直接返回缓存中的可用数据,可使用 `ensureQueryData` 函数 + - 提示:在服务端预取时,建议为 `queryClient` 设置高于 `0` 的默认 `staleTime`,避免为每个预取调用单独传参 +- 如果预取的查询没有对应的 `useQuery` 实例,将在 `gcTime` 指定时间后被删除并进行垃圾回收 +- 这些函数返回 `Promise` 且不返回查询数据。如需获取数据,请改用 `fetchQuery`/`fetchInfiniteQuery` +- 预取函数不会抛出错误,因为它们通常会在 `useQuery` 中重试请求作为优雅降级方案。如需错误捕获,请改用 `fetchQuery`/`fetchInfiniteQuery` + +`prefetchQuery` 的使用示例如下: + +[//]: # 'ExamplePrefetchQuery' + +```tsx +const prefetchTodos = async () => { + // 该查询结果会像普通查询一样被缓存 + await queryClient.prefetchQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) +} +``` + +[//]: # 'ExamplePrefetchQuery' + +无限查询 (Infinite Queries) 的预取方式与常规查询相同。默认仅预取第一页数据并存储于指定 QueryKey 下。如需预取多页数据,可使用 `pages` 选项并配合 `getNextPageParam` 函数: + +[//]: # 'ExamplePrefetchInfiniteQuery' + +```tsx +const prefetchProjects = async () => { + // 该查询结果会像普通查询一样被缓存 + await queryClient.prefetchInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + pages: 3, // 预取前 3 页数据 + }) +} +``` + +[//]: # 'ExamplePrefetchInfiniteQuery' + +接下来我们将探讨如何在不同场景下应用这些预取方法。 + +## 在事件处理器中预取 + +最直接的预取方式是在用户交互时触发。以下示例通过 `onMouseEnter` 或 `onFocus` 事件调用 `queryClient.prefetchQuery`: + +[//]: # 'ExampleEventHandler' + +```tsx +function ShowDetailsButton() { + const queryClient = useQueryClient() + + const prefetch = () => { + queryClient.prefetchQuery({ + queryKey: ['details'], + queryFn: getDetailsData, + // 预取仅在数据早于 staleTime 时触发 + // 此类场景务必设置该参数 + staleTime: 60000, + }) + } + + return ( + + ) +} +``` + +[//]: # 'ExampleEventHandler' + +## 在组件中预取 + +当预知子组件需要某块数据但需等待其他查询完成时,组件生命周期内的预取非常有用。以下示例来自请求瀑布流指南: + +[//]: # 'ExampleComponent' + +```tsx +function Article({ id }) { + const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + if (isPending) { + return '文章加载中...' + } + + return ( + <> + + + + + ) +} + +function Comments({ id }) { + const { data, isPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + ... +} +``` + +[//]: # 'ExampleComponent' + +此时会产生如下请求瀑布流: + +``` +1. |> getArticleById() +2. |> getArticleCommentsById() +``` + +如指南所述,优化方案之一是将 `getArticleCommentsById` 查询提升到父组件并通过 props 传递。但当组件层级复杂或关联性较弱时,可以采用预取方案: + +[//]: # 'ExampleParentComponent' + +```tsx +function Article({ id }) { + const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + // 预取操作 + useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + // 可选优化:避免查询变化导致的重复渲染 + notifyOnChangeProps: [], + }) + + if (isPending) { + return '文章加载中...' + } + + return ( + <> + + + + + ) +} + +function Comments({ id }) { + const { data, isPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + ... +} +``` + +[//]: # 'ExampleParentComponent' + +此时请求变为并行: + +``` +1. |> getArticleById() +1. |> getArticleCommentsById() +``` + +[//]: # 'Suspense' + +若需结合 Suspense 使用预取,需采用特殊处理。由于 `useSuspenseQueries` 会阻塞渲染,而 `useQuery` 又会在 suspenseful query 解析后才启动预取,此时应使用 [`usePrefetchQuery`](../reference/usePrefetchQuery.md) 或 [`usePrefetchInfiniteQuery`](../reference/usePrefetchInfiniteQuery.md) 钩子。 + +实际需要数据的组件可使用 `useSuspenseQuery`。建议为次级查询包裹单独的 `` 边界,避免阻塞主要数据渲染: + +```tsx +function ArticleLayout({ id }) { + usePrefetchQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + return ( + +
    + + ) +} + +function Article({ id }) { + const { data: articleData, isPending } = useSuspenseQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + ... +} +``` + +另一种方案是在查询函数内预取,适用于文章与评论数据强关联的场景: + +```tsx +const queryClient = useQueryClient() +const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: (...args) => { + queryClient.prefetchQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + return getArticleById(...args) + }, +}) +``` + +Effect 中的预取也可行,但注意若同一组件使用 `useSuspenseQuery`,effect 会在查询完成后才执行: + +```tsx +const queryClient = useQueryClient() + +useEffect(() => { + queryClient.prefetchQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) +}, [queryClient, id]) +``` + +总结组件内预取的四种方案(根据场景选择): +- 使用 `usePrefetchQuery` 或 `usePrefetchInfiniteQuery` 在 Suspense 边界前预取 +- 使用 `useQuery` 或 `useSuspenseQueries` 并忽略结果 +- 在查询函数内预取 +- 在 effect 中预取 + +接下来我们看更复杂的案例。 + +[//]: # 'Suspense' + +### 依赖查询与代码分割 + +有时需要基于其他查询结果条件式预取。以下示例来自[《性能与请求瀑布流指南》](./request-waterfalls.md): + +[//]: # 'ExampleConditionally1' + +```tsx +// 动态加载 GraphFeedItem 组件 +// 只有在渲染时才会开始加载 +const GraphFeedItem = React.lazy(() => import('./GraphFeedItem')) + +function Feed() { + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: getFeed, + }) + + if (isPending) { + return '加载动态中...' + } + + return ( + <> + {data.map((feedItem) => { + if (feedItem.type === 'GRAPH') { + return + } + + return + })} + + ) +} + +// GraphFeedItem.tsx +function GraphFeedItem({ feedItem }) { + const { data, isPending } = useQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + + ... +} +``` + +[//]: # 'ExampleConditionally1' + +此时会产生双重请求瀑布: + +``` +1. |> getFeed() +2. |> JS for +3. |> getGraphDataById() +``` + +若无法通过 API 重构让 `getFeed()` 返回 `getGraphDataById()` 数据,虽然无法消除 `getFeed->getGraphDataById` 瀑布流,但通过条件预取可实现代码与数据并行加载(以下示例在查询函数中实现): + +[//]: # 'ExampleConditionally2' + +```tsx +function Feed() { + const queryClient = useQueryClient() + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: async (...args) => { + const feed = await getFeed(...args) + + for (const feedItem of feed) { + if (feedItem.type === 'GRAPH') { + queryClient.prefetchQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + } + } + + return feed + } + }) + + ... +} +``` + +[//]: # 'ExampleConditionally2' + +此时加载流程变为: + +``` +1. |> getFeed() +2. |> JS for +2. |> getGraphDataById() +``` + +但需权衡的是:`getGraphDataById` 的代码现在会打包到父组件中。若 `GraphFeedItem` 出现频率高,这种优化值得;若非常罕见,则可能不划算。 + +[//]: # 'Router' + +## 路由集成 + +由于组件树内的数据获取容易引发请求瀑布流,而各种修复方案又会在应用中不断累积,将预取集成到路由层成为颇具吸引力的解决方案。 + +这种方式需要为每个路由预先声明其组件树所需的数据。传统服务端渲染 (SSR) 应用由于需要在渲染前加载所有数据,长期采用此方案(详见[《服务端渲染与注水指南》](./ssr.md))。 + +以下以 [Tanstack Router](https://tanstack.com/router) 为例展示客户端方案(省略了大量配置代码,完整示例参见 [Tanstack Router 文档](https://tanstack.com/router/latest/docs)): + +路由集成时,可选择阻塞渲染直到数据加载完成,或不等待结果立即开始渲染。也可混合使用——等待关键数据同时预取次要数据。本例中 `/article` 路由会等待文章数据加载,同时预取但不阻塞评论数据: + +```tsx +const queryClient = new QueryClient() +const routerContext = new RouterContext() +const rootRoute = routerContext.createRootRoute({ + component: () => { ... } +}) + +const articleRoute = new Route({ + getParentRoute: () => rootRoute, + path: 'article', + beforeLoad: () => { + return { + articleQueryOptions: { queryKey: ['article'], queryFn: fetchArticle }, + commentsQueryOptions: { queryKey: ['comments'], queryFn: fetchComments }, + } + }, + loader: async ({ + context: { queryClient }, + routeContext: { articleQueryOptions, commentsQueryOptions }, + }) => { + // 立即预取评论但不阻塞 + queryClient.prefetchQuery(commentsQueryOptions) + + // 阻塞直到文章数据加载完成 + await queryClient.prefetchQuery(articleQueryOptions) + }, + component: ({ useRouteContext }) => { + const { articleQueryOptions, commentsQueryOptions } = useRouteContext() + const articleQuery = useQuery(articleQueryOptions) + const commentsQuery = useQuery(commentsQueryOptions) + + return ( + ... + ) + }, + errorComponent: () => '出错了!', +}) +``` + +其他路由器的集成方案也可行,参见 [React Router 示例](../examples/react/react-router)。 + +[//]: # 'Router' + +## 手动初始化查询 + +如果已同步获取到查询数据,可直接使用 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 通过键值对添加或更新缓存结果: + +[//]: # 'ExampleManualPriming' + +```tsx +queryClient.setQueryData(['todos'], todos) +``` + +[//]: # 'ExampleManualPriming' +[//]: # 'Materials' + +## 扩展阅读 + +- 深度探讨如何预先填充查询缓存,请参阅社区资源中的 [#17: 初始化查询缓存](../community/tkdodos-blog.md#17-seeding-the-query-cache) +- 服务端路由与框架的集成方案类似客户端方案,但需额外处理服务端到客户端的数据注水,详见[《服务端渲染与注水指南》](./ssr.md) + +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/react/guides/queries.md b/docs/zh-hans/framework/react/guides/queries.md new file mode 100644 index 00000000000..4f977493dc6 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/queries.md @@ -0,0 +1,146 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:09:52.666Z' +id: queries +title: 查询 +--- +## 查询基础 + +查询是与**唯一键**绑定的、对异步数据源的声明式依赖。查询可用于任何基于 Promise 的方法(包括 GET 和 POST 方法)从服务器获取数据。如果您的方法会修改服务器上的数据,建议改用[变更](./mutations.md)。 + +要在组件或自定义钩子中订阅查询,至少需要调用 `useQuery` 钩子并传入: + +- **该查询的唯一键** +- 一个返回 Promise 的函数,该 Promise 会: + - 解析数据,或 + - 抛出错误 + +[//]: # '示例' + +```tsx +import { useQuery } from '@tanstack/react-query' + +function App() { + const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +} +``` + +[//]: # '示例' + +您提供的**唯一键**将在内部用于重新获取、缓存和在应用程序中共享查询。 + +`useQuery` 返回的查询结果包含模板渲染和数据使用所需的所有信息: + +[//]: # '示例2' + +```tsx +const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +``` + +[//]: # '示例2' + +`result` 对象包含几个非常重要的状态,您需要了解这些状态才能高效工作。查询在任意时刻只能处于以下一种状态: + +- `isPending` 或 `status === 'pending'` - 查询尚无数据 +- `isError` 或 `status === 'error'` - 查询遇到错误 +- `isSuccess` 或 `status === 'success'` - 查询成功且数据可用 + +除了这些主要状态外,根据查询状态还可获取更多信息: + +- `error` - 如果查询处于 `isError` 状态,可通过 `error` 属性获取错误信息。 +- `data` - 如果查询处于 `isSuccess` 状态,可通过 `data` 属性获取数据。 +- `isFetching` - 在任何状态下,如果查询正在获取数据(包括后台重新获取),`isFetching` 将为 `true`。 + +对于**大多数**查询,通常只需检查 `isPending` 状态,然后是 `isError` 状态,最后即可假定数据可用并渲染成功状态: + +[//]: # '示例3' + +```tsx +function Todos() { + const { isPending, isError, data, error } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + }) + + if (isPending) { + return 加载中... + } + + if (isError) { + return 错误:{error.message} + } + + // 此时可以认为 `isSuccess === true` + return ( +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + ) +} +``` + +[//]: # '示例3' + +如果不喜欢使用布尔值,也可以始终使用 `status` 状态: + +[//]: # '示例4' + +```tsx +function Todos() { + const { status, data, error } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + }) + + if (status === 'pending') { + return 加载中... + } + + if (status === 'error') { + return 错误:{error.message} + } + + // 同样 status === 'success',但 "else" 逻辑也适用 + return ( +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + ) +} +``` + +[//]: # '示例4' + +如果在访问 `data` 之前检查了 `pending` 和 `error`,TypeScript 也会正确缩小 `data` 的类型范围。 + +### 获取状态 (FetchStatus) + +除了 `status` 字段外,您还会获得一个额外的 `fetchStatus` 属性,其可选值包括: + +- `fetchStatus === 'fetching'` - 查询正在获取数据。 +- `fetchStatus === 'paused'` - 查询希望获取数据,但被暂停。详情请参阅[网络模式](./network-mode.md)指南。 +- `fetchStatus === 'idle'` - 查询当前未进行任何操作。 + +### 为何有两种不同状态? + +后台重新获取和"过时但重新验证"逻辑使得 `status` 和 `fetchStatus` 的所有组合都可能出现。例如: + +- 处于 `success` 状态的查询通常处于 `idle` 获取状态,但如果正在进行后台重新获取,也可能处于 `fetching` 状态。 +- 刚挂载且无数据的查询通常处于 `pending` 状态和 `fetching` 获取状态,但如果无网络连接,也可能处于 `paused` 状态。 + +因此请记住,查询可能处于 `pending` 状态但并未实际获取数据。经验法则: + +- `status` 提供关于 `data` 的信息:是否有数据? +- `fetchStatus` 提供关于 `queryFn` 的信息:是否正在运行? + +[//]: # '材料' + +## 延伸阅读 + +如需了解执行状态检查的替代方法,请参阅[社区资源](../community/tkdodos-blog.md#4-status-checks-in-react-query)。 + +[//]: # '材料' diff --git a/docs/zh-hans/framework/react/guides/query-cancellation.md b/docs/zh-hans/framework/react/guides/query-cancellation.md new file mode 100644 index 00000000000..366c120e687 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/query-cancellation.md @@ -0,0 +1,194 @@ +--- +source-updated-at: '2025-03-31T09:10:06.000Z' +translation-updated-at: '2025-05-06T04:09:59.909Z' +id: query-cancellation +title: 查询取消 +--- +TanStack Query 为每个查询函数提供了一个 [`AbortSignal` 实例](https://developer.mozilla.org/docs/Web/API/AbortSignal)。当查询过期或变为非活跃状态时,该 `signal` 将被中止。这意味着所有查询均可取消,您可以根据需要在查询函数内部响应取消操作。最棒的是,您可以在享受自动取消带来的所有优势的同时,继续使用普通的 async/await 语法。 + +`AbortController` API 在[大多数运行时环境](https://developer.mozilla.org/docs/Web/API/AbortController#browser_compatibility)中可用,但如果您的运行时环境不支持,则需要提供 polyfill。现有[多种 polyfill 可选](https://www.npmjs.com/search?q=abortcontroller%20polyfill)。 + +## 默认行为 + +默认情况下,在 Promise 解析前卸载或不再使用的查询_不会_被取消。这意味着 Promise 解析后,结果数据仍会保留在缓存中。这对于已经开始接收查询但尚未完成就卸载组件的情况非常有用。如果您再次挂载组件且查询尚未被垃圾回收,数据将仍然可用。 + +但如果消费了 `AbortSignal`,Promise 将被取消(例如中止 fetch 请求),因此查询也必须取消。取消查询将导致其状态_回退_到先前的状态。 + +## 使用 `fetch` + +[//]: # '示例' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const todosResponse = await fetch('/todos', { + // 将 signal 传递给 fetch + signal, + }) + const todos = await todosResponse.json() + + const todoDetails = todos.map(async ({ details }) => { + const response = await fetch(details, { + // 或传递给多个请求 + signal, + }) + return response.json() + }) + + return Promise.all(todoDetails) + }, +}) +``` + +[//]: # '示例' + +## 使用 `axios` [v0.22.0+](https://github.com/axios/axios/releases/tag/v0.22.0) + +[//]: # '示例2' + +```tsx +import axios from 'axios' + +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => + axios.get('/todos', { + // 将 signal 传递给 `axios` + signal, + }), +}) +``` + +[//]: # '示例2' + +### 使用低于 v0.22.0 版本的 `axios` + +[//]: # '示例3' + +```tsx +import axios from 'axios' + +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + // 为该请求创建新的 CancelToken 源 + const CancelToken = axios.CancelToken + const source = CancelToken.source() + + const promise = axios.get('/todos', { + // 将 source token 传递给请求 + cancelToken: source.token, + }) + + // 如果 TanStack Query 发出中止信号,则取消请求 + signal?.addEventListener('abort', () => { + source.cancel('Query was cancelled by TanStack Query') + }) + + return promise + }, +}) +``` + +[//]: # '示例3' + +## 使用 `XMLHttpRequest` + +[//]: # '示例4' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + return new Promise((resolve, reject) => { + var oReq = new XMLHttpRequest() + oReq.addEventListener('load', () => { + resolve(JSON.parse(oReq.responseText)) + }) + signal?.addEventListener('abort', () => { + oReq.abort() + reject() + }) + oReq.open('GET', '/todos') + oReq.send() + }) + }, +}) +``` + +[//]: # '示例4' + +## 使用 `graphql-request` + +可以在客户端的 `request` 方法中设置 `AbortSignal`。 + +[//]: # '示例5' + +```tsx +const client = new GraphQLClient(endpoint) + +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + client.request({ document: query, signal }) + }, +}) +``` + +[//]: # '示例5' + +## 使用低于 v4.0.0 版本的 `graphql-request` + +可以在 `GraphQLClient` 构造函数中设置 `AbortSignal`。 + +[//]: # '示例6' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + const client = new GraphQLClient(endpoint, { + signal, + }) + return client.request(query, variables) + }, +}) +``` + +[//]: # '示例6' + +## 手动取消 + +您可能需要手动取消查询。例如,如果请求耗时过长,可以允许用户点击取消按钮停止请求。只需调用 `queryClient.cancelQueries({ queryKey })` 即可取消查询并回退到之前的状态。如果您消费了传递给查询函数的 `signal`,TanStack Query 还会额外取消 Promise。 + +[//]: # '示例7' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, +}) + +const queryClient = useQueryClient() + +return ( + +) +``` + +[//]: # '示例7' + +## 限制 + +当使用 `Suspense` 钩子时,取消功能不可用:`useSuspenseQuery`、`useSuspenseQueries` 和 `useSuspenseInfiniteQuery`。 diff --git a/docs/zh-hans/framework/react/guides/query-functions.md b/docs/zh-hans/framework/react/guides/query-functions.md new file mode 100644 index 00000000000..d4062f898af --- /dev/null +++ b/docs/zh-hans/framework/react/guides/query-functions.md @@ -0,0 +1,120 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:09:46.316Z' +id: query-functions +title: 查询函数 +--- +# 查询函数 (Query Functions) + +查询函数可以是**任何返回 Promise 的函数**。返回的 Promise 应当**解析数据 (resolve the data)** 或**抛出错误 (throw an error)**。 + +以下都是有效的查询函数配置方式: + +[//]: # 'Example' + +```tsx +useQuery({ queryKey: ['todos'], queryFn: fetchAllTodos }) +useQuery({ queryKey: ['todos', todoId], queryFn: () => fetchTodoById(todoId) }) +useQuery({ + queryKey: ['todos', todoId], + queryFn: async () => { + const data = await fetchTodoById(todoId) + return data + }, +}) +useQuery({ + queryKey: ['todos', todoId], + queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]), +}) +``` + +[//]: # 'Example' + +## 错误处理与抛出 + +为了让 TanStack Query 判定查询发生了错误,查询函数**必须抛出错误**或返回一个**被拒绝的 Promise (rejected Promise)**。在查询函数中抛出的任何错误都会被持久化到查询的 `error` 状态中。 + +[//]: # 'Example2' + +```tsx +const { error } = useQuery({ + queryKey: ['todos', todoId], + queryFn: async () => { + if (somethingGoesWrong) { + throw new Error('Oh no!') + } + if (somethingElseGoesWrong) { + return Promise.reject(new Error('Oh no!')) + } + + return data + }, +}) +``` + +[//]: # 'Example2' + +## 与 `fetch` 等默认不抛出错误的客户端一起使用 + +虽然大多数工具如 `axios` 或 `graphql-request` 会自动为不成功的 HTTP 调用抛出错误,但像 `fetch` 这样的工具默认不会抛出错误。如果是这种情况,你需要自行抛出错误。以下是使用流行的 `fetch` API 实现这一点的简单方法: + +[//]: # 'Example3' + +```tsx +useQuery({ + queryKey: ['todos', todoId], + queryFn: async () => { + const response = await fetch('/todos/' + todoId) + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() + }, +}) +``` + +[//]: # 'Example3' + +## 查询函数变量 (Query Function Variables) + +查询键 (Query keys) 不仅用于唯一标识你要获取的数据,还会作为 QueryFunctionContext 的一部分方便地传递到你的查询函数中。虽然并非总是必要,但这使得在需要时可以提取查询函数: + +[//]: # 'Example4' + +```tsx +function Todos({ status, page }) { + const result = useQuery({ + queryKey: ['todos', { status, page }], + queryFn: fetchTodoList, + }) +} + +// 在查询函数中访问 key、status 和 page 变量! +function fetchTodoList({ queryKey }) { + const [_key, { status, page }] = queryKey + return new Promise() +} +``` + +[//]: # 'Example4' + +### 查询函数上下文 (QueryFunctionContext) + +`QueryFunctionContext` 是传递给每个查询函数的对象,包含以下属性: + +- `queryKey: QueryKey`: [查询键 (Query Keys)](./query-keys.md) +- `client: QueryClient`: [QueryClient](../../../reference/QueryClient.md) +- `signal?: AbortSignal` + - 由 TanStack Query 提供的 [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) 实例 + - 可用于 [查询取消 (Query Cancellation)](./query-cancellation.md) +- `meta: Record | undefined` + - 可选字段,可填充有关查询的附加信息 + +此外,[无限查询 (Infinite Queries)](./infinite-queries.md) 还会传递以下选项: + +- `pageParam: TPageParam` + - 用于获取当前页面的页面参数 +- `direction: 'forward' | 'backward'` + - **已弃用** + - 当前页面获取的方向 + - 要获取当前页面获取的方向,请从 `getNextPageParam` 和 `getPreviousPageParam` 向 `pageParam` 添加方向信息 diff --git a/docs/zh-hans/framework/react/guides/query-invalidation.md b/docs/zh-hans/framework/react/guides/query-invalidation.md new file mode 100644 index 00000000000..3c1128ad84e --- /dev/null +++ b/docs/zh-hans/framework/react/guides/query-invalidation.md @@ -0,0 +1,136 @@ +--- +source-updated-at: '2025-03-25T15:32:47.000Z' +translation-updated-at: '2025-05-06T04:09:50.856Z' +id: query-invalidation +title: 查询失效 +--- +## 查询失效 (Query Invalidation) + +单纯等待查询因陈旧而重新获取并不总是有效,特别是当您明确知道由于用户操作导致某查询数据已过期时。为此,`QueryClient` 提供了 `invalidateQueries` 方法,允许您智能地将查询标记为陈旧状态,并可能触发重新获取! + +[//]: # '示例' + +```tsx +// 使缓存中的所有查询失效 +queryClient.invalidateQueries() +// 使所有以 `todos` 开头的键的查询失效 +queryClient.invalidateQueries({ queryKey: ['todos'] }) +``` + +[//]: # '示例' + +> 注意:其他使用规范化缓存 (normalized caches) 的库会尝试通过命令式或模式推断来用新数据更新本地查询,而 TanStack Query 则为您提供工具来避免维护规范化缓存所需的手动操作,转而采用**定向失效、后台重新获取及最终的原子级更新**策略。 + +当使用 `invalidateQueries` 使查询失效时,会发生两件事: + +- 该查询被标记为陈旧。此状态会覆盖 `useQuery` 或相关钩子中配置的任何 `staleTime` 值 +- 如果该查询当前正通过 `useQuery` 或相关钩子渲染,还会在后台触发重新获取 + +## 通过 `invalidateQueries` 进行查询匹配 + +在使用 `invalidateQueries`、`removeQueries` 等支持部分查询匹配的 API 时,您可以通过前缀匹配多个查询,或精确匹配特定查询。关于可使用的过滤器类型,请参阅[查询过滤器](./filters.md#query-filters)。 + +以下示例中,我们使用 `todos` 前缀来使所有查询键以 `todos` 开头的查询失效: + +[//]: # '示例2' + +```tsx +import { useQuery, useQueryClient } from '@tanstack/react-query' + +// 从上下文中获取 QueryClient +const queryClient = useQueryClient() + +queryClient.invalidateQueries({ queryKey: ['todos'] }) + +// 下面的两个查询都将失效 +const todoListQuery = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, +}) +const todoListQuery = useQuery({ + queryKey: ['todos', { page: 1 }], + queryFn: fetchTodoList, +}) +``` + +[//]: # '示例2' + +您还可以通过向 `invalidateQueries` 方法传递更具体的查询键,来使带有特定变量的查询失效: + +[//]: # '示例3' + +```tsx +queryClient.invalidateQueries({ + queryKey: ['todos', { type: 'done' }], +}) + +// 下面的查询将失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +}) + +// 但下面的查询不会失效 +const todoListQuery = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, +}) +``` + +[//]: # '示例3' + +`invalidateQueries` API 非常灵活,如果您**只想**使那些没有额外变量或子键的 `todos` 查询失效,可以向 `invalidateQueries` 方法传递 `exact: true` 选项: + +[//]: # '示例4' + +```tsx +queryClient.invalidateQueries({ + queryKey: ['todos'], + exact: true, +}) + +// 下面的查询将失效 +const todoListQuery = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, +}) + +// 但下面的查询不会失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +}) +``` + +[//]: # '示例4' + +如果需要**更精细**的控制,可以向 `invalidateQueries` 方法传递一个断言函数。该函数会接收查询缓存中的每个 `Query` 实例,您可以通过返回 `true` 或 `false` 来决定是否使该查询失效: + +[//]: # '示例5' + +```tsx +queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10, +}) + +// 下面的查询将失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { version: 20 }], + queryFn: fetchTodoList, +}) + +// 下面的查询将失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { version: 10 }], + queryFn: fetchTodoList, +}) + +// 但下面的查询不会失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { version: 5 }], + queryFn: fetchTodoList, +}) +``` + +[//]: # '示例5' diff --git a/docs/zh-hans/framework/react/guides/query-keys.md b/docs/zh-hans/framework/react/guides/query-keys.md new file mode 100644 index 00000000000..915148e43dc --- /dev/null +++ b/docs/zh-hans/framework/react/guides/query-keys.md @@ -0,0 +1,103 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:09:40.213Z' +id: query-keys +title: 查询键 +--- +TanStack Query 的核心是基于查询键 (query keys) 为你管理查询缓存。查询键在顶层必须是一个数组,可以简单到只包含单个字符串的数组,也可以复杂到包含多个字符串和嵌套对象的数组。只要查询键是可序列化的,并且**能唯一标识查询的数据**,你就可以使用它! + +## 简单查询键 + +最简单的键形式是由常量值组成的数组。这种格式适用于: + +- 通用列表/索引资源 +- 非层级化资源 + +[//]: # '示例' + +```tsx +// 待办事项列表 +useQuery({ queryKey: ['todos'], ... }) + +// 其他任意内容! +useQuery({ queryKey: ['something', 'special'], ... }) +``` + +[//]: # '示例' + +## 带变量的数组键 + +当查询需要更多信息来唯一描述其数据时,你可以使用包含字符串和任意数量可序列化对象的数组。这适用于: + +- 层级化或嵌套资源 + - 通常会传递 ID、索引或其他原始值来唯一标识项目 +- 带附加参数的查询 + - 通常会传递包含附加选项的对象 + +[//]: # '示例2' + +```tsx +// 单个待办事项 +useQuery({ queryKey: ['todo', 5], ... }) + +// "预览"格式的单个待办事项 +useQuery({ queryKey: ['todo', 5, { preview: true }], ...}) + +// 已完成的待办事项列表 +useQuery({ queryKey: ['todos', { type: 'done' }], ... }) +``` + +[//]: # '示例2' + +## 查询键会被确定性地哈希处理! + +这意味着无论对象中键的顺序如何,以下所有查询都被视为相等: + +[//]: # '示例3' + +```tsx +useQuery({ queryKey: ['todos', { status, page }], ... }) +useQuery({ queryKey: ['todos', { page, status }], ...}) +useQuery({ queryKey: ['todos', { page, status, other: undefined }], ... }) +``` + +[//]: # '示例3' + +然而以下查询键并不相等。数组项的顺序很重要! + +[//]: # '示例4' + +```tsx +useQuery({ queryKey: ['todos', status, page], ... }) +useQuery({ queryKey: ['todos', page, status], ...}) +useQuery({ queryKey: ['todos', undefined, page, status], ...}) +``` + +[//]: # '示例4' + +## 如果查询函数依赖变量,请将其包含在查询键中 + +由于查询键唯一描述了它们获取的数据,因此应该包含查询函数中使用的任何**会变化**的变量。例如: + +[//]: # '示例5' + +```tsx +function Todos({ todoId }) { + const result = useQuery({ + queryKey: ['todos', todoId], + queryFn: () => fetchTodoById(todoId), + }) +} +``` + +[//]: # '示例5' + +请注意,查询键充当查询函数的依赖项。将依赖变量添加到查询键中可确保查询被独立缓存,并且每当变量变化时,_查询会自动重新获取_(取决于你的 `staleTime` 设置)。更多信息和示例请参阅[全面依赖项](../../../eslint/exhaustive-deps.md)部分。 + +[//]: # '材料' + +## 延伸阅读 + +关于在大型应用中组织查询键的技巧,请参阅[高效 React Query 键](../community/tkdodos-blog.md#8-effective-react-query-keys),并查看社区资源中的[查询键工厂包](../community/community-projects.md#query-key-factory)。 + +[//]: # '材料' diff --git a/docs/zh-hans/framework/react/guides/query-options.md b/docs/zh-hans/framework/react/guides/query-options.md new file mode 100644 index 00000000000..95f89d8fafc --- /dev/null +++ b/docs/zh-hans/framework/react/guides/query-options.md @@ -0,0 +1,52 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:06:24.141Z' +id: query-options +title: 查询选项 +--- +## 查询选项 (Query Options) + +在多个地方共享 `queryKey` 和 `queryFn` 同时保持它们彼此关联的最佳方式之一是使用 `queryOptions` 辅助函数。在运行时,这个辅助函数仅返回你传入的内容,但[配合 TypeScript 使用时](../typescript.md#typing-query-options)它能带来诸多优势。你可以在一个地方定义查询的所有可能选项,并同时获得完整的类型推断和类型安全。 + +[//]: # 'Example1' + +```ts +import { queryOptions } from '@tanstack/react-query' + +function groupOptions(id: number) { + return queryOptions({ + queryKey: ['groups', id], + queryFn: () => fetchGroups(id), + staleTime: 5 * 1000, + }) +} + +// 使用示例: + +useQuery(groupOptions(1)) +useSuspenseQuery(groupOptions(5)) +useQueries({ + queries: [groupOptions(1), groupOptions(2)], +}) +queryClient.prefetchQuery(groupOptions(23)) +queryClient.setQueryData(groupOptions(42).queryKey, newGroups) +``` + +[//]: # 'Example1' + +对于无限查询 (Infinite Queries),可以使用单独的 [`infiniteQueryOptions`](../reference/infiniteQueryOptions.md) 辅助函数。 + +你仍然可以在组件级别覆盖某些选项。一个非常常见且实用的模式是为每个组件创建 [`select`](./render-optimizations.md#select) 函数: + +[//]: # 'Example2' + +```ts +// 类型推断仍然有效,因此 query.data 将是 select 的返回类型而非 queryFn 的返回类型 + +const query = useQuery({ + ...groupOptions(1), + select: (data) => data.groupName, +}) +``` + +[//]: # 'Example2' diff --git a/docs/zh-hans/framework/react/guides/query-retries.md b/docs/zh-hans/framework/react/guides/query-retries.md new file mode 100644 index 00000000000..20567c030c9 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/query-retries.md @@ -0,0 +1,81 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:04:04.683Z' +id: query-retries +title: 查询重试 +--- +当 `useQuery` 查询失败(查询函数抛出错误)时,如果该查询的请求未达到最大连续重试次数(默认为 `3`)或提供了判断是否允许重试的函数,TanStack Query 会自动重试该查询。 + +您可以在全局级别和单个查询级别配置重试行为: + +- 设置 `retry = false` 将禁用重试 +- 设置 `retry = 6` 会在显示函数抛出的最终错误前重试失败请求 6 次 +- 设置 `retry = true` 会无限重试失败请求 +- 设置 `retry = (failureCount, error) => ...` 允许根据失败原因自定义重试逻辑 + +[//]: # 'Info' + +> 在服务端,重试默认值为 `0` 以确保服务端渲染尽可能快速 + +[//]: # 'Info' +[//]: # 'Example' + +```tsx +import { useQuery } from '@tanstack/react-query' + +// 让特定查询重试指定次数 +const result = useQuery({ + queryKey: ['todos', 1], + queryFn: fetchTodoListPage, + retry: 10, // 将在显示错误前重试失败请求 10 次 +}) +``` + +[//]: # 'Example' + +> 注意:在最后一次重试尝试前,`error` 属性的内容将作为 `failureReason` 响应属性存在于 `useQuery` 中。因此在上例中,前 9 次重试尝试(共 10 次)的任何错误内容都将属于 `failureReason` 属性,只有当所有重试尝试后错误仍然存在时,最终才会出现在 `error` 属性中。 + +## 重试延迟 + +默认情况下,TanStack Query 不会在请求失败后立即重试。按照标准做法,每次重试尝试都会逐渐增加退避延迟。 + +默认的 `retryDelay` 设置为每次尝试翻倍(从 `1000` 毫秒开始),但不超过 30 秒: + +[//]: # 'Example2' + +```tsx +// 为所有查询配置 +import { + QueryCache, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, + }, +}) + +function App() { + return ... +} +``` + +[//]: # 'Example2' + +虽然不推荐,但您显然可以在 Provider 和单个查询选项中覆盖 `retryDelay` 函数/整数值。如果设置为整数而非函数,延迟时间将始终保持不变: + +[//]: # 'Example3' + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + retryDelay: 1000, // 每次重试都固定等待 1000 毫秒,无论重试次数多少 +}) +``` + +[//]: # 'Example3' diff --git a/docs/zh-hans/framework/react/guides/render-optimizations.md b/docs/zh-hans/framework/react/guides/render-optimizations.md new file mode 100644 index 00000000000..5f4bd27989f --- /dev/null +++ b/docs/zh-hans/framework/react/guides/render-optimizations.md @@ -0,0 +1,75 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:04:41.928Z' +id: render-optimizations +title: 渲染优化 +--- +React Query 自动应用多项优化策略,确保组件仅在真正需要时重新渲染。这主要通过以下方式实现: + +## 结构共享 (structural sharing) + +React Query 采用称为"结构共享"的技术,尽可能在重新渲染间保持引用不变。当通过网络获取数据时,通常通过 JSON 解析响应会得到全新引用。但若数据内容未变化,React Query 会保留原始引用;若仅部分数据变更,则保留未变更部分,仅替换变更部分。 + +> 注意:此优化仅在 `queryFn` 返回 JSON 兼容数据时生效。可通过全局或单查询设置 `structuralSharing: false` 关闭该功能,也可通过传入自定义函数实现个性化结构共享。 + +### 引用一致性 (referential identity) + +从 `useQuery`、`useInfiniteQuery`、`useMutation` 返回的顶层对象及 `useQueries` 返回的数组**不具备引用稳定性**——每次渲染都会生成新引用。但这些钩子返回的 `data` 属性会尽可能保持稳定。 + +## 属性追踪 (tracked properties) + +React Query 仅当组件实际"使用"了 `useQuery` 返回的某个属性时才会触发重新渲染,这是通过[自定义 getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#custom_setters_and_getters)实现的。该机制避免了许多不必要的重渲染(例如 `isFetching` 或 `isStale` 等频繁变更但未被使用的属性)。 + +可通过全局或单查询设置 `notifyOnChangeProps` 自定义此功能。若要完全关闭,可设为 `notifyOnChangeProps: 'all'`。 + +> 注意:自定义 getter 需通过解构或直接访问属性触发。若使用对象剩余解构会禁用此优化,我们提供了 [lint 规则](../../../eslint/no-rest-destructuring.md)防止此问题。 + +## 选择器 (select) + +通过 `select` 选项可指定组件应订阅的数据子集,适用于高度优化的数据转换或避免不必要重渲染场景: + +```js +export const useTodos = (select) => { + return useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + select, + }) +} + +export const useTodoCount = () => { + return useTodos((data) => data.length) +} +``` + +使用 `useTodoCount` 自定义钩子的组件仅当待办事项数量变化时重渲染,而单个待办事项名称变更等操作不会触发重渲染。 + +> 注意:`select` 操作基于成功缓存的数据,不适合用于抛出错误。错误应源自 `queryFn`,若 `select` 函数返回错误会导致 `data` 为 `undefined` 而 `isSuccess` 为 `true`。建议在 `queryFn` 中处理数据错误,或在查询钩子外部处理与缓存无关的异常情况。 + +### 记忆化 (memoization) + +`select` 函数仅在以下情况重新执行: +- 函数引用发生变化 +- `data` 发生变化 + +因此如上例所示的行内 `select` 函数会在每次渲染时执行。为避免这种情况,可用 `useCallback` 包裹,或在无依赖时提取为稳定函数引用: + +```js +// 使用 useCallback 包裹 +export const useTodoCount = () => { + return useTodos(useCallback((data) => data.length, [])) +} +``` + +```js +// 提取为稳定函数引用 +const selectTodoCount = (data) => data.length + +export const useTodoCount = () => { + return useTodos(selectTodoCount) +} +``` + +## 延伸阅读 + +关于这些主题的深度指南,请参阅社区资源中的 [React Query 渲染优化](../community/tkdodos-blog.md#3-react-query-render-optimizations)。 diff --git a/docs/zh-hans/framework/react/guides/request-waterfalls.md b/docs/zh-hans/framework/react/guides/request-waterfalls.md new file mode 100644 index 00000000000..c4abc043815 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/request-waterfalls.md @@ -0,0 +1,339 @@ +--- +source-updated-at: '2025-04-02T06:46:03.000Z' +translation-updated-at: '2025-05-06T04:11:08.176Z' +id: request-waterfalls +title: 性能与请求瀑布流 +--- +应用性能是一个广泛而复杂的领域,虽然 React Query 无法让你的 API 变得更快,但在使用 React Query 时仍需注意一些事项以确保最佳性能。 + +使用 React Query 或任何允许在组件内部获取数据的库时,最大的性能隐患是**请求瀑布流**。本页剩余部分将解释什么是请求瀑布流、如何发现它们,以及如何重构应用或 API 来避免它们。 + +[预取与路由集成指南](./prefetching.md)在此基础上进一步讲解,教你如何在无法或不适合重构应用或 API 时提前预取数据。 + +[服务端渲染与注水指南](./ssr.md)教你如何在服务端预取数据并将其传递到客户端,从而避免重复获取。 + +[高级服务端渲染指南](./advanced-ssr.md)进一步讲解如何将这些模式应用到服务端组件 (Server Components) 和流式服务端渲染 (Streaming Server Rendering) 中。 + +## 什么是请求瀑布流? + +请求瀑布流指的是当某个资源(代码、CSS、图片、数据)的请求必须等待另一个资源的请求完成后才能开始。 + +以网页为例。在加载 CSS、JS 等内容之前,浏览器需要先加载标记 (markup)。这就是一个请求瀑布流: + +``` +1. |-> Markup +2. |-> CSS +2. |-> JS +2. |-> Image +``` + +如果在 JS 文件中获取 CSS,就会形成双重瀑布流: + +``` +1. |-> Markup +2. |-> JS +3. |-> CSS +``` + +如果该 CSS 使用了背景图片,则形成三重瀑布流: + +``` +1. |-> Markup +2. |-> JS +3. |-> CSS +4. |-> Image +``` + +发现和分析请求瀑布流的最佳方式通常是打开浏览器开发者工具的“网络 (Network)”选项卡。 + +每个瀑布流至少代表一次与服务器的往返通信(除非资源被本地缓存)。因此,请求瀑布流的负面影响高度依赖用户的网络延迟。以前面的三重瀑布流为例,它实际上代表 4 次服务器往返。在 250ms 延迟(3G 网络或恶劣网络条件下很常见)的情况下,仅计算延迟时间就达到 4*250=1000ms。如果能将其优化为第一个示例中的仅 2 次往返,延迟时间将降至 500ms,背景图片的加载时间可能缩短一半! + +## 请求瀑布流与 React Query + +现在来看 React Query 的情况。我们首先关注不使用服务端渲染的场景。在发起查询之前需要先加载 JS,因此在屏幕上显示数据之前会形成双重瀑布流: + +``` +1. |-> Markup +2. |-> JS +3. |-> Query +``` + +以此为基准,下面分析几种可能导致 React Query 请求瀑布流的模式及规避方法: + +- 单组件瀑布流 / 串行查询 +- 嵌套组件瀑布流 +- 代码分割 + +### 单组件瀑布流 / 串行查询 + +当一个组件先获取一个查询,再获取另一个查询时,就会形成请求瀑布流。这种情况常出现在第二个查询是[依赖查询 (Dependent Query)](./dependent-queries.md)时——即它依赖第一个查询的数据来发起请求: + +```tsx +// 获取用户 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, +}) + +const userId = user?.id + +// 然后获取用户的项目 +const { + status, + fetchStatus, + data: projects, +} = useQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + // 该查询在 userId 存在前不会执行 + enabled: !!userId, +}) +``` + +虽然并非总能实现,但为了最佳性能,最好重构 API 以便通过单个查询获取这两项数据。在上例中,与其先获取 `getUserByEmail` 才能调用 `getProjectsByUser`,不如新增一个 `getProjectsByUserEmail` 查询来消除瀑布流。 + +> 另一种在不重构 API 的情况下缓解依赖查询的方法是:将瀑布流转移到延迟更低的服务端。这是[高级服务端渲染指南](./advanced-ssr.md)中介绍的服务端组件 (Server Components) 的设计理念。 + +使用 React Query 的 Suspense 时也会出现串行查询: + +```tsx +function App () { + // 以下查询会串行执行,导致多次服务器往返: + const usersQuery = useSuspenseQuery({ queryKey: ['users'], queryFn: fetchUsers }) + const teamsQuery = useSuspenseQuery({ queryKey: ['teams'], queryFn: fetchTeams }) + const projectsQuery = useSuspenseQuery({ queryKey: ['projects'], queryFn: fetchProjects }) + + // 注意:由于上述查询会暂停渲染,所有查询完成前不会渲染任何数据 + ... +} +``` + +注意:使用常规 `useQuery` 时这些查询会并行执行。 + +幸运的是,这个问题很容易修复——当组件中有多个 Suspense 查询时,始终使用 `useSuspenseQueries` 钩子: + +```tsx +const [usersQuery, teamsQuery, projectsQuery] = useSuspenseQueries({ + queries: [ + { queryKey: ['users'], queryFn: fetchUsers }, + { queryKey: ['teams'], queryFn: fetchTeams }, + { queryKey: ['projects'], queryFn: fetchProjects }, + ], +}) +``` + +### 嵌套组件瀑布流 + +当父组件和子组件都包含查询,且父组件在查询完成前不会渲染子组件时,就会出现嵌套组件瀑布流。这种情况可能发生在 `useQuery` 和 `useSuspenseQuery` 中。 + +如果子组件的渲染依赖于父组件的数据,或者子组件需要父组件传递部分结果作为 prop 才能发起查询,就形成了**依赖型**嵌套组件瀑布流。 + +首先看一个子组件**不依赖**父组件的例子: + +```tsx +function Article({ id }) { + const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + if (isPending) { + return 'Loading article...' + } + + return ( + <> + + + + + ) + +} + +function Comments({ id }) { + const { data, isPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + ... +} +``` + +注意:虽然 `` 接收父组件传递的 `id` prop,但这个 id 在 `
    ` 渲染时已经可用,因此没有理由不能同时获取评论和文章数据。在实际应用中,子组件可能嵌套在父组件多层级之下,这类瀑布流更难发现和修复。但在本例中,一种消除瀑布流的方法是将评论查询提升到父组件: + +```tsx +function Article({ id }) { + const { data: articleData, isPending: articlePending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + const { data: commentsData, isPending: commentsPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + if (articlePending) { + return 'Loading article...' + } + + return ( + <> + + + {commentsPending ? ( + 'Loading comments...' + ) : ( + + )} + + ) +} +``` + +现在两个查询会并行执行。注意:如果使用 Suspense,应该改用 `useSuspenseQueries` 合并这两个查询。 + +另一种消除瀑布流的方法是在 `
    ` 组件中预取评论,或者在路由级别预取这两个查询(页面加载或导航时)。更多细节请参阅[预取与路由集成指南](./prefetching.md)。 + +接下来看一个**依赖型**嵌套组件瀑布流的例子: + +```tsx +function Feed() { + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: getFeed, + }) + + if (isPending) { + return 'Loading feed...' + } + + return ( + <> + {data.map((feedItem) => { + if (feedItem.type === 'GRAPH') { + return + } + + return + })} + + ) +} + +function GraphFeedItem({ feedItem }) { + const { data, isPending } = useQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + + ... +} +``` + +第二个查询 `getGraphDataById` 在两方面依赖父组件:首先,只有当 `feedItem` 是图表类型时才会执行;其次,它需要父组件传递的 `id`。 + +``` +1. |> getFeed() +2. |> getGraphDataById() +``` + +在这个例子中,我们无法简单地通过将查询提升到父组件或添加预取来消除瀑布流。就像本指南开头的依赖查询示例一样,一种选择是重构 API 让 `getFeed` 查询包含图表数据。另一种更高级的解决方案是利用服务端组件 (Server Components) 将瀑布流转移到延迟更低的服务端(详见[高级服务端渲染指南](./advanced-ssr.md)),但要注意这可能涉及重大的架构变更。 + +即使存在少量查询瀑布流,应用仍能保持良好的性能。关键是要意识到这是常见的性能问题并保持警惕。当涉及代码分割时,情况会变得更加棘手,下面就来分析这种情况。 + +### 代码分割 + +将应用的 JS 代码拆分为更小的块并仅加载必要部分,通常是实现良好性能的关键步骤。但这也有缺点——常常会引入请求瀑布流。当被分割的代码中还包含查询时,问题会进一步加剧。 + +来看一个稍作修改的 Feed 示例: + +```tsx +// 延迟加载 GraphFeedItem 组件,意味着在渲染前不会开始加载 +const GraphFeedItem = React.lazy(() => import('./GraphFeedItem')) + +function Feed() { + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: getFeed, + }) + + if (isPending) { + return 'Loading feed...' + } + + return ( + <> + {data.map((feedItem) => { + if (feedItem.type === 'GRAPH') { + return + } + + return + })} + + ) +} + +// GraphFeedItem.tsx +function GraphFeedItem({ feedItem }) { + const { data, isPending } = useQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + + ... +} +``` + +这个例子形成了双重瀑布流: + +``` +1. |> getFeed() +2. |> JS for +3. |> getGraphDataById() +``` + +但仅从代码角度看是这样。如果考虑该页面的首次加载过程,实际上需要完成 5 次服务器往返才能渲染图表: + +``` +1. |> Markup +2. |> JS for +3. |> getFeed() +4. |> JS for +5. |> getGraphDataById() +``` + +注意:服务端渲染时情况会有所不同,我们将在[服务端渲染与注水指南](./ssr.md)中详细探讨。另外要注意的是,包含 `` 的路由通常也会被代码分割,这可能又增加一次跳转。 + +对于代码分割的情况,将 `getGraphDataById` 查询提升到 `` 组件并设为条件查询,或添加条件预取可能有所帮助。这样该查询可以与代码并行获取,将示例部分转变为: + +``` +1. |> getFeed() +2. |> getGraphDataById() +2. |> JS for +``` + +但这是一种权衡——现在 `getGraphDataById` 的数据获取代码被打包进了 `` 的主包中。请根据具体情况评估最佳方案。更多实现方法请参阅[预取与路由集成指南](./prefetching.md)。 + +> 以下两种方案的权衡: +> +> - 将所有数据获取代码包含在主包中,即使很少使用 +> - 将数据获取代码放在分割包中,但会产生请求瀑布流 +> +> 并不理想,这也是服务端组件 (Server Components) 的设计动机之一。通过服务端组件可以同时避免这两个问题,更多与 React Query 的结合应用请参阅[高级服务端渲染指南](./advanced-ssr.md)。 + +## 总结与要点 + +请求瀑布流是一个非常常见且复杂的性能问题,涉及多种权衡。应用中可能意外引入瀑布流的方式包括: + +- 在子组件中添加查询,未意识到父组件已有查询 +- 在父组件中添加查询,未意识到子组件已有查询 +- 将带有查询的组件及其后代移动到已有查询的新父组件中 +- 等等... + +由于这种意外复杂性,保持对瀑布流的警惕并定期检查应用(一个好方法是时不时检查网络选项卡!)是非常值得的。不一定要消除所有瀑布流才能获得良好性能,但要特别关注那些影响重大的情况。 + +在下一指南中,我们将通过[预取与路由集成](./prefetching.md)探索更多消除瀑布流的方法。 diff --git a/docs/zh-hans/framework/react/guides/scroll-restoration.md b/docs/zh-hans/framework/react/guides/scroll-restoration.md new file mode 100644 index 00000000000..236f8a897a2 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/scroll-restoration.md @@ -0,0 +1,11 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:09:15.717Z' +id: scroll-restoration +title: 滚动恢复 +--- +## 滚动恢复 (Scroll Restoration) + +传统上,当你在网页浏览器中导航回之前访问过的页面时,会发现页面会自动滚动到你上次离开时的位置。这一功能被称为 **滚动恢复 (scroll restoration)**。但随着现代 Web 应用逐渐转向客户端数据获取 (client side data fetching),这一特性曾出现了一定程度的退化。不过在使用 TanStack Query 时,情况就完全不同了。 + +开箱即用,所有查询(包括分页查询和无限加载查询)的"滚动恢复 (scroll restoration)"功能在 TanStack Query 中都能完美运作™️。这是因为查询结果会被缓存,并且在组件渲染时可以同步获取。只要你的查询缓存时间足够长(默认缓存时间为 5 分钟)且未被垃圾回收机制清除,滚动恢复功能就能始终正常工作。 diff --git a/docs/zh-hans/framework/react/guides/ssr.md b/docs/zh-hans/framework/react/guides/ssr.md new file mode 100644 index 00000000000..918f18f9827 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/ssr.md @@ -0,0 +1,484 @@ +--- +source-updated-at: '2025-04-02T06:46:03.000Z' +translation-updated-at: '2025-05-06T04:09:15.592Z' +id: ssr +title: 服务端渲染与注水 +--- +# 服务端渲染与注水 (Server Rendering & Hydration) + +在本指南中,您将学习如何结合服务端渲染使用 React Query。 + +关于背景知识,请参阅[预取与路由集成](./prefetching.md)指南。在此之前,您可能还需要查看[性能与请求瀑布流指南](./request-waterfalls.md)。 + +如需了解高级服务端渲染模式(如流式传输、服务器组件和新的 Next.js app router),请参阅[高级服务端渲染指南](./advanced-ssr.md)。 + +如果您只想查看代码示例,可以直接跳转到下方的[完整 Next.js pages router 示例](#full-nextjs-pages-router-example)或[完整 Remix 示例](#full-remix-example)。 + +## 服务端渲染与 React Query + +那么什么是服务端渲染?本指南的其余部分将假设您已熟悉这个概念,但让我们花些时间看看它与 React Query 的关系。服务端渲染是在服务器上生成初始 HTML 的行为,这样用户在页面加载时就能立即看到一些内容。这可以在页面被请求时按需发生(SSR),也可以因为之前的请求被缓存或在构建时(SSG)提前发生。 + +如果您阅读过请求瀑布流指南,可能会记得这个流程: + +``` +1. |-> 标记(无内容) +2. |-> JavaScript +3. |-> 查询 +``` + +在客户端渲染的应用程序中,这是用户在屏幕上看到任何内容之前至少需要进行的 3 次服务器往返。服务端渲染的一种理解方式是将其转变为: + +``` +1. |-> 标记(包含内容 AND 初始数据) +2. |-> JavaScript +``` + +一旦 **1.** 完成,用户就可以看到内容,当 **2.** 完成时,页面变得可交互和可点击。因为标记中还包含我们需要的初始数据,所以步骤 **3.** 根本不需要在客户端运行,至少在您因某些原因想要重新验证数据之前不需要。 + +这都是从客户端的角度来看的。在服务器端,我们需要在生成/渲染标记之前**预取**数据,需要将该数据**脱水**为可序列化的格式以便嵌入到标记中,而在客户端,我们需要将该数据**注水**到 React Query 缓存中,以避免在客户端进行新的获取。 + +继续阅读以了解如何用 React Query 实现这三个步骤。 + +## 关于 Suspense 的快速说明 + +本指南使用常规的 `useQuery` API。虽然我们不一定会推荐,但可以用 `useSuspenseQuery` 替代它,**只要您总是预取所有查询**。这样做的好处是您可以在客户端使用 `` 来处理加载状态。 + +如果您在使用 `useSuspenseQuery` 时忘记预取查询,后果将取决于您使用的框架。在某些情况下,数据会在服务器端 Suspense 并获取,但永远不会被注水到客户端,客户端会再次获取。在这些情况下,您会遇到标记注水不匹配的问题,因为服务器和客户端尝试渲染不同的内容。 + +## 初始设置 + +使用 React Query 的第一步始终是创建一个 `queryClient` 并将应用程序包裹在 `` 中。在进行服务端渲染时,重要的是在您的应用程序内部、React 状态中创建 `queryClient` 实例(实例引用也可以)。**这确保不同用户和请求之间的数据不会共享**,同时仍然只在组件生命周期中创建一次 `queryClient`。 + +Next.js pages router 示例: + +```tsx +// _app.tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +// 永远不要这样做: +// const queryClient = new QueryClient() +// +// 在文件根级别创建 queryClient 会使缓存 +// 在所有请求之间共享,意味着 _所有_ 数据都会传递给 _所有_ 用户。 +// 除了对性能不利外,这还会泄露任何敏感数据。 + +export default function MyApp({ Component, pageProps }) { + // 应该这样做,确保每个请求都有自己的缓存: + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 时,我们通常希望设置默认的 staleTime + // 大于 0,以避免在客户端立即重新获取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +Remix 示例: + +```tsx +// app/root.tsx +import { Outlet } from '@remix-run/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +export default function MyApp() { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 时,我们通常希望设置默认的 staleTime + // 大于 0,以避免在客户端立即重新获取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +## 使用 `initialData` 快速开始 + +最快的方法是根本不涉及 React Query 的预取功能,也不使用 `dehydrate`/`hydrate` API。相反,您可以将原始数据作为 `initialData` 选项传递给 `useQuery`。让我们看一个使用 Next.js pages router 和 `getServerSideProps` 的示例。 + +```tsx +export async function getServerSideProps() { + const posts = await getPosts() + return { props: { posts } } +} + +function Posts(props) { + const { data } = useQuery({ + queryKey: ['posts'], + queryFn: getPosts, + initialData: props.posts, + }) + + // ... +} +``` + +这也适用于 `getStaticProps` 甚至较旧的 `getInitialProps`,相同的模式可以应用于任何具有等效功能的其他框架。这是在 Remix 中的相同示例: + +```tsx +export async function loader() { + const posts = await getPosts() + return json({ posts }) +} + +function Posts() { + const { posts } = useLoaderData() + + const { data } = useQuery({ + queryKey: ['posts'], + queryFn: getPosts, + initialData: posts, + }) + + // ... +} +``` + +设置非常简单,这可以快速解决某些情况,但与完整方法相比,有**一些权衡需要考虑**: + +- 如果您在树中更深层的组件中调用 `useQuery`,则需要将 `initialData` 传递到该点 +- 如果您在多个位置使用相同的查询调用 `useQuery`,只将 `initialData` 传递给其中一个可能会很脆弱,并在应用程序更改时中断。如果您删除或移动了带有 `initialData` 的 `useQuery` 的组件,更深的 `useQuery` 可能不再有任何数据。将 `initialData` 传递给**所有**需要它的查询也可能很繁琐。 +- 无法知道查询在服务器上获取的时间,因此 `dataUpdatedAt` 和确定查询是否需要重新获取是基于页面加载时间而不是查询获取时间 +- 如果缓存中已有查询的数据,`initialData` 永远不会覆盖这些数据,**即使新数据比旧数据更新**。 + - 要理解为什么这特别糟糕,请考虑上面的 `getServerSideProps` 示例。如果您多次导航到页面并返回,`getServerSideProps` 每次都会被调用并获取新数据,但因为使用了 `initialData` 选项,客户端缓存和数据永远不会更新。 + +设置完整的注水解决方案很简单,并且没有这些缺点,这将是本文档其余部分的重点。 + +## 使用注水 API + +只需稍多的设置,您就可以在预加载阶段使用 `queryClient` 预取查询,将该 `queryClient` 的序列化版本传递给应用程序的渲染部分并在那里重用。这避免了上述缺点。随意跳转到完整的 Next.js pages router 和 Remix 示例,但在一般情况下,这些是额外的步骤: + +- 在框架的 loader 函数中,创建 `const queryClient = new QueryClient(options)` +- 在 loader 函数中,为每个要预取的查询执行 `await queryClient.prefetchQuery(...)` + - 您希望尽可能使用 `await Promise.all(...)` 并行获取查询 + - 可以有不预取的查询。这些不会在服务器端渲染,而是在应用程序变得可交互后在客户端获取。这对于仅在用户交互后显示的内容或位于页面较下方以避免阻塞更关键内容的内容非常有用。 +- 从 loader 返回 `dehydrate(queryClient)`,注意返回此的确切语法因框架而异 +- 用 `` 包裹您的树,其中 `dehydratedState` 来自框架的 loader。如何获取 `dehydratedState` 也因框架而异。 + - 这可以针对每个路由完成,也可以在应用程序顶部完成以避免样板代码,请参阅示例 + +> 一个有趣的细节是实际上涉及**三个** `queryClient`。框架的 loader 是一种"预加载"阶段,发生在渲染之前,这个阶段有自己的 `queryClient` 进行预取。这个阶段的脱水结果被传递给**服务器渲染过程**和**客户端渲染过程**,它们各自有自己的 `queryClient`。这确保它们从相同的数据开始,因此可以返回相同的标记。 + +> 服务器组件是另一种"预加载"阶段,也可以"预加载"(预渲染)React 组件树的部分。在[高级服务端渲染指南](./advanced-ssr.md)中了解更多。 + +### 完整 Next.js pages router 示例 + +> 有关 app router 的文档,请参阅[高级服务端渲染指南](./advanced-ssr.md)。 + +初始设置: + +```tsx +// _app.tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +export default function MyApp({ Component, pageProps }) { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 时,我们通常希望设置默认的 staleTime + // 大于 0,以避免在客户端立即重新获取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +在每个路由中: + +```tsx +// pages/posts.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, + useQuery, +} from '@tanstack/react-query' + +// 也可以是 getServerSideProps +export async function getStaticProps() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} + +function Posts() { + // 这个 useQuery 也可以发生在 的更深层子组件中, + // 数据将立即可用 + const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts }) + + // 这个查询没有在服务器上预取,将在客户端才开始 + // 获取,两种模式可以混合使用 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} + +export default function PostsRoute({ dehydratedState }) { + return ( + + + + ) +} +``` + +### 完整 Remix 示例 + +初始设置: + +```tsx +// app/root.tsx +import { Outlet } from '@remix-run/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +export default function MyApp() { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 时,我们通常希望设置默认的 staleTime + // 大于 0,以避免在客户端立即重新获取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +在每个路由中,注意也可以在嵌套路由中这样做: + +```tsx +// app/routes/posts.tsx +import { json } from '@remix-run/node' +import { + dehydrate, + HydrationBoundary, + QueryClient, + useQuery, +} from '@tanstack/react-query' + +export async function loader() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return json({ dehydratedState: dehydrate(queryClient) }) +} + +function Posts() { + // 这个 useQuery 也可以发生在 的更深层子组件中, + // 数据将立即可用 + const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts }) + + // 这个查询没有在服务器上预取,将在客户端才开始 + // 获取,两种模式可以混合使用 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} + +export default function PostsRoute() { + const { dehydratedState } = useLoaderData() + return ( + + + + ) +} +``` + +## 可选 - 移除样板代码 + +在每个路由中都有这部分可能看起来有很多样板代码: + +```tsx +export default function PostsRoute({ dehydratedState }) { + return ( + + + + ) +} +``` + +虽然这种方法没有问题,但如果您想摆脱这种样板代码,可以这样修改 Next.js 的设置: + +```tsx +// _app.tsx +import { + HydrationBoundary, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' + +export default function MyApp({ Component, pageProps }) { + const [queryClient] = React.useState(() => new QueryClient()) + + return ( + + + + + + ) +} + +// pages/posts.tsx +// 移除带有 HydrationBoundary 的 PostsRoute,直接导出 Posts: +export default function Posts() { ... } +``` + +对于 Remix,这稍微复杂一些,我们建议查看 [use-dehydrated-state](https://github.com/maplegrove-io/use-dehydrated-state) 包。 + +## 预取依赖查询 + +在预取指南中,我们学习了如何[预取依赖查询](./prefetching.md#dependent-queries--code-splitting),但我们如何在框架的 loader 中做到这一点?考虑以下代码,取自[依赖查询指南](./dependent-queries.md): + +```tsx +// 获取用户 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, +}) + +const userId = user?.id + +// 然后获取用户的项目 +const { + status, + fetchStatus, + data: projects, +} = useQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + // 查询直到 userId 存在才会执行 + enabled: !!userId, +}) +``` + +我们如何预取这个以便它可以进行服务端渲染?这里有一个示例: + +```tsx +// 对于 Remix,将此重命名为 loader +export async function getServerSideProps() { + const queryClient = new QueryClient() + + const user = await queryClient.fetchQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, + }) + + if (user?.userId) { + await queryClient.prefetchQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + }) + } + + // 对于 Remix: + // return json({ dehydratedState: dehydrate(queryClient) }) + return { props: { dehydratedState: dehydrate(queryClient) } } +} +``` + +当然,这可能会变得更复杂,但由于这些 loader 函数只是 JavaScript,您可以使用该语言的全部功能来构建您的逻辑。确保预取所有您希望进行服务端渲染的查询。 + +## 错误处理 + +React Query 默认为优雅降级策略。这意味着: + +- `queryClient.prefetchQuery(...)` 从不抛出错误 +- `dehydrate(...)` 只包含成功的查询,不包括失败的查询 + +这将导致任何失败的查询在客户端重试,并且服务端渲染的输出将包括加载状态而不是完整内容。 + +虽然这是一个很好的默认设置,但有时这并不是您想要的。当关键内容缺失时,您可能希望根据情况返回 404 或 500 状态码。对于这些情况,请改用 `queryClient.fetchQuery(...)`,它会在失败时抛出错误,让您以适当的方式处理事情。 + +```tsx +let result + +try { + result = await queryClient.fetchQuery(...) +} catch (error) { + // 处理错误,参考您的框架文档 +} + +// 您可能还想检查并处理任何无效的 `result` +``` + +如果出于某种原因,您希望在脱水状态中包含失败的查询以避免重试,可以使用 `shouldDehydrateQuery` 选项覆盖默认函数并实现自己的逻辑: + +```tsx +dehydrate(queryClient, { + shouldDehydrateQuery: (query) => { + // 这将包括所有查询,包括失败的查询, + // 但您也可以通过检查 `query` 实现自己的逻辑 + return true + }, +}) +``` + +## 序列化 + +当在 Next.js 中执行 `return { props: { dehydratedState: dehydrate(queryClient) } }` 或在 Remix 中执行 `return json({ dehydratedState: dehydrate(queryClient) })` 时,发生的是 `queryClient` 的 `dehydratedState` 表示由框架序列化,以便可以嵌入到标记中并传输到客户端。 + +默认情况下,这些框架仅支持返回可安全序列化/解析的内容,因此不支持 `undefined`、`Error`、`Date`、`Map`、`Set`、`BigInt`、`Infinity`、`NaN`、`-0`、正则表达式等。这也意味着您不能从查询中返回任何这些内容。如果返回这些值是您想要的,请查看 [superjson](https://github.com/blitz-js/superjson) 或类似的包。 + +如果您使用自定义 SSR 设置,您需要自己处理这一步。您的第一反应可能是使用 `JSON.stringify(dehydratedState)`,但由于默认情况下这不会转义像 `` 这样的内容,这很容易导致 diff --git a/docs/zh-hans/framework/react/guides/suspense.md b/docs/zh-hans/framework/react/guides/suspense.md new file mode 100644 index 00000000000..17f3748d27f --- /dev/null +++ b/docs/zh-hans/framework/react/guides/suspense.md @@ -0,0 +1,222 @@ +--- +source-updated-at: '2025-04-02T06:46:03.000Z' +translation-updated-at: '2025-05-06T04:06:04.912Z' +id: suspense +title: Suspense +--- +React Query 也可以与 React 的 Suspense for Data Fetching APIs 配合使用。为此,我们提供了专用的钩子: + +- [useSuspenseQuery](../reference/useSuspenseQuery.md) +- [useSuspenseInfiniteQuery](../reference/useSuspenseInfiniteQuery.md) +- [useSuspenseQueries](../reference/useSuspenseQueries.md) +- 此外,你还可以使用 `useQuery().promise` 和 `React.use()`(实验性功能) + +当启用 suspense 模式时,不再需要 `status` 状态和 `error` 对象,它们会被 `React.Suspense` 组件(包括使用 `fallback` 属性和 React 错误边界来捕获错误)替代。请阅读 [重置错误边界](#resetting-error-boundaries) 并查看 [Suspense 示例](../examples/react/suspense) 了解如何设置 suspense 模式。 + +如果你希望 mutations 将错误传播到最近的错误边界(类似于 queries),可以将 `throwOnError` 选项设置为 `true`。 + +为 query 启用 suspense 模式: + +```tsx +import { useSuspenseQuery } from '@tanstack/react-query' + +const { data } = useSuspenseQuery({ queryKey, queryFn }) +``` + +这在 TypeScript 中运行良好,因为 `data` 保证已定义(错误和加载状态由 Suspense 和 ErrorBoundaries 处理)。 + +另一方面,因此你不能有条件地启用/禁用 Query。对于依赖的 Queries 来说,这通常不是必需的,因为使用 suspense 时,组件内的所有 Queries 会按顺序获取。 + +这种 Query 也不存在 `placeholderData`。为了防止 UI 在更新期间被 fallback 替换,请将更改 QueryKey 的更新包装在 [startTransition](https://react.dev/reference/react/Suspense#preventing-unwanted-fallbacks) 中。 + +### throwOnError 默认值 + +默认情况下,并非所有错误都会抛出到最近的错误边界——我们只会在没有其他数据可显示时抛出错误。这意味着如果 Query 曾经在缓存中成功获取过数据,即使数据是 `stale` 的,组件也会渲染。因此,`throwOnError` 的默认值为: + +``` +throwOnError: (error, query) => typeof query.state.data === 'undefined' +``` + +由于你不能更改 `throwOnError`(因为这可能导致 `data` 变为 `undefined`),如果你希望所有错误都由错误边界处理,必须手动抛出错误: + +```tsx +import { useSuspenseQuery } from '@tanstack/react-query' + +const { data, error, isFetching } = useSuspenseQuery({ queryKey, queryFn }) + +if (error && !isFetching) { + throw error +} + +// 继续渲染数据 +``` + +## 重置错误边界 + +无论你在 queries 中使用的是 **suspense** 还是 **throwOnError**,都需要一种方式让 queries 知道在发生错误后重新渲染时你想重试。 + +Query 错误可以通过 `QueryErrorResetBoundary` 组件或 `useQueryErrorResetBoundary` 钩子重置。 + +使用组件时,它会重置组件边界内的所有 query 错误: + +```tsx +import { QueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => ( + + {({ reset }) => ( + ( +
    + 发生错误! + +
    + )} + > + +
    + )} +
    +) +``` + +使用钩子时,它会重置最近的 `QueryErrorResetBoundary` 内的所有 query 错误。如果没有定义边界,则会全局重置: + +```tsx +import { useQueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => { + const { reset } = useQueryErrorResetBoundary() + return ( + ( +
    + 发生错误! + +
    + )} + > + +
    + ) +} +``` + +## 渲染时获取 vs 边渲染边获取 + +默认情况下,React Query 在 `suspense` 模式下作为 **渲染时获取 (Fetch-on-render)** 解决方案运行良好,无需额外配置。这意味着当你的组件尝试挂载时,它们会触发 query 获取并暂停,但只有在你导入并挂载它们之后才会发生。如果你想更进一步,实现 **边渲染边获取 (Render-as-you-fetch)** 模型,我们建议在路由回调和/或用户交互事件上实现 [预取 (Prefetching)](./prefetching.md),以便在挂载之前开始加载 queries,甚至在你开始导入或挂载它们的父组件之前。 + +## 服务端 Suspense 与流式渲染 + +如果你使用 `NextJs`,可以使用我们的 **实验性** 集成来实现服务端 Suspense:`@tanstack/react-query-next-experimental`。这个包允许你在服务端(在客户端组件中)获取数据,只需在组件中调用 `useSuspenseQuery`。结果会随着 SuspenseBoundaries 的解析从服务端流式传输到客户端。 + +要实现这一点,请将你的应用包装在 `ReactQueryStreamedHydration` 组件中: + +```tsx +// app/providers.tsx +'use client' + +import { + isServer, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' +import * as React from 'react' +import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental' + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 时,通常希望将默认的 staleTime 设置为 + // 大于 0 的值,以避免在客户端立即重新获取 + staleTime: 60 * 1000, + }, + }, + }) +} + +let browserQueryClient: QueryClient | undefined = undefined + +function getQueryClient() { + if (isServer) { + // 服务端:始终创建一个新的 query client + return makeQueryClient() + } else { + // 浏览器:如果没有 query client,则创建一个新的 + // 这非常重要,这样在初始渲染期间 React 暂停时不会重新创建 client + // 如果我们在创建 query client 下方有 suspense 边界,则可能不需要这样做 + if (!browserQueryClient) browserQueryClient = makeQueryClient() + return browserQueryClient + } +} + +export function Providers(props: { children: React.ReactNode }) { + // 注意:如果在初始化 query client 时没有 suspense 边界, + // 避免使用 useState,因为 React 会在初始渲染暂停时丢弃 client + const queryClient = getQueryClient() + + return ( + + + {props.children} + + + ) +} +``` + +更多信息,请查看 [NextJs Suspense 流式渲染示例](../examples/react/nextjs-suspense-streaming) 和 [高级渲染与注水 (Advanced Rendering & Hydration)](./advanced-ssr.md) 指南。 + +## 使用 `useQuery().promise` 和 `React.use()`(实验性功能) + +> 要启用此功能,你需要在创建 `QueryClient` 时将 `experimental_prefetchInRender` 选项设置为 `true` + +**示例代码:** + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + experimental_prefetchInRender: true, + }, + }, +}) +``` + +**用法:** + +```tsx +import React from 'react' +import { useQuery } from '@tanstack/react-query' +import { fetchTodos, type Todo } from './api' + +function TodoList({ query }: { query: UseQueryResult }) { + const data = React.use(query.promise) + + return ( +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + ) +} + +export function App() { + const query = useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) + + return ( + <> +

    Todos

    + Loading...}> + + + + ) +} +``` diff --git a/docs/zh-hans/framework/react/guides/testing.md b/docs/zh-hans/framework/react/guides/testing.md new file mode 100644 index 00000000000..1850d2de5f2 --- /dev/null +++ b/docs/zh-hans/framework/react/guides/testing.md @@ -0,0 +1,168 @@ +--- +source-updated-at: '2025-03-24T10:00:44.000Z' +translation-updated-at: '2025-05-06T04:03:34.392Z' +id: testing +title: 测试 +--- +React Query 通过钩子(hooks)机制工作——无论是我们提供的钩子还是围绕它们封装的自定义钩子。 + +在 React 17 或更早版本中,可以使用 [React Hooks Testing Library](https://react-hooks-testing-library.com/) 库为这些自定义钩子编写单元测试。 + +通过以下命令安装: + +```sh +npm install @testing-library/react-hooks react-test-renderer --save-dev +``` + +(`react-test-renderer` 库是 `@testing-library/react-hooks` 的 peer 依赖项,其版本需与当前使用的 React 版本对应。) + +*注意*:在 React 18 或更高版本中,`renderHook` 可直接通过 `@testing-library/react` 包使用,不再需要 `@testing-library/react-hooks`。 + +## 第一个测试 + +安装完成后,可以编写一个简单测试。假设有以下自定义钩子: + +```tsx +export function useCustomHook() { + return useQuery({ queryKey: ['customHook'], queryFn: () => 'Hello' }) +} +``` + +对应的测试代码如下: + +```tsx +import { renderHook, waitFor } from '@testing-library/react' + +const queryClient = new QueryClient() +const wrapper = ({ children }) => ( + {children} +) + +const { result } = renderHook(() => useCustomHook(), { wrapper }) + +await waitFor(() => expect(result.current.isSuccess).toBe(true)) + +expect(result.current.data).toEqual('Hello') +``` + +注意我们提供了一个自定义包装器(wrapper),用于构建 `QueryClient` 和 `QueryClientProvider`。这确保了测试与其他测试完全隔离。 + +虽然可以全局只编写一次包装器,但需确保每次测试前清空 `QueryClient`,且测试不能并行运行,否则会相互影响结果。 + +## 关闭重试机制 + +库默认会进行三次指数退避重试,因此测试错误查询时可能导致超时。最简单的方式是通过 `QueryClientProvider` 全局关闭重试。扩展上述示例: + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ 关闭重试 + retry: false, + }, + }, +}) +const wrapper = ({ children }) => ( + {children} +) +``` + +这会设置组件树中所有查询的默认行为为“不重试”。需注意,仅当实际 `useQuery` 未显式设置重试次数时此配置生效。若某查询明确要求重试5次,该设置仍会优先于全局默认值。 + +## 在 Jest 中设置 gcTime 为 Infinity + +如果使用 Jest,可将 `gcTime` 设为 `Infinity` 以避免出现“Jest 在测试完成后未及时退出”的错误。这是服务端的默认行为,仅当显式设置 `gcTime` 时才需要调整。 + +## 测试网络请求 + +React Query 的主要用途是缓存网络请求,因此验证代码是否发起正确的网络请求至关重要。 + +测试方法有多种,本例使用 [nock](https://www.npmjs.com/package/nock)。假设有以下自定义钩子: + +```tsx +function useFetchData() { + return useQuery({ + queryKey: ['fetchData'], + queryFn: () => request('/api/data'), + }) +} +``` + +测试代码如下: + +```tsx +const queryClient = new QueryClient() +const wrapper = ({ children }) => ( + {children} +) + +const expectation = nock('http://example.com').get('/api/data').reply(200, { + answer: 42, +}) + +const { result } = renderHook(() => useFetchData(), { wrapper }) + +await waitFor(() => expect(result.current.isSuccess).toBe(true)) + +expect(result.current.data).toEqual({ answer: 42 }) +``` + +这里使用 `waitFor` 等待查询状态变为成功,确保钩子已完成处理并返回正确数据。*注意*:在 React 18 中,`waitFor` 的语义有所变化。 + +## 测试加载更多/无限滚动 + +首先模拟 API 响应: + +```tsx +function generateMockedResponse(page) { + return { + page: page, + items: [...] + } +} +``` + +然后通过 `nock` 配置基于页码区分响应,利用 `uri` 实现动态匹配(如 `"/?page=1"` 或 `"/?page=2"`): + +```tsx +const expectation = nock('http://example.com') + .persist() + .query(true) + .get('/api/data') + .reply(200, (uri) => { + const url = new URL(`http://example.com${uri}`) + const { page } = Object.fromEntries(url.searchParams) + return generateMockedResponse(page) + }) +``` + +(注意 `.persist()`,因为会多次调用同一接口) + +测试时关键点是等待数据断言通过: + +```tsx +const { result } = renderHook(() => useInfiniteQueryCustomHook(), { + wrapper, +}) + +await waitFor(() => expect(result.current.isSuccess).toBe(true)) + +expect(result.current.data.pages).toStrictEqual(generateMockedResponse(1)) + +result.current.fetchNextPage() + +await waitFor(() => + expect(result.current.data.pages).toStrictEqual([ + ...generateMockedResponse(1), + ...generateMockedResponse(2), + ]), +) + +expectation.done() +``` + +*注意*:React 18 中 `waitFor` 的语义变化同上文所述。 + +## 延伸阅读 + +更多技巧及使用 `mock-service-worker` 的替代方案,可参考社区资源中的 [Testing React Query](../community/tkdodos-blog.md#5-testing-react-query)。 diff --git a/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md b/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md new file mode 100644 index 00000000000..99555615cec --- /dev/null +++ b/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md @@ -0,0 +1,83 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:02:08.804Z' +id: updates-from-mutation-responses +title: 从变更响应中更新 +--- +处理那些**更新**服务器上对象的变更(mutations)时,变更响应中通常会自动返回新对象。与其重新获取该条目的查询并浪费网络请求去获取已有的数据,不如利用变更函数返回的对象,通过 [Query Client 的 `setQueryData`](../../../reference/QueryClient.md#queryclientsetquerydata) 方法立即用新数据更新现有查询: + +[//]: # '示例' + +```tsx +const queryClient = useQueryClient() + +const mutation = useMutation({ + mutationFn: editTodo, + onSuccess: (data) => { + queryClient.setQueryData(['todo', { id: 5 }], data) + }, +}) + +mutation.mutate({ + id: 5, + name: 'Do the laundry', +}) + +// 下面的查询会通过成功变更的响应自动更新 +const { status, data, error } = useQuery({ + queryKey: ['todo', { id: 5 }], + queryFn: fetchTodoById, +}) +``` + +[//]: # '示例' + +若想将 `onSuccess` 逻辑封装成可复用的变更,可以创建如下自定义钩子: + +[//]: # '示例2' + +```tsx +const useMutateTodo = () => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: editTodo, + // 注意第二个参数是 `mutate` 函数接收的变量对象 + onSuccess: (data, variables) => { + queryClient.setQueryData(['todo', { id: variables.id }], data) + }, + }) +} +``` + +[//]: # '示例2' + +## 不可变性 + +通过 `setQueryData` 更新必须遵循**不可变**原则。**禁止**直接修改从缓存中获取的数据并原地写入缓存。虽然初期可能生效,但会导致难以察觉的潜在错误。 + +[//]: # '示例3' + +```tsx +queryClient.setQueryData(['posts', { id }], (oldData) => { + if (oldData) { + // ❌ 切勿如此操作 + oldData.title = 'my new post title' + } + return oldData +}) + +queryClient.setQueryData( + ['posts', { id }], + // ✅ 这才是正确方式 + (oldData) => + oldData + ? { + ...oldData, + title: 'my new post title', + } + : oldData, +) +``` + +[//]: # '示例3' diff --git a/docs/zh-hans/framework/react/guides/window-focus-refetching.md b/docs/zh-hans/framework/react/guides/window-focus-refetching.md new file mode 100644 index 00000000000..b427df98aaf --- /dev/null +++ b/docs/zh-hans/framework/react/guides/window-focus-refetching.md @@ -0,0 +1,106 @@ +--- +source-updated-at: '2024-05-10T12:03:22.000Z' +translation-updated-at: '2025-05-06T04:02:39.658Z' +id: window-focus-refetching +title: 窗口焦点重新获取 +--- +如果用户离开应用后返回,且查询数据已过时,**TanStack Query 会自动在后台为你请求最新数据**。你可以通过 `refetchOnWindowFocus` 选项全局或按查询禁用此行为: + +#### 全局禁用 + +[//]: # 'Example' + +```tsx +// +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, // 默认值: true + }, + }, +}) + +function App() { + return ... +} +``` + +[//]: # 'Example' + +#### 按查询禁用 + +[//]: # 'Example2' + +```tsx +useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + refetchOnWindowFocus: false, +}) +``` + +[//]: # 'Example2' + +## 自定义窗口聚焦事件 + +在极少数情况下,你可能希望自行管理触发 TanStack Query 重新验证的窗口聚焦事件。为此,TanStack Query 提供了 `focusManager.setEventListener` 函数,该函数会提供窗口聚焦时应触发的回调,并允许你设置自定义事件。调用 `focusManager.setEventListener` 时,先前设置的事件处理器会被移除(大多数情况下是默认处理器),并替换为你的新处理器。以下是默认处理器的实现示例: + +[//]: # 'Example3' + +```tsx +focusManager.setEventListener((handleFocus) => { + // 监听 visibilitychange 事件 + if (typeof window !== 'undefined' && window.addEventListener) { + const visibilitychangeHandler = () => { + handleFocus(document.visibilityState === 'visible') + } + window.addEventListener('visibilitychange', visibilitychangeHandler, false) + return () => { + // 确保在新处理器设置时取消订阅 + window.removeEventListener('visibilitychange', visibilitychangeHandler) + } + } +}) +``` + +[//]: # 'Example3' +[//]: # 'ReactNative' + +## 在 React Native 中管理聚焦状态 + +不同于在 `window` 上监听事件,React Native 通过 [`AppState` 模块](https://reactnative.dev/docs/appstate#app-states) 提供聚焦信息。你可以使用 `AppState` 的 "change" 事件在应用状态变为 "active" 时触发更新: + +```tsx +import { AppState } from 'react-native' +import { focusManager } from '@tanstack/react-query' + +function onAppStateChange(status: AppStateStatus) { + if (Platform.OS !== 'web') { + focusManager.setFocused(status === 'active') + } +} + +useEffect(() => { + const subscription = AppState.addEventListener('change', onAppStateChange) + + return () => subscription.remove() +}, []) +``` + +[//]: # 'ReactNative' + +## 管理聚焦状态 + +[//]: # 'Example4' + +```tsx +import { focusManager } from '@tanstack/react-query' + +// 覆盖默认聚焦状态 +focusManager.setFocused(true) + +// 回退到默认聚焦检查 +focusManager.setFocused(undefined) +``` + +[//]: # 'Example4' diff --git a/docs/zh-hans/framework/react/installation.md b/docs/zh-hans/framework/react/installation.md new file mode 100644 index 00000000000..2ae09b460fc --- /dev/null +++ b/docs/zh-hans/framework/react/installation.md @@ -0,0 +1,92 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:26:26.668Z' +id: installation +title: 安装 +--- +# 安装 + +您可以通过 [NPM](https://npmjs.com/) 安装 React Query,或者通过 [ESM.sh](https://esm.sh/) 使用传统的 ` +``` + +> 您可以在 [这里](https://react.dev/reference/react/createElement#creating-an-element-without-jsx) 找到不使用 JSX 来使用 React 的说明。 + +### 环境要求 + +React Query 针对现代浏览器进行了优化,兼容以下浏览器配置: + +``` +Chrome >= 91 +Firefox >= 90 +Edge >= 91 +Safari >= 15 +iOS >= 15 +Opera >= 77 +``` + +> 根据您的环境,可能需要添加 polyfill。如果需要支持旧版浏览器,您需要自行转译 `node_modules` 中的库文件。 + +### 推荐配置 + +建议同时使用我们的 [ESLint Plugin Query](../../eslint/eslint-plugin-query.md) 来帮助您在编码时发现错误和不一致。可通过以下命令安装: + +```bash +npm i -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +pnpm add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +yarn add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +bun add -D @tanstack/eslint-plugin-query +``` diff --git a/docs/zh-hans/framework/react/overview.md b/docs/zh-hans/framework/react/overview.md new file mode 100644 index 00000000000..76d11bcd74a --- /dev/null +++ b/docs/zh-hans/framework/react/overview.md @@ -0,0 +1,102 @@ +--- +source-updated-at: '2025-04-02T06:45:29.000Z' +translation-updated-at: '2025-05-06T04:32:50.321Z' +id: overview +title: 概述 +--- +TanStack Query(前身为 React Query)常被描述为 Web 应用程序中缺失的数据获取库,但更专业地说,它能让 **获取、缓存、同步和更新服务端状态 (server state)** 在你的 Web 应用中变得轻而易举。 + +## 动机 + +大多数核心 Web 框架 **并未** 提供一种全面的数据获取或更新方式。因此,开发者最终要么构建封装了严格数据获取理念的元框架 (meta-frameworks),要么发明自己的数据获取方法。这通常意味着拼凑基于组件的状态和副作用,或使用更通用的状态管理库来存储和提供整个应用中的异步数据。 + +虽然大多数传统状态管理库非常适合处理客户端状态 (client state),但它们 **并不擅长处理异步或服务端状态 (server state)**。这是因为 **服务端状态完全不同**。首先,服务端状态: + +- 持久化存储在远程位置,你可能无法控制或拥有该位置 +- 需要通过异步 API 进行获取和更新 +- 意味着共享所有权,可能被其他人更改而无需你知晓 +- 如果不加注意,可能会在你的应用中变得“过时 (out of date)” + +一旦你理解了应用中服务端状态的本质,**更多挑战会接踵而至**,例如: + +- 缓存...(可能是编程中最难的事情) +- 将针对同一数据的多个请求去重 (deduping) 为单个请求 +- 在后台更新“过时”数据 +- 判断数据何时“过时” +- 尽可能快地反映数据更新 +- 分页 (pagination) 和懒加载 (lazy loading) 等性能优化 +- 管理服务端状态的内存和垃圾回收 (garbage collection) +- 通过结构共享 (structural sharing) 记忆化 (memoizing) 查询结果 + +如果你没有被这个清单吓到,那一定意味着你已经解决了所有服务端状态问题,值得嘉奖。然而,如果你像绝大多数人一样,那么你要么尚未解决全部或大部分挑战,而我们才刚刚触及皮毛! + +TanStack Query 无疑是管理服务端状态的 **最佳** 库之一。它 **开箱即用、零配置**,并且可以随着应用的增长按需定制。 + +TanStack Query 能让你战胜并克服 **服务端状态** 的棘手挑战,在数据控制你之前掌控你的应用数据。 + +从技术角度来说,TanStack Query 可能会: + +- 帮助你从应用中移除 **大量** 复杂且难以理解的代码,仅用寥寥几行 TanStack Query 逻辑替代 +- 使你的应用更易维护,更容易构建新功能,而无需担心接入新的服务端状态数据源 +- 通过让你的应用感觉比以往更快、响应更迅速,直接影响终端用户的体验 +- 可能帮助你节省带宽并提升内存性能 + +[//]: # 'Example' + +## 说够了,直接看代码吧! + +在下面的示例中,你可以看到 TanStack Query 最基本、最简单的形式,用于获取 TanStack Query GitHub 项目本身的统计信息: + +[在 StackBlitz 中打开](https://stackblitz.com/github/TanStack/query/tree/main/examples/react/simple) + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/react-query' + +const queryClient = new QueryClient() + +export default function App() { + return ( + + + + ) +} + +function Example() { + const { isPending, error, data } = useQuery({ + queryKey: ['repoData'], + queryFn: () => + fetch('https://api.github.com/repos/TanStack/query').then((res) => + res.json(), + ), + }) + + if (isPending) return 'Loading...' + + if (error) return 'An error has occurred: ' + error.message + + return ( +
    +

    {data.name}

    +

    {data.description}

    + 👀 {data.subscribers_count}{' '} + ✨ {data.stargazers_count}{' '} + 🍴 {data.forks_count} +
    + ) +} +``` + +[//]: # 'Example' +[//]: # 'Materials' + +## 你说服我了,接下来呢? + +- 考虑参加官方的 [TanStack Query 课程](https://query.gg?s=tanstack)(或为整个团队购买!) +- 通过我们极其详尽的 [入门指南](./installation.md) 和 [API 参考](./reference/useQuery.md),按照自己的节奏学习 TanStack Query + +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/react/plugins/broadcastQueryClient.md b/docs/zh-hans/framework/react/plugins/broadcastQueryClient.md new file mode 100644 index 00000000000..aa4ea76cd37 --- /dev/null +++ b/docs/zh-hans/framework/react/plugins/broadcastQueryClient.md @@ -0,0 +1,61 @@ +--- +source-updated-at: '2024-12-16T15:14:41.000Z' +translation-updated-at: '2025-05-06T04:46:07.089Z' +id: broadcastQueryClient +title: broadcastQueryClient (实验性) +--- +> 重要提示:该工具目前处于实验阶段。这意味着在次要版本和补丁版本中都可能出现破坏性变更。使用时需自行承担风险。如果您选择在生产环境中依赖此实验阶段的功能,请将版本锁定到具体的补丁版本以避免意外中断。 + +`broadcastQueryClient` 是一个用于在同源浏览器标签页/窗口之间广播和同步 queryClient 状态的工具。 + +## 安装 + +该工具作为独立包提供,可通过 `'@tanstack/query-broadcast-client-experimental'` 导入。 + +## 使用方式 + +导入 `broadcastQueryClient` 函数,传入您的 `QueryClient` 实例,并可选地设置 `broadcastChannel`。 + +```tsx +import { broadcastQueryClient } from '@tanstack/query-broadcast-client-experimental' + +const queryClient = new QueryClient() + +broadcastQueryClient({ + queryClient, + broadcastChannel: 'my-app', +}) +``` + +## API + +### `broadcastQueryClient` + +向该函数传入一个 `QueryClient` 实例,并可选择性地传入 `broadcastChannel`。 + +```tsx +broadcastQueryClient({ queryClient, broadcastChannel }) +``` + +### `Options` + +选项对象: + +```tsx +interface BroadcastQueryClientOptions { + /** 需要同步的 QueryClient */ + queryClient: QueryClient + /** 用于在标签页和窗口之间通信的唯一频道名称 */ + broadcastChannel?: string + /** BroadcastChannel API 的选项 */ + options?: BroadcastChannelOptions +} +``` + +默认选项为: + +```tsx +{ + broadcastChannel = 'tanstack-query', +} +``` diff --git a/docs/zh-hans/framework/react/plugins/createAsyncStoragePersister.md b/docs/zh-hans/framework/react/plugins/createAsyncStoragePersister.md new file mode 100644 index 00000000000..7f3be4f07fd --- /dev/null +++ b/docs/zh-hans/framework/react/plugins/createAsyncStoragePersister.md @@ -0,0 +1,119 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:46:58.579Z' +id: createAsyncStoragePersister +title: createAsyncStoragePersister +--- +## 安装 + +该工具作为一个独立包提供,可通过 `'@tanstack/query-async-storage-persister'` 导入使用。 + +```bash +npm install @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +pnpm add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +yarn add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +bun add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +## 使用方式 + +- 导入 `createAsyncStoragePersister` 函数 +- 创建一个新的 asyncStoragePersister + - 可传入任何符合 `AsyncStorage` 接口的 `storage` 对象 - 以下示例使用 React Native 的 async-storage +- 使用 [`PersistQueryClientProvider`](./persistQueryClient.md#persistqueryclientprovider) 组件包裹你的应用 + +```tsx +import AsyncStorage from '@react-native-async-storage/async-storage' +import { QueryClient } from '@tanstack/react-query' +import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client' +import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小时 + }, + }, +}) + +const asyncStoragePersister = createAsyncStoragePersister({ + storage: AsyncStorage, +}) + +const Root = () => ( + + + +) + +export default Root +``` + +## 重试机制 + +重试逻辑与 [SyncStoragePersister](./createSyncStoragePersister.md) 相同,但支持异步操作。你也可以使用所有预定义的重试处理器。 + +## API 接口 + +### `createAsyncStoragePersister` + +调用此函数创建 asyncStoragePersister,后续可与 `persistQueryClient` 配合使用。 + +```tsx +createAsyncStoragePersister(options: CreateAsyncStoragePersisterOptions) +``` + +### 配置选项 + +```tsx +interface CreateAsyncStoragePersisterOptions { + /** 用于缓存存取操作的存储客户端 */ + storage: AsyncStorage | undefined | null + /** 本地存储缓存时使用的键名 */ + key?: string + /** 为避免频繁写入本地存储, + * 可设置节流时间(毫秒)来控制缓存写入频率 */ + throttleTime?: number + /** 数据序列化方法 */ + serialize?: (client: PersistedClient) => string + /** 数据反序列化方法 */ + deserialize?: (cachedString: string) => PersistedClient + /** 错误时的持久化重试策略 **/ + retry?: AsyncPersistRetryer +} + +interface AsyncStorage { + getItem: (key: string) => Promise + setItem: (key: string, value: string) => Promise + removeItem: (key: string) => Promise +} +``` + +默认配置为: + +```tsx +{ + key = `REACT_QUERY_OFFLINE_CACHE`, + throttleTime = 1000, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` diff --git a/docs/zh-hans/framework/react/plugins/createPersister.md b/docs/zh-hans/framework/react/plugins/createPersister.md new file mode 100644 index 00000000000..2289febfb4d --- /dev/null +++ b/docs/zh-hans/framework/react/plugins/createPersister.md @@ -0,0 +1,135 @@ +--- +source-updated-at: '2024-02-25T09:15:32.000Z' +translation-updated-at: '2025-05-06T04:45:26.192Z' +id: createPersister +title: createPersister (实验性) +--- +## 安装 + +该工具作为一个独立包提供,可通过 `'@tanstack/query-persist-client-core'` 导入使用。 + +```bash +npm install @tanstack/query-persist-client-core +``` + +或 + +```bash +pnpm add @tanstack/query-persist-client-core +``` + +或 + +```bash +yarn add @tanstack/query-persist-client-core +``` + +或 + +```bash +bun add @tanstack/query-persist-client-core +``` + +> 注意:此工具也包含在 `@tanstack/react-query-persist-client` 包中,因此如果已使用该包则无需单独安装。 + +## 使用方式 + +- 导入 `experimental_createPersister` 函数 +- 创建一个新的 `experimental_createPersister` + - 可传入任何符合 `AsyncStorage` 或 `Storage` 接口的 `storage` —— 以下示例使用 React Native 的 async-storage +- 将该 `persister` 作为选项传递给 Query。可通过两种方式实现:传递给 `QueryClient` 的 `defaultOptions`,或传递给任意 `useQuery` 钩子实例 + - 若将 `persister` 作为 `defaultOptions` 传递,所有查询将被持久化到提供的 `storage` 中。还可通过传递 `filters` 进一步缩小范围。与 `persistClient` 插件不同,此方式不会将整个 query client 作为单个项目持久化,而是分别持久化每个查询。查询哈希值将作为键名使用 + - 若将 `persister` 提供给单个 `useQuery` 钩子,则仅该查询会被持久化 + +这种方式无需存储整个 `QueryClient`,而是可选择应用中值得持久化的内容。每个查询都是延迟恢复(当查询首次使用时)和持久化(在每次 `queryFn` 运行后)的,因此无需节流处理。恢复查询后也会遵循 `staleTime` 设置,因此若数据被视为 `stale`,将在恢复后立即重新获取;若数据为 `fresh` 状态,则不会运行 `queryFn`。 + +从内存中垃圾回收查询**不会**影响持久化数据。这意味着可以缩短查询在内存中的保留时间以提高**内存效率**。当下次使用时,它们会直接从持久化存储中恢复。 + +```tsx +import AsyncStorage from '@react-native-async-storage/async-storage' +import { QueryClient } from '@tanstack/react-query' +import { experimental_createPersister } from '@tanstack/query-persist-client-core' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 30, // 30 秒 + persister: experimental_createPersister({ + storage: AsyncStorage, + maxAge: 1000 * 60 * 60 * 12, // 12 小时 + }), + }, + }, +}) +``` + +### 适配的默认值 + +`createPersister` 插件在技术上封装了 `queryFn`,因此若 `queryFn` 未运行则不会恢复。在此意义上,它充当了查询与网络之间的缓存层。因此,当使用 persister 时,`networkMode` 默认设为 `'offlineFirst'`,以便即使没有网络连接也能从持久化存储中恢复。 + +## API + +### `experimental_createPersister` + +```tsx +experimental_createPersister(options: StoragePersisterOptions) +``` + +#### `选项` + +```tsx +export interface StoragePersisterOptions { + /** 用于设置和检索缓存项的存储客户端 + * 对于 SSR 请传入 `undefined` + */ + storage: AsyncStorage | Storage | undefined | null + /** + * 如何将数据序列化存储 + * @默认值 `JSON.stringify` + */ + serialize?: (persistedQuery: PersistedQuery) => string + /** + * 如何从存储中反序列化数据 + * @默认值 `JSON.parse` + */ + deserialize?: (cachedString: string) => PersistedQuery + /** + * 用于强制使现有缓存失效的唯一字符串 + * 若缓存不共享相同的破坏字符串则会被视为无效 + */ + buster?: string + /** + * 缓存允许的最大存活时间(毫秒) + * 若发现持久化缓存超过此时长 + * 将被丢弃 + * @默认值 24 小时 + */ + maxAge?: number + /** + * 存储键的前缀 + * 存储键由前缀和查询哈希组成,格式为 `prefix-queryHash` + */ + prefix?: string + /** + * 用于筛选哪些查询应被持久化的过滤器 + */ + filters?: QueryFilters +} + +interface AsyncStorage { + getItem: (key: string) => Promise + setItem: (key: string, value: string) => Promise + removeItem: (key: string) => Promise +} +``` + +默认选项为: + +```tsx +{ + prefix = 'tanstack-query', + maxAge = 1000 * 60 * 60 * 24, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` diff --git a/docs/zh-hans/framework/react/plugins/createSyncStoragePersister.md b/docs/zh-hans/framework/react/plugins/createSyncStoragePersister.md new file mode 100644 index 00000000000..f0ba27a3300 --- /dev/null +++ b/docs/zh-hans/framework/react/plugins/createSyncStoragePersister.md @@ -0,0 +1,155 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:44:34.356Z' +id: createSyncStoragePersister +title: createSyncStoragePersister +--- +## 安装 + +该工具作为独立包提供,可通过 `'@tanstack/query-sync-storage-persister'` 导入使用。 + +```bash +npm install @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +pnpm add @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +yarn add @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +bun add @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +## 使用方式 + +- 导入 `createSyncStoragePersister` 函数 +- 创建新的 syncStoragePersister 实例 +- 将其传递给 [`persistQueryClient`](./persistQueryClient.md) 函数 + +```tsx +import { persistQueryClient } from '@tanstack/react-query-persist-client' +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小时 + }, + }, +}) + +const localStoragePersister = createSyncStoragePersister({ + storage: window.localStorage, +}) +// const sessionStoragePersister = createSyncStoragePersister({ storage: window.sessionStorage }) + +persistQueryClient({ + queryClient, + persister: localStoragePersister, +}) +``` + +## 重试机制 + +持久化可能失败(例如当数据大小超出存储空间时)。通过向 persister 提供 `retry` 函数可以优雅处理错误。 + +重试函数接收尝试保存的 `persistedClient`、发生的 `error` 以及重试次数 `errorCount` 作为输入。它应返回一个_新的_ `PersistedClient` 用于再次尝试持久化。若返回 _undefined_ 则表示不再尝试。 + +```tsx +export type PersistRetryer = (props: { + persistedClient: PersistedClient + error: Error + errorCount: number +}) => PersistedClient | undefined +``` + +### 预定义策略 + +默认情况下不会进行重试。您可以使用以下预定义策略处理重试(需从 `'@tanstack/react-query-persist-client'` 导入): + +- `removeOldestQuery` + - 返回移除最旧查询后的新 `PersistedClient` + +```tsx +const localStoragePersister = createSyncStoragePersister({ + storage: window.localStorage, + retry: removeOldestQuery, +}) +``` + +## API 接口 + +### `createSyncStoragePersister` + +调用此函数创建 syncStoragePersister 实例,后续可配合 `persistQueryClient` 使用。 + +```tsx +createSyncStoragePersister(options: CreateSyncStoragePersisterOptions) +``` + +### 配置选项 + +```tsx +interface CreateSyncStoragePersisterOptions { + /** 用于缓存存取的存储客户端 (window.localStorage 或 window.sessionStorage) */ + storage: Storage | undefined | null + /** 存储缓存时使用的键名 */ + key?: string + /** 为避免频繁操作, + * 可设置节流时间(毫秒)来控制存储频率 */ + throttleTime?: number + /** 数据序列化方法 */ + serialize?: (client: PersistedClient) => string + /** 数据反序列化方法 */ + deserialize?: (cachedString: string) => PersistedClient + /** 错误时的重试策略 **/ + retry?: PersistRetryer +} +``` + +默认配置为: + +```tsx +{ + key = `REACT_QUERY_OFFLINE_CACHE`, + throttleTime = 1000, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` + +#### `serialize` 与 `deserialize` 选项 + +`localStorage` 存在存储容量限制。如需存储更多数据,可覆写这两个函数,使用 [lz-string](https://github.com/pieroxy/lz-string/) 等库进行数据压缩/解压。 + +```tsx +import { QueryClient } from '@tanstack/react-query' +import { persistQueryClient } from '@tanstack/react-query-persist-client' +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' + +import { compress, decompress } from 'lz-string' + +const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: Infinity } }, +}) + +persistQueryClient({ + queryClient: queryClient, + persister: createSyncStoragePersister({ + storage: window.localStorage, + serialize: (data) => compress(JSON.stringify(data)), + deserialize: (data) => JSON.parse(decompress(data)), + }), + maxAge: Infinity, +}) +``` diff --git a/docs/zh-hans/framework/react/plugins/persistQueryClient.md b/docs/zh-hans/framework/react/plugins/persistQueryClient.md new file mode 100644 index 00000000000..4adb1887b6e --- /dev/null +++ b/docs/zh-hans/framework/react/plugins/persistQueryClient.md @@ -0,0 +1,287 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:43:46.632Z' +id: persistQueryClient +title: persistQueryClient +--- +这是一套用于与“持久化工具 (persisters)”交互的工具集,它们能保存你的 `queryClient` 以供后续使用。不同的 **持久化工具 (persisters)** 可将客户端和缓存存储到多种不同的存储层中。 + +## 构建持久化工具 + +- [createSyncStoragePersister](./createSyncStoragePersister.md) +- [createAsyncStoragePersister](./createAsyncStoragePersister.md) +- [创建自定义持久化工具](#persisters) + +## 工作原理 + +**重要提示** - 为确保持久化正常工作,你可能需要在 `QueryClient` 中传递 `gcTime` 值以覆盖默认的水合 (hydration) 设置(如上所示)。 + +如果在创建 `QueryClient` 实例时未设置此值,水合阶段将默认使用 `300000`(5 分钟),且存储的缓存会在 5 分钟不活动后被丢弃。这是默认的垃圾回收行为。 + +该值应设置为与 `persistQueryClient` 的 `maxAge` 选项相同或更高。例如,若 `maxAge` 为 24 小时(默认值),则 `gcTime` 应设为 24 小时或更长。若低于 `maxAge`,垃圾回收会提前触发并丢弃存储的缓存。 + +你也可以传递 `Infinity` 来完全禁用垃圾回收行为。 + +由于 JavaScript 的限制,最大允许的 `gcTime` 约为 24 天(详见[更多信息](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value))。 + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小时 + }, + }, +}) +``` + +### 缓存清除 (Cache Busting) + +有时你可能对应用或数据进行了更改,这些更改会立即使所有缓存数据失效。此时,你可以传递一个 `buster` 字符串选项。如果找到的缓存不包含该字符串,它将被丢弃。以下多个函数接受此选项: + +```tsx +persistQueryClient({ queryClient, persister, buster: buildHash }) +persistQueryClientSave({ queryClient, persister, buster: buildHash }) +persistQueryClientRestore({ queryClient, persister, buster: buildHash }) +``` + +### 移除机制 + +如果数据满足以下任一条件: + +1. 已过期(见 `maxAge`) +2. 已清除(见 `buster`) +3. 出现错误(如 `throws ...`) +4. 为空(如 `undefined`) + +持久化工具的 `removeClient()` 将被调用,缓存会立即被丢弃。 + +## API + +### `persistQueryClientSave` + +- 你的查询/突变会被 [`脱水 (dehydrated)`](../reference/hydration.md#dehydrate) 并通过你提供的持久化工具存储。 +- `createSyncStoragePersister` 和 `createAsyncStoragePersister` 会对此操作进行节流,最多每 1 秒执行一次,以避免潜在的昂贵写入。查阅其文档以了解如何自定义节流时间。 + +你可以用它在选择的时刻显式持久化缓存。 + +```tsx +persistQueryClientSave({ + queryClient, + persister, + buster = '', + dehydrateOptions = undefined, +}) +``` + +### `persistQueryClientSubscribe` + +每当 `queryClient` 的缓存发生变化时运行 `persistQueryClientSave`。例如:你可以在用户登录并勾选“记住我”时启动 `subscribe`。 + +- 它返回一个 `unsubscribe` 函数,可用于停止监听,结束对持久化缓存的更新。 +- 如果希望在 `unsubscribe` 后清除持久化缓存,可以向 `persistQueryClientRestore` 发送一个新的 `buster`,这将触发持久化工具的 `removeClient` 函数并丢弃持久化缓存。 + +```tsx +persistQueryClientSubscribe({ + queryClient, + persister, + buster = '', + dehydrateOptions = undefined, +}) +``` + +### `persistQueryClientRestore` + +- 尝试从持久化工具中 [`水合 (hydrate)`](../reference/hydration.md#hydrate) 先前脱水的查询/突变缓存,将其恢复到传入的查询客户端中。 +- 如果找到的缓存比 `maxAge`(默认为 24 小时)更旧,它将被丢弃。此时间可根据需要自定义。 + +你可以用它在选择的时刻恢复缓存。 + +```tsx +persistQueryClientRestore({ + queryClient, + persister, + maxAge = 1000 * 60 * 60 * 24, // 24 小时 + buster = '', + hydrateOptions = undefined, +}) +``` + +### `persistQueryClient` + +执行以下操作: + +1. 立即恢复所有持久化缓存(见 [`persistQueryClientRestore`](#persistqueryclientrestore)) +2. 订阅查询缓存并返回 `unsubscribe` 函数(见 [`persistQueryClientSubscribe`](#persistqueryclientsubscribe))。 + +此功能从 3.x 版本保留至今。 + +```tsx +persistQueryClient({ + queryClient, + persister, + maxAge = 1000 * 60 * 60 * 24, // 24 小时 + buster = '', + hydrateOptions = undefined, + dehydrateOptions = undefined, +}) +``` + +### `Options` + +所有可用选项如下: + +```tsx +interface PersistQueryClientOptions { + /** 需要持久化的 QueryClient */ + queryClient: QueryClient + /** 用于存储和恢复缓存的持久化工具接口 */ + persister: Persister + /** 缓存的最大允许存活时间(毫秒)。 + * 如果找到的持久化缓存比此时间更旧, + * 它将 **静默** 被丢弃(默认为 24 小时) */ + maxAge?: number + /** 用于强制使现有缓存失效的唯一字符串, + * 如果它们不共享相同的 buster 字符串 */ + buster?: string + /** 传递给 hydrate 函数的选项, + * 不用于 `persistQueryClientSave` 或 `persistQueryClientSubscribe` */ + hydrateOptions?: HydrateOptions + /** 传递给 dehydrate 函数的选项, + * 不用于 `persistQueryClientRestore` */ + dehydrateOptions?: DehydrateOptions +} +``` + +实际上有三种接口可用: + +- `PersistedQueryClientSaveOptions` 用于 `persistQueryClientSave` 和 `persistQueryClientSubscribe`(不使用 `hydrateOptions`)。 +- `PersistedQueryClientRestoreOptions` 用于 `persistQueryClientRestore`(不使用 `dehydrateOptions`)。 +- `PersistQueryClientOptions` 用于 `persistQueryClient` + +## 与 React 一起使用 + +[persistQueryClient](#persistQueryClient) 会尝试恢复缓存并自动订阅后续更改,从而将你的客户端同步到提供的存储中。 + +然而,恢复是异步的,因为所有持久化工具本质上都是异步的。这意味着如果在恢复过程中渲染应用,可能会在查询挂载和获取同时发生时遇到竞态条件。 + +此外,如果在 React 组件生命周期之外订阅更改,你将无法取消订阅: + +```tsx +// 🚨 永远不会停止同步订阅 +persistQueryClient({ + queryClient, + persister: localStoragePersister, +}) + +// 🚨 与恢复同时发生 +ReactDOM.createRoot(rootElement).render() +``` + +### PersistQueryClientProvider + +针对此用例,你可以使用 `PersistQueryClientProvider`。它会根据 React 组件生命周期正确订阅/取消订阅,并确保在恢复过程中查询不会开始获取。查询仍会渲染,但它们会被置为 `fetchingState: 'idle'`,直到数据恢复完成。之后,除非恢复的数据足够新,否则它们会重新获取,同时也会尊重 `initialData`。它可以替代普通的 [QueryClientProvider](../reference/QueryClientProvider.md): + +```tsx +import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client' +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小时 + }, + }, +}) + +const persister = createSyncStoragePersister({ + storage: window.localStorage, +}) + +ReactDOM.createRoot(rootElement).render( + + + , +) +``` + +#### Props + +`PersistQueryClientProvider` 接受与 [QueryClientProvider](../reference/QueryClientProvider.md) 相同的 props,并额外包括: + +- `persistOptions: PersistQueryClientOptions` + - 所有可传递给 [persistQueryClient](#persistqueryclient) 的[选项](#options),但不包括 `QueryClient` 本身 +- `onSuccess?: () => Promise | unknown` + - 可选 + - 初始恢复完成时调用 + - 可用于 [resumePausedMutations](../../../reference/QueryClient.md#queryclientresumepausedmutations) + - 如果返回 Promise,会等待其完成;恢复过程在此期间被视为进行中 + +### useIsRestoring + +如果使用 `PersistQueryClientProvider`,还可以配合使用 `useIsRestoring` 钩子来检查当前是否正在进行恢复。`useQuery` 等函数内部也会检查此状态,以避免恢复和挂载查询之间的竞态条件。 + +## 持久化工具 + +### 持久化工具接口 + +持久化工具具有以下接口: + +```tsx +export interface Persister { + persistClient(persistClient: PersistedClient): Promisable + restoreClient(): Promisable + removeClient(): Promisable +} +``` + +持久化客户端条目具有以下接口: + +```tsx +export interface PersistedClient { + timestamp: number + buster: string + cacheState: any +} +``` + +你可以导入这些接口(以构建持久化工具): + +```tsx +import { + PersistedClient, + Persister, +} from '@tanstack/react-query-persist-client' +``` + +### 构建持久化工具 + +你可以按需实现持久化。以下是一个构建 [Indexed DB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) 持久化工具的示例。相比 `Web Storage API`,Indexed DB 更快,存储容量超过 5MB,且不需要序列化。这意味着它能直接存储 JavaScript 原生类型,如 `Date` 和 `File`。 + +```tsx +import { get, set, del } from 'idb-keyval' +import { + PersistedClient, + Persister, +} from '@tanstack/react-query-persist-client' + +/** + * 创建一个 Indexed DB 持久化工具 + * @see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API + */ +export function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') { + return { + persistClient: async (client: PersistedClient) => { + await set(idbValidKey, client) + }, + restoreClient: async () => { + return await get(idbValidKey) + }, + removeClient: async () => { + await del(idbValidKey) + }, + } satisfies Persister +} +``` diff --git a/docs/zh-hans/framework/react/quick-start.md b/docs/zh-hans/framework/react/quick-start.md new file mode 100644 index 00000000000..3011be825cd --- /dev/null +++ b/docs/zh-hans/framework/react/quick-start.md @@ -0,0 +1,78 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-03T21:51:49.019Z' +id: quick-start +title: 快速开始 +--- +以下代码片段简要展示了 React Query 的 3 个核心概念: + +- [查询 (Queries)](./guides/queries.md) +- [变更 (Mutations)](./guides/mutations.md) +- [查询失效 (Query Invalidation)](./guides/query-invalidation.md) + +[//]: # '示例' + +如需查看完整功能示例,请参考我们的 [简单 StackBlitz 示例](../examples/simple) + +```tsx +import { + useQuery, + useMutation, + useQueryClient, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' +import { getTodos, postTodo } from '../my-api' + +// 创建客户端 +const queryClient = new QueryClient() + +function App() { + return ( + // 将客户端提供给应用 + + + + ) +} + +function Todos() { + // 访问客户端 + const queryClient = useQueryClient() + + // 查询 + const query = useQuery({ queryKey: ['todos'], queryFn: getTodos }) + + // 变更 + const mutation = useMutation({ + mutationFn: postTodo, + onSuccess: () => { + // 使缓存失效并重新获取 + queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, + }) + + return ( +
    +
      {query.data?.map((todo) =>
    • {todo.title}
    • )}
    + + +
    + ) +} + +render(, document.getElementById('root')) +``` + +[//]: # '示例' + +这三个概念构成了 React Query 的大部分核心功能。文档的后续章节将详细讲解每个核心概念。 diff --git a/docs/zh-hans/framework/react/react-native.md b/docs/zh-hans/framework/react/react-native.md new file mode 100644 index 00000000000..97e9edb44e0 --- /dev/null +++ b/docs/zh-hans/framework/react/react-native.md @@ -0,0 +1,117 @@ +--- +source-updated-at: '2025-02-12T15:01:46.000Z' +translation-updated-at: '2025-05-06T04:25:35.500Z' +id: react-native +title: React Native +--- +React Query 设计上可直接在 React Native 中开箱使用,但目前开发工具(devtools)仅支持 React DOM。 + +您可以尝试以下第三方插件: +- [Expo](https://docs.expo.dev/) 插件:https://github.com/expo/dev-plugins/tree/main/packages/react-query +- [Flipper](https://fbflipper.com/docs/getting-started/react-native/) 插件:https://github.com/bgaleotti/react-query-native-devtools +- [Reactotron](https://github.com/infinitered/reactotron/) 插件:https://github.com/hsndmr/reactotron-react-query + +若您愿意协助我们开发跨平台的内置开发工具,欢迎随时联系! + +## 在线状态管理 + +React Query 已支持浏览器断网重连后自动重新请求数据。在 React Native 中实现该功能需使用 `onlineManager`,示例如下: + +```tsx +import NetInfo from '@react-native-community/netinfo' +import { onlineManager } from '@tanstack/react-query' + +onlineManager.setEventListener((setOnline) => { + return NetInfo.addEventListener((state) => { + setOnline(!!state.isConnected) + }) +}) +``` + +或 + +```tsx +import { onlineManager } from '@tanstack/react-query' +import * as Network from 'expo-network' + +onlineManager.setEventListener((setOnline) => { + const eventSubscription = Network.addNetworkStateListener((state) => { + setOnline(!!state.isConnected) + }) + return eventSubscription.remove +}) +``` + +## 应用聚焦时重新请求 + +不同于浏览器监听 `window` 事件,React Native 通过 [`AppState` 模块](https://reactnative.dev/docs/appstate#app-states) 提供应用状态信息。可利用 "change" 事件在应用状态变为 "active" 时触发更新: + +```tsx +import { useEffect } from 'react' +import { AppState, Platform } from 'react-native' +import type { AppStateStatus } from 'react-native' +import { focusManager } from '@tanstack/react-query' + +function onAppStateChange(status: AppStateStatus) { + if (Platform.OS !== 'web') { + focusManager.setFocused(status === 'active') + } +} + +useEffect(() => { + const subscription = AppState.addEventListener('change', onAppStateChange) + + return () => subscription.remove() +}, []) +``` + +## 屏幕聚焦时刷新 + +某些场景下,您可能需要在 React Native 屏幕重新聚焦时重新请求数据。以下自定义钩子会在屏幕聚焦时调用传入的 `refetch` 方法: + +```tsx +import React from 'react' +import { useFocusEffect } from '@react-navigation/native' + +export function useRefreshOnFocus(refetch: () => Promise) { + const firstTimeRef = React.useRef(true) + + useFocusEffect( + React.useCallback(() => { + if (firstTimeRef.current) { + firstTimeRef.current = false + return + } + + refetch() + }, [refetch]), + ) +} +``` + +上述代码中首次加载会跳过 `refetch`,因为 `useFocusEffect` 在组件挂载时也会触发回调。 + +## 禁用非聚焦屏幕的查询 + +若您不希望某些查询在屏幕失焦时保持活跃状态,可通过 `useQuery` 的 `subscribed` 属性控制查询是否持续订阅更新。结合 React Navigation 的 `useIsFocused`,可实现屏幕失焦时自动取消订阅: + +```tsx +import React from 'react' +import { useIsFocused } from '@react-navigation/native' +import { useQuery } from '@tanstack/react-query' +import { Text } from 'react-native' + +function MyComponent() { + const isFocused = useIsFocused() + + const { dataUpdatedAt } = useQuery({ + queryKey: ['key'], + queryFn: () => fetch(...), + subscribed: isFocused, + }) + + return DataUpdatedAt: {dataUpdatedAt} +} +``` + +当 `subscribed` 为 `false` 时,查询将取消订阅更新,既不会触发重渲染也不会为该屏幕获取新数据。当值恢复为 `true`(例如屏幕重新聚焦时),查询会重新订阅并保持数据最新。 diff --git a/docs/zh-hans/framework/react/reference/QueryClientProvider.md b/docs/zh-hans/framework/react/reference/QueryClientProvider.md new file mode 100644 index 00000000000..44c9064357d --- /dev/null +++ b/docs/zh-hans/framework/react/reference/QueryClientProvider.md @@ -0,0 +1,23 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:41:57.472Z' +id: QueryClientProvider +title: QueryClientProvider +--- +使用 `QueryClientProvider` 组件将 `QueryClient` 连接并提供给您的应用: + +```tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +const queryClient = new QueryClient() + +function App() { + return ... +} +``` + +**配置项** + +- `client: QueryClient` + - **必填** + - 需要提供的 QueryClient 实例 diff --git a/docs/zh-hans/framework/react/reference/QueryErrorResetBoundary.md b/docs/zh-hans/framework/react/reference/QueryErrorResetBoundary.md new file mode 100644 index 00000000000..f2310446d95 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/QueryErrorResetBoundary.md @@ -0,0 +1,65 @@ +--- +source-updated-at: '2024-04-11T09:16:39.000Z' +translation-updated-at: '2025-05-06T04:41:50.065Z' +id: QueryErrorResetBoundary +title: QueryErrorResetBoundary +--- +## 动机 + +大多数核心 Web 框架**并未**提供一种全面的数据获取或更新方案。因此,开发者最终要么构建封装了严格数据获取规范的元框架,要么自行发明数据获取方式。这通常意味着拼凑基于组件的状态和副作用,或是使用更通用的状态管理库来存储并提供应用中的异步数据。 + +虽然大多数传统状态管理库非常适合处理客户端状态,但它们**在处理异步或服务端状态时表现欠佳**。这是因为**服务端状态完全不同**。首先,服务端状态: + +- 持久化存储在您可能无法控制或拥有的远程位置 +- 需要通过异步 API 进行获取和更新 +- 意味着共享所有权,可能被他人更改而您不知情 +- 如果不加注意,可能在应用中变得"过时" + +一旦理解了应用中服务端状态的本质,**更多挑战将接踵而至**,例如: + +- 缓存...(可能是编程中最难实现的部分) +- 将针对相同数据的多个请求去重为单个请求 +- 在后台更新"过时"数据 +- 判断数据何时"过时" +- 尽可能快地反映数据更新 +- 分页和懒加载等性能优化 +- 管理服务端状态的内存和垃圾回收 +- 通过结构共享记忆化查询结果 + +如果您没有被这个清单吓到,那一定意味着您已经解决了所有服务端状态问题并值得嘉奖。然而,如果您像绝大多数人一样,那么您要么尚未解决全部或大部分挑战,而我们才刚刚触及表面! + +TanStack Query 无疑是管理服务端状态的最佳库之一。它开箱即用,零配置就能表现出色,并且可以随着应用增长按需定制。 + +TanStack Query 让您能够战胜和克服服务端状态的各种棘手挑战,在应用数据控制您之前掌控它们。 + +从技术角度而言,TanStack Query 很可能: + +- 帮助您从应用中移除**大量**复杂且难以理解的代码,仅用几行 TanStack Query 逻辑替代 +- 提高应用的可维护性,更轻松地构建新功能而无需担心连接新的服务端状态数据源 +- 通过使应用感觉比以往更快、响应更迅速,直接影响最终用户体验 +- 可能帮助节省带宽并提升内存性能 + +当在查询中使用 **suspense** 或 **throwOnError** 时,您需要一种方式让查询知道在发生错误后重新渲染时您想重试。通过 `QueryErrorResetBoundary` 组件,您可以重置组件边界内的任何查询错误。 + +```tsx +import { QueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => ( + + {({ reset }) => ( + ( +
    + There was an error! + +
    + )} + > + +
    + )} +
    +) +``` diff --git a/docs/zh-hans/framework/react/reference/hydration.md b/docs/zh-hans/framework/react/reference/hydration.md new file mode 100644 index 00000000000..bc3309d511b --- /dev/null +++ b/docs/zh-hans/framework/react/reference/hydration.md @@ -0,0 +1,129 @@ +--- +source-updated-at: '2025-02-17T12:52:17.000Z' +translation-updated-at: '2025-05-06T04:41:17.708Z' +id: hydration +title: 注水 +--- +## `dehydrate` + +`dehydrate` 会创建一个 `cache` 的冻结表示,后续可通过 `HydrationBoundary` 或 `hydrate` 进行水合。这在将预取查询从服务端传递到客户端,或将查询持久化到 localStorage 或其他持久化存储时非常有用。默认情况下,它仅包含当前成功的查询。 + +```tsx +import { dehydrate } from '@tanstack/react-query' + +const dehydratedState = dehydrate(queryClient, { + shouldDehydrateQuery, + shouldDehydrateMutation, +}) +``` + +**选项** + +- `client: QueryClient` + - **必填** + - 需要被脱水的 `queryClient` +- `options: DehydrateOptions` + - 可选 + - `shouldDehydrateMutation: (mutation: Mutation) => boolean` + - 可选 + - 是否对变更 (mutation) 进行脱水 + - 该函数会针对缓存中的每个变更被调用 + - 返回 `true` 表示将此变更包含在脱水中,否则返回 `false` + - 默认仅包含暂停的变更 + - 若希望在保留默认行为的同时扩展此函数,可在返回语句中导入并执行 `defaultShouldDehydrateMutation` + - `shouldDehydrateQuery: (query: Query) => boolean` + - 可选 + - 是否对查询进行脱水 + - 该函数会针对缓存中的每个查询被调用 + - 返回 `true` 表示将此查询包含在脱水中,否则返回 `false` + - 默认仅包含成功的查询 + - 若希望在保留默认行为的同时扩展此函数,可在返回语句中导入并执行 `defaultShouldDehydrateQuery` + - `serializeData?: (data: any) => any` 用于在脱水期间转换(序列化)数据的函数 + - `shouldRedactErrors?: (error: unknown) => boolean` + - 可选 + - 是否在脱水过程中对来自服务端的错误进行脱敏 + - 该函数会针对缓存中的每个错误被调用 + - 返回 `true` 表示对此错误脱敏,否则返回 `false` + - 默认会对所有错误脱敏 + +**返回值** + +- `dehydratedState: DehydratedState` + - 包含后续水合 `queryClient` 所需的所有内容 + - 你**不应**依赖此响应的具体格式,它不属于公共 API 且可能随时变更 + - 此结果未经过序列化处理,如有需要请自行序列化 + +### 限制 + +某些存储系统(如浏览器的 [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API))要求值必须可 JSON 序列化。如果你需要脱水无法自动序列化为 JSON 的值(如 `Error` 或 `undefined`),必须自行序列化。由于默认仅包含成功的查询,若要同时包含 `Errors`,需提供 `shouldDehydrateQuery`,例如: + +```tsx +// 服务端 +const state = dehydrate(client, { shouldDehydrateQuery: () => true }) // 同时包含 Errors +const serializedState = mySerialize(state) // 将 Error 实例转换为对象 + +// 客户端 +const state = myDeserialize(serializedState) // 将对象转换回 Error 实例 +hydrate(client, state) +``` + +## `hydrate` + +`hydrate` 将先前脱水的状态添加回 `cache` 中。 + +```tsx +import { hydrate } from '@tanstack/react-query' + +hydrate(queryClient, dehydratedState, options) +``` + +**选项** + +- `client: QueryClient` + - **必填** + - 要注入状态的 `queryClient` +- `dehydratedState: DehydratedState` + - **必填** + - 要注入到客户端的状态 +- `options: HydrateOptions` + - 可选 + - `defaultOptions: DefaultOptions` + - 可选 + - `mutations: MutationOptions` 用于已水合变更的默认变更选项 + - `queries: QueryOptions` 用于已水合查询的默认查询选项 + - `deserializeData?: (data: any) => any` 在将数据放入缓存前对其进行转换(反序列化)的函数 + - `queryClient?: QueryClient` + - 使用此选项可指定自定义 QueryClient。否则将使用最近上下文中的 QueryClient + +### 限制 + +若尝试水合的查询已存在于 queryCache 中,`hydrate` 仅会在数据比缓存中现有数据更新时覆盖它们。否则,这些数据**不会**被应用。 + +[//]: # 'HydrationBoundary' + +## `HydrationBoundary` + +`HydrationBoundary` 将先前脱水的状态注入到由 `useQueryClient()` 返回的 `queryClient` 中。如果客户端已包含数据,新查询将基于更新时间戳智能合并。 + +```tsx +import { HydrationBoundary } from '@tanstack/react-query' + +function App() { + return ... +} +``` + +> 注意:只有 `queries` 可以通过 `HydrationBoundary` 进行脱水 + +**选项** + +- `state: DehydratedState` + - 要水合的状态 +- `options: HydrateOptions` + - 可选 + - `defaultOptions: QueryOptions` + - 用于已水合查询的默认查询选项 + - `queryClient?: QueryClient` + - 使用此选项可指定自定义 QueryClient。否则将使用最近上下文中的 QueryClient + +[//]: # 'HydrationBoundary' diff --git a/docs/zh-hans/framework/react/reference/infiniteQueryOptions.md b/docs/zh-hans/framework/react/reference/infiniteQueryOptions.md new file mode 100644 index 00000000000..b690d3d50af --- /dev/null +++ b/docs/zh-hans/framework/react/reference/infiniteQueryOptions.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:40:23.130Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +--- +```tsx +infiniteQueryOptions({ + queryKey, + ...options, +}) +``` + +**选项配置** + +通常你可以向 `infiniteQueryOptions` 传递所有 [`useInfiniteQuery`](./useInfiniteQuery.md) 支持的参数。部分选项在被转发到如 `queryClient.prefetchInfiniteQuery` 这类函数时不会生效,但 TypeScript 仍会允许这些额外的属性存在。 + +- `queryKey: QueryKey` + - **必填** + - 用于生成选项配置的查询键 (Query Key)。 diff --git a/docs/zh-hans/framework/react/reference/queryOptions.md b/docs/zh-hans/framework/react/reference/queryOptions.md new file mode 100644 index 00000000000..dabc8056057 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/queryOptions.md @@ -0,0 +1,25 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:40:14.841Z' +id: queryOptions +title: queryOptions +--- +```tsx +queryOptions({ + queryKey, + ...options, +}) +``` + +**选项** + +通常你可以向 `queryOptions` 传递所有能传递给 [`useQuery`](./useQuery.md) 的参数。部分选项在被转发到类似 `queryClient.prefetchQuery` 的函数时不会生效,但 TypeScript 仍会允许这些额外属性的存在。 + +- `queryKey: QueryKey` + - **必填** + - 用于生成选项的查询键 (query key) +- `experimental_prefetchInRender?: boolean` + - 可选 + - 默认为 `false` + - 设为 `true` 时,查询将在渲染期间预取,这对某些优化场景很有用 + - 需要开启此选项才能使用实验性的 `useQuery().promise` 功能 diff --git a/docs/zh-hans/framework/react/reference/useInfiniteQuery.md b/docs/zh-hans/framework/react/reference/useInfiniteQuery.md new file mode 100644 index 00000000000..a209ef36304 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useInfiniteQuery.md @@ -0,0 +1,93 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:40:03.526Z' +id: useInfiniteQuery +title: useInfiniteQuery +--- +```tsx +const { + fetchNextPage, + fetchPreviousPage, + hasNextPage, + hasPreviousPage, + isFetchingNextPage, + isFetchingPreviousPage, + promise, + ...result +} = useInfiniteQuery({ + queryKey, + queryFn: ({ pageParam }) => fetchPage(pageParam), + initialPageParam: 1, + ...options, + getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => + lastPage.nextCursor, + getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) => + firstPage.prevCursor, +}) +``` + +**选项** + +`useInfiniteQuery` 的选项与 [`useQuery` 钩子](./useQuery.md) 相同,但额外包含以下参数: + +- `queryFn: (context: QueryFunctionContext) => Promise` + - **必填(仅当未定义默认查询函数时)** [`defaultQueryFn`](../guides/default-query-function.md) + - 查询用于请求数据的函数。 + - 接收一个 [QueryFunctionContext](../guides/query-functions.md#queryfunctioncontext) 对象。 + - 必须返回一个会解析为数据或抛出错误的 Promise。 +- `initialPageParam: TPageParam` + - **必填** + - 获取第一页时使用的默认页面参数。 +- `getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null` + - **必填** + - 当查询接收到新数据时,此函数会接收无限数据列表的最后一页、所有页面的完整数组以及页面参数信息。 + - 应返回一个**单一变量**,该变量将作为最后一个可选参数传递给查询函数。 + - 返回 `undefined` 或 `null` 表示没有下一页可用。 +- `getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) => TPageParam | undefined | null` + - 当查询接收到新数据时,此函数会接收无限数据列表的第一页、所有页面的完整数组以及页面参数信息。 + - 应返回一个**单一变量**,该变量将作为最后一个可选参数传递给查询函数。 + - 返回 `undefined` 或 `null` 表示没有上一页可用。 +- `maxPages: number | undefined` + - 无限查询数据中存储的最大页数。 + - 当达到最大页数时,获取新页将导致从页面数组中移除第一页或最后一页(取决于指定的方向)。 + - 如果为 `undefined` 或等于 `0`,则页数不受限制。 + - 默认值为 `undefined`。 + - 如果 `maxPages` 值大于 `0`,则必须正确定义 `getNextPageParam` 和 `getPreviousPageParam`,以便在需要时允许双向获取页面。 + +**返回值** + +`useInfiniteQuery` 返回的属性与 [`useQuery` 钩子](./useQuery.md) 相同,但额外包含以下属性,且 `isRefetching` 和 `isRefetchError` 略有不同: + +- `data.pages: TData[]` + - 包含所有页面的数组。 +- `data.pageParams: unknown[]` + - 包含所有页面参数的数组。 +- `isFetchingNextPage: boolean` + - 当通过 `fetchNextPage` 获取下一页时为 `true`。 +- `isFetchingPreviousPage: boolean` + - 当通过 `fetchPreviousPage` 获取上一页时为 `true`。 +- `fetchNextPage: (options?: FetchNextPageOptions) => Promise` + - 此函数允许你获取下一页结果。 + - `options.cancelRefetch: boolean` 如果设置为 `true`,重复调用 `fetchNextPage` 将每次触发 `queryFn`,无论之前的调用是否已解析。同时,之前调用的结果将被忽略。如果设置为 `false`,重复调用 `fetchNextPage` 在第一次调用解析前不会产生任何效果。默认为 `true`。 +- `fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise` + - 此函数允许你获取上一页结果。 + - `options.cancelRefetch: boolean` 与 `fetchNextPage` 相同。 +- `hasNextPage: boolean` + - 如果存在可获取的下一页(通过 `getNextPageParam` 选项判断)则为 `true`。 +- `hasPreviousPage: boolean` + - 如果存在可获取的上一页(通过 `getPreviousPageParam` 选项判断)则为 `true`。 +- `isFetchNextPageError: boolean` + - 如果获取下一页时查询失败则为 `true`。 +- `isFetchPreviousPageError: boolean` + - 如果获取上一页时查询失败则为 `true`。 +- `isRefetching: boolean` + - 当后台重新获取正在进行时为 `true`,**不包括**初始的 `pending` 状态或获取下一页/上一页的操作。 + - 等同于 `isFetching && !isPending && !isFetchingNextPage && !isFetchingPreviousPage`。 +- `isRefetchError: boolean` + - 如果重新获取页面时查询失败则为 `true`。 +- `promise: Promise` + - 一个稳定的 Promise,解析为查询结果。 + - 可与 `React.use()` 配合使用以获取数据。 + - 需要在 `QueryClient` 上启用 `experimental_prefetchInRender` 特性标志。 + +请注意,命令式获取调用(如 `fetchNextPage`)可能会干扰默认的重新获取行为,导致数据过时。确保仅在响应用户操作时调用这些函数,或添加类似 `hasNextPage && !isFetching` 的条件。 diff --git a/docs/zh-hans/framework/react/reference/useIsFetching.md b/docs/zh-hans/framework/react/reference/useIsFetching.md new file mode 100644 index 00000000000..f707659eea3 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useIsFetching.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:39:06.621Z' +id: useIsFetching +title: useIsFetching +--- +`useIsFetching` 是一个可选钩子,返回当前应用在后台加载或获取中的查询 `数量`(适用于全局加载指示器)。 + +```tsx +import { useIsFetching } from '@tanstack/react-query' +// 有多少查询正在获取中? +const isFetching = useIsFetching() +// 匹配 posts 前缀的查询有多少正在获取中? +const isFetchingPosts = useIsFetching({ queryKey: ['posts'] }) +``` + +**选项** + +- `filters?: QueryFilters`: [查询过滤器](../guides/filters.md#query-filters) +- `queryClient?: QueryClient`, + - 使用此选项可指定自定义 QueryClient。否则将使用最近上下文中的 QueryClient。 + +**返回值** + +- `isFetching: number` + - 表示当前应用在后台加载或获取中的查询数量。 diff --git a/docs/zh-hans/framework/react/reference/useIsMutating.md b/docs/zh-hans/framework/react/reference/useIsMutating.md new file mode 100644 index 00000000000..d6a09750f49 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useIsMutating.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:38:54.202Z' +id: useIsMutating +title: useIsMutating +--- +`useIsMutating` 是一个可选钩子,用于返回当前应用中正在获取的变更操作(mutations)数量(适用于全局加载指示器场景)。 + +```tsx +import { useIsMutating } from '@tanstack/react-query' +// 获取当前正在进行的变更操作数量 +const isMutating = useIsMutating() +// 获取匹配 posts 前缀的变更操作数量 +const isMutatingPosts = useIsMutating({ mutationKey: ['posts'] }) +``` + +**选项参数** + +- `filters?: MutationFilters`: [变更过滤器](../guides/filters.md#mutation-filters) +- `queryClient?: QueryClient`, + - 用于指定自定义 QueryClient 实例。若未提供,则使用最近上下文中的实例。 + +**返回值** + +- `isMutating: number` + - 返回当前应用中正在获取的变更操作数量。 diff --git a/docs/zh-hans/framework/react/reference/useMutation.md b/docs/zh-hans/framework/react/reference/useMutation.md new file mode 100644 index 00000000000..9fd581876e3 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useMutation.md @@ -0,0 +1,161 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:38:43.637Z' +id: useMutation +title: useMutation +--- +```tsx +const { + data, + error, + isError, + isIdle, + isPending, + isPaused, + isSuccess, + failureCount, + failureReason, + mutate, + mutateAsync, + reset, + status, + submittedAt, + variables, +} = useMutation( + { + mutationFn, + gcTime, + meta, + mutationKey, + networkMode, + onError, + onMutate, + onSettled, + onSuccess, + retry, + retryDelay, + scope, + throwOnError, + }, + queryClient, +) + +mutate(variables, { + onError, + onSettled, + onSuccess, +}) +``` + +**参数1 (选项)** + +- `mutationFn: (variables: TVariables) => Promise` + - **必填(仅在未定义默认 mutation 函数时)** + - 执行异步任务并返回 Promise 的函数 + - `variables` 是 `mutate` 将传递给 `mutationFn` 的对象 +- `gcTime: number | Infinity` + - 未使用/非活跃的缓存数据在内存中保留的时间(毫秒)。当 mutation 的缓存变为未使用或非活跃状态时,该缓存数据将在此时长后被垃圾回收。若指定了不同的缓存时间,将使用最长的时间。 + - 设置为 `Infinity` 将禁用垃圾回收 + - 注意:最大允许时间约为 24 天。详见[更多](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value)。 +- `mutationKey: unknown[]` + - 可选 + - 可设置 mutation key 以继承通过 `queryClient.setMutationDefaults` 设置的默认值 +- `networkMode: 'online' | 'always' | 'offlineFirst'` + - 可选 + - 默认为 `'online'` + - 详见[网络模式](../guides/network-mode.md) +- `onMutate: (variables: TVariables) => Promise | TContext | void` + - 可选 + - 该函数会在 mutation 函数触发前执行,并接收与 mutation 函数相同的变量 + - 适用于对资源执行乐观更新,期望 mutation 成功 + - 该函数返回的值将在 mutation 失败时传递给 `onError` 和 `onSettled` 函数,可用于回滚乐观更新 +- `onSuccess: (data: TData, variables: TVariables, context: TContext) => Promise | unknown` + - 可选 + - 该函数会在 mutation 成功时触发,并接收 mutation 的结果 + - 若返回 Promise,将在继续前等待其解析 +- `onError: (err: TError, variables: TVariables, context?: TContext) => Promise | unknown` + - 可选 + - 该函数会在 mutation 遇到错误时触发,并接收错误信息 + - 若返回 Promise,将在继续前等待其解析 +- `onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise | unknown` + - 可选 + - 该函数会在 mutation 成功获取或遇到错误时触发,并接收数据或错误信息 + - 若返回 Promise,将在继续前等待其解析 +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - 默认为 `0` + - 若为 `false`,失败的 mutation 不会重试 + - 若为 `true`,失败的 mutation 会无限重试 + - 若设为数字(如 `3`),失败的 mutation 会重试直到失败次数达到该数字 +- `retryDelay: number | (retryAttempt: number, error: TError) => number` + - 该函数接收 `retryAttempt` 整数和实际错误,返回下一次尝试前的延迟时间(毫秒) + - 如 `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` 这样的函数会应用指数退避 + - 如 `attempt => attempt * 1000` 这样的函数会应用线性退避 +- `scope: { id: string }` + - 可选 + - 默认为唯一 ID(因此所有 mutation 并行运行) + - 具有相同 scope ID 的 mutation 会串行执行 +- `throwOnError: undefined | boolean | (error: TError) => boolean` + - 设为 `true` 时,mutation 错误会在渲染阶段抛出并传播到最近的错误边界 + - 设为 `false` 时,禁用向错误边界抛出错误的行为 + - 若设为函数,将接收错误并返回布尔值,指示是否在错误边界显示错误(`true`)或将其作为状态返回(`false`) +- `meta: Record` + - 可选 + - 若设置,会在 mutation 缓存条目上存储额外信息,可在需要时使用。该信息在 `mutation` 可用的任何地方都可访问(如 `MutationCache` 的 `onError`、`onSuccess` 函数) + +**参数2 (QueryClient)** + +- `queryClient?: QueryClient` + - 用于指定自定义的 QueryClient。若未提供,则使用最近上下文中的 QueryClient + +**返回值** + +- `mutate: (variables: TVariables, { onSuccess, onSettled, onError }) => void` + - 可调用的 mutation 函数,传入变量以触发 mutation,并可选择性地挂钩到额外的回调选项 + - `variables: TVariables` + - 可选 + - 传递给 `mutationFn` 的变量对象 + - `onSuccess: (data: TData, variables: TVariables, context: TContext) => void` + - 可选 + - 该函数会在 mutation 成功时触发,并接收 mutation 的结果 + - 无返回值,返回的值将被忽略 + - `onError: (err: TError, variables: TVariables, context: TContext | undefined) => void` + - 可选 + - 该函数会在 mutation 遇到错误时触发,并接收错误信息 + - 无返回值,返回的值将被忽略 + - `onSettled: (data: TData | undefined, error: TError | null, variables: TVariables, context: TContext | undefined) => void` + - 可选 + - 该函数会在 mutation 成功获取或遇到错误时触发,并接收数据或错误信息 + - 无返回值,返回的值将被忽略 + - 若发起多次请求,`onSuccess` 只会在最后一次调用后触发 +- `mutateAsync: (variables: TVariables, { onSuccess, onSettled, onError }) => Promise` + - 类似于 `mutate`,但返回可被 await 的 Promise +- `status: string` + - 可能的值: + - `idle`:mutation 函数执行前的初始状态 + - `pending`:mutation 正在执行 + - `error`:最后一次 mutation 尝试导致错误 + - `success`:最后一次 mutation 尝试成功 +- `isIdle`、`isPending`、`isSuccess`、`isError`:从 `status` 派生的布尔变量 +- `isPaused: boolean` + - 若 mutation 被 `paused`,则为 `true` + - 详见[网络模式](../guides/network-mode.md) +- `data: undefined | unknown` + - 默认为 `undefined` + - mutation 最后一次成功解析的数据 +- `error: null | TError` + - 查询的错误对象(若遇到错误) +- `reset: () => void` + - 清理 mutation 内部状态的函数(即将其重置为初始状态) +- `failureCount: number` + - mutation 的失败计数 + - 每次 mutation 失败时递增 + - mutation 成功时重置为 `0` +- `failureReason: null | TError` + - mutation 重试的失败原因 + - mutation 成功时重置为 `null` +- `submittedAt: number` + - mutation 提交时的时间戳 + - 默认为 `0` +- `variables: undefined | TVariables` + - 传递给 `mutationFn` 的 `variables` 对象 + - 默认为 `undefined` diff --git a/docs/zh-hans/framework/react/reference/useMutationState.md b/docs/zh-hans/framework/react/reference/useMutationState.md new file mode 100644 index 00000000000..e0209016e2b --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useMutationState.md @@ -0,0 +1,82 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:37:20.212Z' +id: useMutationState +title: useMutationState +--- +`useMutationState` 是一个钩子函数,用于访问 `MutationCache` 中的所有变更 (mutation) 状态。你可以通过传递 `filters` 参数来筛选变更,并通过 `select` 参数转换变更状态。 + +**示例 1:获取所有进行中变更的变量** + +```tsx +import { useMutationState } from '@tanstack/react-query' + +const variables = useMutationState({ + filters: { status: 'pending' }, + select: (mutation) => mutation.state.variables, +}) +``` + +**示例 2:通过 `mutationKey` 获取特定变更的所有数据** + +```tsx +import { useMutation, useMutationState } from '@tanstack/react-query' + +const mutationKey = ['posts'] + +// 我们需要获取状态的某个变更 +const mutation = useMutation({ + mutationKey, + mutationFn: (newPost) => { + return axios.post('/posts', newPost) + }, +}) + +const data = useMutationState({ + // 此变更键需与目标变更的键匹配(见上文) + filters: { mutationKey }, + select: (mutation) => mutation.state.data, +}) +``` + +**示例 3:通过 `mutationKey` 访问最新的变更数据** +每次调用 `mutate` 都会在变更缓存中添加一个新条目,持续 `gcTime` 毫秒。 + +要访问最新的调用,可以检查 `useMutationState` 返回的最后一个条目。 + +```tsx +import { useMutation, useMutationState } from '@tanstack/react-query' + +const mutationKey = ['posts'] + +// 我们需要获取状态的某个变更 +const mutation = useMutation({ + mutationKey, + mutationFn: (newPost) => { + return axios.post('/posts', newPost) + }, +}) + +const data = useMutationState({ + // 此变更键需与目标变更的键匹配(见上文) + filters: { mutationKey }, + select: (mutation) => mutation.state.data, +}) + +// 最新的变更数据 +const latest = data[data.length - 1] +``` + +**配置项** + +- `options` + - `filters?: MutationFilters`: [变更过滤器](../guides/filters.md#mutation-filters) + - `select?: (mutation: Mutation) => TResult` + - 用于转换变更状态。 +- `queryClient?: QueryClient`, + - 用于指定自定义的 QueryClient。若不提供,则使用最近上下文中的实例。 + +**返回值** + +- `Array` + - 返回由 `select` 函数处理后的匹配变更结果数组。 diff --git a/docs/zh-hans/framework/react/reference/usePrefetchInfiniteQuery.md b/docs/zh-hans/framework/react/reference/usePrefetchInfiniteQuery.md new file mode 100644 index 00000000000..bc0bc34b131 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/usePrefetchInfiniteQuery.md @@ -0,0 +1,38 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:36:52.114Z' +id: usePrefetchInfiniteQuery +title: usePrefetchInfiniteQuery +--- +```tsx +usePrefetchInfiniteQuery(options) +``` + +**选项** + +你可以向 `usePrefetchInfiniteQuery` 传递所有 [`queryClient.prefetchInfiniteQuery`](../../../reference/QueryClient.md#queryclientprefetchinfinitequery) 支持的参数。请注意以下必填项: + +- `queryKey: QueryKey` + + - **必填** + - 用于在渲染期间预取的查询键 (query key) + +- `queryFn: (context: QueryFunctionContext) => Promise` + + - **必填(但仅在未定义默认查询函数时)** 更多信息请参阅 [默认查询函数](../guides/default-query-function.md) + +- `initialPageParam: TPageParam` + + - **必填** + - 获取第一页时使用的默认页面参数 (page param) + +- `getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null` + + - **必填** + - 当接收到该查询的新数据时,此函数会接收无限数据列表的最后一页、所有页面的完整数组以及页面参数信息 + - 它应返回**单个变量**,该变量将作为最后一个可选参数传递给查询函数 + - 返回 `undefined` 或 `null` 表示没有可用的下一页 + +- **返回值** + +`usePrefetchInfiniteQuery` 不返回任何内容,它仅用于在渲染期间(在包裹了使用 [`useSuspenseInfiniteQuery`](../reference/useSuspenseInfiniteQuery.md) 的组件的 Suspense 边界之前)触发预取 diff --git a/docs/zh-hans/framework/react/reference/usePrefetchQuery.md b/docs/zh-hans/framework/react/reference/usePrefetchQuery.md new file mode 100644 index 00000000000..c35a60c3559 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/usePrefetchQuery.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:36:32.898Z' +id: usePrefetchQuery +title: usePrefetchQuery +--- +```tsx +usePrefetchQuery(options) +``` + +**选项** + +你可以向 `usePrefetchQuery` 传递所有 [`queryClient.prefetchQuery`](../../../reference/QueryClient.md#queryclientprefetchquery) 支持的参数。请注意以下必填项: + +- `queryKey: QueryKey` + - **必填** + - 需要在渲染期间预取的查询键 (query key) + +- `queryFn: (context: QueryFunctionContext) => Promise` + - **必填(但仅在未定义默认查询函数时适用)** 更多信息请参阅 [默认查询函数](../guides/default-query-function.md) + +**返回值** + +`usePrefetchQuery` 不返回任何值,其用途仅是在渲染期间触发预取操作,通常用于包裹 [`useSuspenseQuery`](../reference/useSuspenseQuery.md) 组件的 suspense 边界之前执行。 diff --git a/docs/zh-hans/framework/react/reference/useQueries.md b/docs/zh-hans/framework/react/reference/useQueries.md new file mode 100644 index 00000000000..05f9a38ea43 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useQueries.md @@ -0,0 +1,68 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:36:21.202Z' +id: useQueries +title: useQueries +--- +`useQueries` 钩子可用于获取可变数量的查询: + +```tsx +const ids = [1, 2, 3] +const results = useQueries({ + queries: ids.map((id) => ({ + queryKey: ['post', id], + queryFn: () => fetchPost(id), + staleTime: Infinity, + })), +}) +``` + +**选项** + +`useQueries` 钩子接收一个配置对象,其中 **queries** 键的值是一个数组,数组中的查询配置对象与 [`useQuery` 钩子](../reference/useQuery.md) 完全相同(不包括 `queryClient` 选项 —— 因为 `QueryClient` 可以在顶层传入)。 + +- `queryClient?: QueryClient` + - 用于提供自定义的 QueryClient。否则会使用最近上下文中的实例。 +- `combine?: (result: UseQueriesResults) => TCombinedResult` + - 用于将多个查询结果合并为单个值。 + +> 在查询对象数组中多次使用相同的查询键可能导致查询间共享数据。为避免这种情况,建议对查询进行去重,并将结果映射回所需结构。 + +**placeholderData** + +`useQueries` 同样支持 `placeholderData` 选项,但由于 `useQueries` 的输入可能在每次渲染时具有不同数量的查询,因此它不会像 `useQuery` 那样从先前渲染的查询中获取信息。 + +**返回值** + +`useQueries` 钩子返回一个包含所有查询结果的数组,返回顺序与输入顺序一致。 + +## 合并结果 + +若需要将结果中的 `data`(或其他查询信息)合并为单个值,可以使用 `combine` 选项。合并结果会通过结构共享尽可能保持引用稳定。 + +```tsx +const ids = [1, 2, 3] +const combinedQueries = useQueries({ + queries: ids.map((id) => ({ + queryKey: ['post', id], + queryFn: () => fetchPost(id), + })), + combine: (results) => { + return { + data: results.map((result) => result.data), + pending: results.some((result) => result.isPending), + } + }, +}) +``` + +上例中,`combinedQueries` 将是一个包含 `data` 和 `pending` 属性的对象。注意查询结果的其他属性会被丢弃。 + +### 记忆化 + +`combine` 函数仅在以下情况下重新执行: + +- `combine` 函数本身的引用发生变化 +- 任意查询结果发生变化 + +这意味着如上所示的内联 `combine` 函数会在每次渲染时运行。为避免这种情况,可以用 `useCallback` 包裹 `combine` 函数,或将其提取为无依赖的稳定函数引用。 diff --git a/docs/zh-hans/framework/react/reference/useQuery.md b/docs/zh-hans/framework/react/reference/useQuery.md new file mode 100644 index 00000000000..0c9d55bb563 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useQuery.md @@ -0,0 +1,255 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:35:52.836Z' +id: useQuery +title: useQuery +--- +```tsx +const { + data, + dataUpdatedAt, + error, + errorUpdatedAt, + failureCount, + failureReason, + fetchStatus, + isError, + isFetched, + isFetchedAfterMount, + isFetching, + isInitialLoading, + isLoading, + isLoadingError, + isPaused, + isPending, + isPlaceholderData, + isRefetchError, + isRefetching, + isStale, + isSuccess, + promise, + refetch, + status, +} = useQuery( + { + queryKey, + queryFn, + gcTime, + enabled, + networkMode, + initialData, + initialDataUpdatedAt, + meta, + notifyOnChangeProps, + placeholderData, + queryKeyHashFn, + refetchInterval, + refetchIntervalInBackground, + refetchOnMount, + refetchOnReconnect, + refetchOnWindowFocus, + retry, + retryOnMount, + retryDelay, + select, + staleTime, + structuralSharing, + subscribed, + throwOnError, + }, + queryClient, +) +``` + +**参数1 (配置项)** + +- `queryKey: unknown[]` + - **必填** + - 用于此查询的查询键 (query key)。 + - 查询键会被哈希成一个稳定的哈希值。详见 [查询键](../guides/query-keys.md)。 + - 当此键发生变化时,查询会自动更新(只要 `enabled` 未设为 `false`)。 +- `queryFn: (context: QueryFunctionContext) => Promise` + - **必填,但仅在未定义默认查询函数时** 详见 [默认查询函数](../guides/default-query-function.md)。 + - 查询用于请求数据的函数。 + - 接收一个 [QueryFunctionContext](../guides/query-functions.md#queryfunctioncontext)。 + - 必须返回一个会解析数据或抛出错误的 Promise。数据不能为 `undefined`。 +- `enabled: boolean | (query: Query) => boolean` + - 设为 `false` 可禁用此查询自动运行。 + - 可用于 [依赖查询](../guides/dependent-queries.md)。 +- `networkMode: 'online' | 'always' | 'offlineFirst` + - 可选 + - 默认为 `'online'` + - 详见 [网络模式](../guides/network-mode.md)。 +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - 如果为 `false`,默认情况下失败的查询不会重试。 + - 如果为 `true`,失败的查询会无限重试。 + - 如果设为数字(如 `3`),失败的查询会重试直到失败次数达到该数字。 + - 客户端默认为 `3`,服务端默认为 `0`。 +- `retryOnMount: boolean` + - 如果设为 `false`,当查询包含错误时不会在挂载时重试。默认为 `true`。 +- `retryDelay: number | (retryAttempt: number, error: TError) => number` + - 此函数接收 `retryAttempt` 整数和实际的 Error,并返回下一次尝试前的延迟时间(毫秒)。 + - 像 `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` 这样的函数会应用指数退避。 + - 像 `attempt => attempt * 1000` 这样的函数会应用线性退避。 +- `staleTime: number | ((query: Query) => number)` + - 可选 + - 默认为 `0` + - 数据被视为过时的时间(毫秒)。此值仅适用于定义它的钩子。 + - 如果设为 `Infinity`,数据永远不会被视为过时。 + - 如果设为函数,该函数会接收查询并计算 `staleTime`。 +- `gcTime: number | Infinity` + - 默认为 `5 * 60 * 1000`(5 分钟)或在 SSR 期间为 `Infinity` + - 未使用/非活跃的缓存数据在内存中保留的时间(毫秒)。当查询的缓存变为未使用或非活跃时,该缓存数据会在此时间后被垃圾回收。如果指定了不同的垃圾回收时间,将使用最长的时间。 + - 注意:最大允许时间约为 24 天。详见 [更多](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value)。 + - 如果设为 `Infinity`,会禁用垃圾回收。 +- `queryKeyHashFn: (queryKey: QueryKey) => string` + - 可选 + - 如果指定,此函数用于将 `queryKey` 哈希为字符串。 +- `refetchInterval: number | false | ((query: Query) => number | false | undefined)` + - 可选 + - 如果设为数字,所有查询会按此频率(毫秒)持续重新获取。 + - 如果设为函数,该函数会接收查询并计算频率。 +- `refetchIntervalInBackground: boolean` + - 可选 + - 如果设为 `true`,设置为持续重新获取的查询会在其标签页/窗口处于后台时继续重新获取。 +- `refetchOnMount: boolean | "always" | ((query: Query) => boolean | "always")` + - 可选 + - 默认为 `true` + - 如果设为 `true`,当数据过时会在挂载时重新获取。 + - 如果设为 `false`,不会在挂载时重新获取。 + - 如果设为 `"always"`,会在挂载时始终重新获取。 + - 如果设为函数,该函数会接收查询并计算值。 +- `refetchOnWindowFocus: boolean | "always" | ((query: Query) => boolean | "always")` + - 可选 + - 默认为 `true` + - 如果设为 `true`,当数据过时会在窗口获得焦点时重新获取。 + - 如果设为 `false`,不会在窗口获得焦点时重新获取。 + - 如果设为 `"always"`,会在窗口获得焦点时始终重新获取。 + - 如果设为函数,该函数会接收查询并计算值。 +- `refetchOnReconnect: boolean | "always" | ((query: Query) => boolean | "always")` + - 可选 + - 默认为 `true` + - 如果设为 `true`,当数据过时会在重新连接时重新获取。 + - 如果设为 `false`,不会在重新连接时重新获取。 + - 如果设为 `"always"`,会在重新连接时始终重新获取。 + - 如果设为函数,该函数会接收查询并计算值。 +- `notifyOnChangeProps: string[] | "all" | (() => string[] | "all" | undefined)` + - 可选 + - 如果设置,组件只会在列出的属性变化时重新渲染。 + - 例如设为 `['data', 'error']`,组件只会在 `data` 或 `error` 属性变化时重新渲染。 + - 如果设为 `"all"`,组件会退出智能跟踪,并在查询更新时始终重新渲染。 + - 如果设为函数,该函数会执行以计算属性列表。 + - 默认情况下,会跟踪属性访问,组件只会在跟踪的属性变化时重新渲染。 +- `select: (data: TData) => unknown` + - 可选 + - 此选项可用于转换或选择查询函数返回数据的一部分。它影响返回的 `data` 值,但不影响查询缓存中存储的内容。 + - `select` 函数只会在 `data` 变化或 `select` 函数本身的引用变化时运行。为了优化,可以用 `useCallback` 包裹函数。 +- `initialData: TData | () => TData` + - 可选 + - 如果设置,此值会用作查询缓存的初始数据(只要查询尚未创建或缓存)。 + - 如果设为函数,该函数会在共享/根查询初始化期间调用**一次**,并应同步返回初始数据。 + - 初始数据默认被视为过时,除非设置了 `staleTime`。 + - `initialData` **会持久化**到缓存。 +- `initialDataUpdatedAt: number | (() => number | undefined)` + - 可选 + - 如果设置,此值会用作 `initialData` 本身最后更新的时间(毫秒)。 +- `placeholderData: TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData` + - 可选 + - 如果设置,此值会用作此特定查询观察器的占位数据,当查询仍处于 `pending` 状态时。 + - `placeholderData` **不会持久化**到缓存。 + - 如果为 `placeholderData` 提供函数,第一个参数会接收之前观察的查询数据(如果可用),第二个参数是完整的 previousQuery 实例。 +- `structuralSharing: boolean | (oldData: unknown | undefined, newData: unknown) => unknown)` + - 可选 + - 默认为 `true` + - 如果设为 `false`,会禁用查询结果之间的结构共享。 + - 如果设为函数,旧数据和新数据会通过此函数传递,该函数应将它们组合为查询的解析数据。这样,即使数据包含不可序列化的值,也可以保留旧数据的引用以提高性能。 +- `subscribed: boolean` + - 可选 + - 默认为 `true` + - 如果设为 `false`,此 `useQuery` 实例不会订阅缓存。这意味着它不会自行触发 `queryFn`,也不会在数据通过其他方式进入缓存时接收更新。 +- `throwOnError: undefined | boolean | (error: TError, query: Query) => boolean` + - 设为 `true` 时,错误会在渲染阶段抛出并传播到最近的错误边界。 + - 设为 `false` 可禁用 `suspense` 将错误抛出到错误边界的默认行为。 + - 如果设为函数,会传入错误和查询,并应返回布尔值,指示是否在错误边界中显示错误(`true`)或将错误作为状态返回(`false`)。 +- `meta: Record` + - 可选 + - 如果设置,会在查询缓存条目上存储额外的信息,可根据需要使用。在 `query` 可访问的任何地方都可以访问它,也是提供给 `queryFn` 的 `QueryFunctionContext` 的一部分。 + +**参数2 (QueryClient)** + +- `queryClient?: QueryClient`, + - 使用此参数可自定义 QueryClient。否则会使用最近上下文中的 QueryClient。 + +**返回值** + +- `status: QueryStatus` + - 可能为: + - `pending`:如果无缓存数据且查询尝试尚未完成。 + - `error`:如果查询尝试导致错误。对应的 `error` 属性包含从尝试获取中接收的错误。 + - `success`:如果查询接收到无错误的响应并准备显示其数据。查询的 `data` 属性是从成功获取中接收的数据,或者如果查询的 `enabled` 属性设为 `false` 且尚未获取,`data` 是初始化时提供给查询的第一个 `initialData`。 +- `isPending: boolean` + - 从上述 `status` 变量派生的布尔值,为方便提供。 +- `isSuccess: boolean` + - 从上述 `status` 变量派生的布尔值,为方便提供。 +- `isError: boolean` + - 从上述 `status` 变量派生的布尔值,为方便提供。 +- `isLoadingError: boolean` + - 如果查询在首次获取时失败则为 `true`。 +- `isRefetchError: boolean` + - 如果查询在重新获取时失败则为 `true`。 +- `data: TData` + - 默认为 `undefined`。 + - 查询最后成功解析的数据。 +- `dataUpdatedAt: number` + - 查询最近返回 `status` 为 `"success"` 时的时间戳。 +- `error: null | TError` + - 默认为 `null` + - 查询的错误对象,如果抛出错误。 +- `errorUpdatedAt: number` + - 查询最近返回 `status` 为 `"error"` 时的时间戳。 +- `isStale: boolean` + - 如果缓存中的数据无效或数据比给定的 `staleTime` 旧则为 `true`。 +- `isPlaceholderData: boolean` + - 如果显示的数据是占位数据则为 `true`。 +- `isFetched: boolean` + - 如果查询已获取则为 `true`。 +- `isFetchedAfterMount: boolean` + - 如果查询在组件挂载后已获取则为 `true`。 + - 此属性可用于不显示任何先前缓存的数据。 +- `fetchStatus: FetchStatus` + - `fetching`:当 `queryFn` 正在执行时为 `true`,包括初始 `pending` 和后台重新获取。 + - `paused`:查询想要获取,但已被 `paused`。 + - `idle`:查询未在获取。 + - 详见 [网络模式](../guides/network-mode)。 +- `isFetching: boolean` + - 从上述 `fetchStatus` 变量派生的布尔值,为方便提供。 +- `isPaused: boolean` + - 从上述 `fetchStatus` 变量派生的布尔值,为方便提供。 +- `isRefetching: boolean` + - 当后台重新获取进行中时为 `true`,**不包括**初始 `pending`。 + - 等同于 `isFetching && !isPending`。 +- `isLoading: boolean` + - 当查询首次获取进行中时为 `true`。 + - 等同于 `isFetching && isPending`。 +- `isInitialLoading: boolean` + - **已弃用** + - `isLoading` 的别名,将在下一个主版本中移除。 +- `failureCount: number` + - 查询的失败次数。 + - 每次查询失败时递增。 + - 查询成功时重置为 `0`。 +- `failureReason: null | TError` + - 查询重试的失败原因。 + - 查询成功时重置为 `null`。 +- `errorUpdateCount: number` + - 所有错误的总和。 +- `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise` + - 手动重新获取查询的函数。 + - 如果查询出错,错误只会被记录。如果想抛出错误,传递 `throwOnError: true` 选项。 + - `cancelRefetch?: boolean` + - 默认为 `true` + - 默认情况下,新请求发出前会取消当前正在运行的请求。 + - 设为 `false` 时,如果已有请求运行则不会重新获取。 +- `promise: Promise` + - 一个稳定的 Promise,会解析为查询的数据。 + - 需要在 `QueryClient` 上启用 `experimental_prefetchInRender` 特性标志。 diff --git a/docs/zh-hans/framework/react/reference/useQueryClient.md b/docs/zh-hans/framework/react/reference/useQueryClient.md new file mode 100644 index 00000000000..7dcecc6fb68 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useQueryClient.md @@ -0,0 +1,18 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:33:32.399Z' +id: useQueryClient +title: useQueryClient +--- +`useQueryClient` 钩子返回当前的 `QueryClient` 实例。 + +```tsx +import { useQueryClient } from '@tanstack/react-query' + +const queryClient = useQueryClient(queryClient?: QueryClient) +``` + +**选项** + +- `queryClient?: QueryClient`, + - 用于指定自定义的 QueryClient。若未提供,则使用最近上下文中的 QueryClient。 diff --git a/docs/zh-hans/framework/react/reference/useQueryErrorResetBoundary.md b/docs/zh-hans/framework/react/reference/useQueryErrorResetBoundary.md new file mode 100644 index 00000000000..f2e6958f28c --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useQueryErrorResetBoundary.md @@ -0,0 +1,29 @@ +--- +source-updated-at: '2024-04-11T09:16:39.000Z' +translation-updated-at: '2025-05-06T04:33:21.777Z' +id: useQueryErrorResetBoundary +title: useQueryErrorResetBoundary +--- +该钩子函数会重置最近一层 `QueryErrorResetBoundary` 内的所有查询错误。如果未定义边界,则会全局重置错误: + +```tsx +import { useQueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => { + const { reset } = useQueryErrorResetBoundary() + return ( + ( +
    + 发生错误! + +
    + )} + > + +
    + ) +} +``` diff --git a/docs/zh-hans/framework/react/reference/useSuspenseInfiniteQuery.md b/docs/zh-hans/framework/react/reference/useSuspenseInfiniteQuery.md new file mode 100644 index 00000000000..6aa586cd8a0 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useSuspenseInfiniteQuery.md @@ -0,0 +1,31 @@ +--- +source-updated-at: '2025-03-31T09:10:17.000Z' +translation-updated-at: '2025-05-06T04:33:11.338Z' +id: useSuspenseInfiniteQuery +title: useSuspenseInfiniteQuery +--- +```tsx +const result = useSuspenseInfiniteQuery(options) +``` + +**配置项** + +与 [useInfiniteQuery](../reference/useInfiniteQuery.md) 相同,但以下选项除外: + +- `suspense` +- `throwOnError` +- `enabled` +- `placeholderData` + +**返回值** + +返回对象与 [useInfiniteQuery](../reference/useInfiniteQuery.md) 相同,但存在以下差异: + +- `data` 保证已定义 +- 不存在 `isPlaceholderData` 属性 +- `status` 始终为 `success` + - 派生的状态标志会相应设置 + +**注意事项** + +[取消查询](../guides/query-cancellation.md) 功能不可用。 diff --git a/docs/zh-hans/framework/react/reference/useSuspenseQueries.md b/docs/zh-hans/framework/react/reference/useSuspenseQueries.md new file mode 100644 index 00000000000..b152874249a --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useSuspenseQueries.md @@ -0,0 +1,31 @@ +--- +source-updated-at: '2025-03-31T09:10:17.000Z' +translation-updated-at: '2025-05-06T04:27:49.567Z' +id: useSuspenseQueries +title: useSuspenseQueries +--- +```tsx +const result = useSuspenseQueries(options) +``` + +**参数选项** + +与 [useQueries](../reference/useQueries.md) 相同,但每个 `query` 不能包含以下属性: +- `suspense` +- `throwOnError` +- `enabled` +- `placeholderData` + +**返回值** + +返回结构与 [useQueries](../reference/useQueries.md) 相同,但针对每个 `query` 有以下区别: +- `data` 保证已定义 +- 不包含 `isPlaceholderData` 属性 +- `status` 始终为 `success` + - 派生的状态标志也会相应设置 + +**注意事项** + +请注意组件只会在**所有查询**完成加载后重新挂载。因此,如果在所有查询完成期间某个查询已过期,重新挂载时会再次发起请求。为避免此问题,请确保设置足够长的 `staleTime`。 + +[取消查询](../guides/query-cancellation.md) 功能不可用。 diff --git a/docs/zh-hans/framework/react/reference/useSuspenseQuery.md b/docs/zh-hans/framework/react/reference/useSuspenseQuery.md new file mode 100644 index 00000000000..d326b1d8da3 --- /dev/null +++ b/docs/zh-hans/framework/react/reference/useSuspenseQuery.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2025-03-31T09:10:17.000Z' +translation-updated-at: '2025-05-06T04:33:00.266Z' +id: useSuspenseQuery +title: useSuspenseQuery +--- +```tsx +const result = useSuspenseQuery(options) +``` + +**参数选项** + +与 [useQuery](../reference/useQuery.md) 相同,但以下参数除外: + +- `throwOnError` +- `enabled` +- `placeholderData` + +**返回值** + +返回对象与 [useQuery](../reference/useQuery.md) 相同,但存在以下差异: + +- `data` 保证已定义(非 undefined) +- 不包含 `isPlaceholderData` 字段 +- `status` 始终为 `success` 状态 + - 所有衍生标志位会相应设置 + +**注意事项** + +[查询取消 (Cancellation)](../guides/query-cancellation.md) 功能不可用 diff --git a/docs/zh-hans/framework/react/typescript.md b/docs/zh-hans/framework/react/typescript.md new file mode 100644 index 00000000000..81fc2179f6d --- /dev/null +++ b/docs/zh-hans/framework/react/typescript.md @@ -0,0 +1,251 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-06T04:29:57.696Z' +id: typescript +title: TypeScript +--- +React Query 现已采用 **TypeScript** 编写,确保库与您的项目具备类型安全! + +注意事项: + +- 当前类型系统要求使用 TypeScript **v4.7** 或更高版本 +- 本仓库中的类型变更被视为**非破坏性变更**,通常以 **patch** 版本号发布(否则每个类型增强都会导致主版本号变更!) +- **强烈建议您将 react-query 包版本锁定到特定 patch 版本,并在升级时做好类型可能在任何版本间被修复或升级的准备** +- React Query 中与类型无关的公共 API 仍严格遵循语义化版本规范。 + +## 类型推断 + +React Query 中的类型通常能很好地流动,因此您通常无需自行添加类型注解 + +[//]: # 'TypeInference1' + +```tsx +const { data } = useQuery({ + // ^? const data: number | undefined + queryKey: ['test'], + queryFn: () => Promise.resolve(5), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFC8MQAdqnhIAJnRh0icALwoM2XHgAUAbSqDkIAEa4qAXQA0cFQEo5APjgAFciGAYAdLVQQANgDd0KgKxmzXgB6ILgw8IA9AH5eIA) + +[//]: # 'TypeInference1' +[//]: # 'TypeInference2' + +```tsx +const { data } = useQuery({ + // ^? const data: string | undefined + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFC8MQAdqnhIAJnRh0icALwoM2XHgAUAbSox0IqgF0ANHBUBKOQD44ABXIhgGAHS1UEADYA3dCoCsxw0gwu6EwAXHASUuZhknT2MBAAyjBQwIIA5iaExrwA9Nlw+QUAegD8vEA) + +[//]: # 'TypeInference2' + +当您的 `queryFn` 具有明确定义的返回类型时,类型推断效果最佳。请注意大多数数据获取库默认返回 `any` 类型,因此请确保将其提取到具有正确类型的函数中: + +[//]: # 'TypeInference3' + +```tsx +const fetchGroups = (): Promise => + axios.get('/groups').then((response) => response.data) + +const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const data: Group[] | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFCiSw4dAB7AIqUuUpURY1Nx68YeMOjgBxcsjBwAvIjjAAJgC44AO2QgARriK9eDCOdTwS6GAwAWmiNon6ABQAlGYAClLAGAA8vtoA2gC6AHx6qbLiAHQA5h6BVAD02Vpg8sGZMF7o5oG0qJAuarqpdQ0YmUZ0MHTBDjxOLvBInd1EeigY2Lh4gfFUxX6lVIkANKQe3nGlvTwFBXAHhwB6APxwA65wI3RmW0lwAD4o5kboJMDm6Ea8QA) + +[//]: # 'TypeInference3' + +## 类型收窄 + +React Query 使用 [可辨识联合类型 (discriminated union type)](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) 作为查询结果,通过 `status` 字段和派生的状态布尔标志进行区分。这使您能够检查例如 `success` 状态来确保 `data` 已定义: + +[//]: # 'TypeNarrowing' + +```tsx +const { data, isSuccess } = useQuery({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), +}) + +if (isSuccess) { + data + // ^? const data: number +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFC8MQAdqnhIAJnRh0ANHGCoAysgYN0qVETgBeFBmy48ACgDaVGGphUAurMMBKbQD44ABXIh56AHS1UEADYAbuiGAKx2dry8wCRwhvJKKmqoDgi8cBlwElK8APS5GQB6APy8hLxAA) + +[//]: # 'TypeNarrowing' + +## 错误字段类型标注 + +错误类型默认为 `Error`,因为这符合大多数用户的预期。 + +[//]: # 'TypingError' + +```tsx +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: Error +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFZVLWR9VQ5ADSkwWGZ9WOSnJxwl1cAegD8QA) + +[//]: # 'TypingError' + +如果您想抛出自定义错误,或非 `Error` 类型的对象,可以指定错误字段的类型: + +[//]: # 'TypingError2' + +```tsx +const { error } = useQuery(['groups'], fetchGroups) +// ^? const error: string | null +``` + +[//]: # 'TypingError2' + +但这样做的缺点是 `useQuery` 的其他泛型参数将无法进行类型推断。通常不建议抛出非 `Error` 类型的对象,因此如果存在子类如 `AxiosError`,可以使用**类型收窄**使错误字段更具体: + +[//]: # 'TypingError3' + +```tsx +import axios from 'axios' + +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: Error | null + +if (axios.isAxiosError(error)) { + error + // ^? const error: AxiosError +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA) + +[//]: # 'TypingError3' + +### 注册全局错误类型 + +TanStack Query v5 允许通过扩展 `Register` 接口来设置全局错误类型,无需在调用处指定泛型参数。这将确保类型推断仍然有效,同时错误字段会是指定的类型: + +[//]: # 'RegisterErrorType' + +```tsx +import '@tanstack/react-query' + +declare module '@tanstack/react-query' { + interface Register { + defaultError: AxiosError + } +} + +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: AxiosError | null +``` + +[//]: # 'RegisterErrorType' +[//]: # 'TypingMeta' + +## 元数据 (Meta) 类型标注 + +### 注册全局元数据类型 + +与注册 [全局错误类型](#registering-a-global-error) 类似,您也可以注册全局 `Meta` 类型。这确保 [查询](./reference/useQuery.md) 和 [变更](./reference/useMutation.md) 中的可选 `meta` 字段保持一致且类型安全。注意注册的类型必须扩展 `Record`,以保证 `meta` 始终是对象类型。 + +```ts +import '@tanstack/react-query' + +interface MyMeta extends Record { + // 您的元数据类型定义 +} + +declare module '@tanstack/react-query' { + interface Register { + queryMeta: MyMeta + mutationMeta: MyMeta + } +} +``` + +[//]: # 'TypingMeta' +[//]: # 'TypingQueryAndMutationKeys' + +## 查询与变更键 (Key) 类型标注 + +### 注册查询与变更键类型 + +同样类似于注册 [全局错误类型](#registering-a-global-error),您还可以注册全局 `QueryKey` 和 `MutationKey` 类型。这允许您为键提供更多结构,匹配应用程序的层次关系,并在库的所有相关接口中保持类型安全。注意注册的类型必须扩展 `Array` 类型,以确保键仍然是数组形式。 + +```ts +import '@tanstack/react-query' + +type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray] + +declare module '@tanstack/react-query' { + interface Register { + queryKey: QueryKey + mutationKey: QueryKey + } +} +``` + +[//]: # 'TypingQueryAndMutationKeys' +[//]: # 'TypingQueryOptions' + +## 查询选项 (Query Options) 类型标注 + +如果将查询选项内联到 `useQuery` 中,您将获得自动类型推断。但若需要将查询选项提取到单独函数中以便在 `useQuery` 和 `prefetchQuery` 等场景共享,则会失去类型推断。此时可以使用 `queryOptions` 辅助函数恢复类型推断: + +```ts +import { queryOptions } from '@tanstack/react-query' + +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +useQuery(groupOptions()) +queryClient.prefetchQuery(groupOptions()) +``` + +此外,`queryOptions` 返回的 `queryKey` 知晓关联的 `queryFn`,我们可以利用此类型信息使 `queryClient.getQueryData` 等函数也能感知这些类型: + +```ts +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +const data = queryClient.getQueryData(groupOptions().queryKey) +// ^? const data: Group[] | undefined +``` + +若不使用 `queryOptions`,`data` 的类型将为 `unknown`,除非显式传递泛型参数: + +```ts +const data = queryClient.getQueryData(['groups']) +``` + +[//]: # 'TypingQueryOptions' +[//]: # 'Materials' + +## 扩展阅读 + +关于类型推断的技巧,请参阅社区资源中的 [React Query 与 TypeScript](./community/tkdodos-blog.md#6-react-query-and-typescript)。要了解如何实现最佳类型安全,可阅读 [类型安全的 React Query](./community/tkdodos-blog.md#19-type-safe-react-query)。 + +[//]: # 'Materials' + +## 使用 `skipToken` 实现类型安全的查询禁用 + +如果使用 TypeScript,可以通过 `skipToken` 禁用查询。这在需要根据条件禁用查询但仍希望保持类型安全时非常有用。更多信息请参阅 [禁用查询](./guides/disabling-queries.md) 指南。 diff --git a/docs/zh-hans/framework/react/videos.md b/docs/zh-hans/framework/react/videos.md new file mode 100644 index 00000000000..7d61ae47b8d --- /dev/null +++ b/docs/zh-hans/framework/react/videos.md @@ -0,0 +1,72 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T04:26:01.170Z' +id: videos +title: 视频与演讲 +--- + + +[点击此处查看上述演示使用的代码仓库](https://github.com/tannerlinsley/react-query-blog-refactor-example) + + + + + + + + diff --git a/docs/zh-hans/framework/solid/community/community-projects.md b/docs/zh-hans/framework/solid/community/community-projects.md new file mode 100644 index 00000000000..79c72c27605 --- /dev/null +++ b/docs/zh-hans/framework/solid/community/community-projects.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.671Z' +id: community-projects +title: Community Projects +ref: docs/zh-hans/framework/react/community/community-projects.md +replace: + React Query: TanStack Query +--- + diff --git a/docs/zh-hans/framework/solid/community/tkdodos-blog.md b/docs/zh-hans/framework/solid/community/tkdodos-blog.md new file mode 100644 index 00000000000..27015eb8f47 --- /dev/null +++ b/docs/zh-hans/framework/solid/community/tkdodos-blog.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.635Z' +id: tkdodos-blog +title: TkDodo's Blog +ref: docs/zh-hans/framework/react/community/tkdodos-blog.md +--- + diff --git a/docs/zh-hans/framework/solid/devtools.md b/docs/zh-hans/framework/solid/devtools.md new file mode 100644 index 00000000000..89b4a7c9728 --- /dev/null +++ b/docs/zh-hans/framework/solid/devtools.md @@ -0,0 +1,82 @@ +--- +source-updated-at: '2024-08-21T11:18:15.000Z' +translation-updated-at: '2025-05-06T05:15:27.103Z' +id: devtools +title: 开发者工具 +--- +欢呼雀跃吧,因为 Solid Query 配备了专属的开发工具 (Devtools)!🥳 + +当你开始使用 Solid Query 时,这些开发工具将成为得力助手。它们能直观展示 Solid Query 的内部运作机制,在你遇到棘手问题时,很可能为你节省数小时的调试时间! + +## 安装并导入开发工具 + +开发工具是一个独立包,需单独安装: + +```bash +npm i @tanstack/solid-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/solid-query-devtools +``` + +或 + +```bash +yarn add @tanstack/solid-query-devtools +``` + +或 + +```bash +bun add @tanstack/solid-query-devtools +``` + +导入方式如下: + +```tsx +import { SolidQueryDevtools } from '@tanstack/solid-query-devtools' +``` + +默认情况下,Solid Query 开发工具仅在 `isServer === true` 时包含在构建包中([`isServer`](https://github.com/solidjs/solid/blob/a72d393a07b22f9b7496e5eb93712188ccce0d28/packages/solid/web/src/index.ts#L37) 来自 `solid-js/web` 包),因此无需担心生产构建时需要手动排除它们。 + +## 浮动模式 + +浮动模式会将开发工具作为固定悬浮元素挂载到应用中,并在屏幕角落提供显示/隐藏的切换按钮。该状态会保存在 localStorage 中,即使刷新页面也会被记住。 + +请将以下代码尽可能放置在 Solid 应用的顶层。越靠近页面根节点,效果越好! + +```tsx +import { SolidQueryDevtools } from '@tanstack/solid-query-devtools' + +function App() { + return ( + + {/* 应用的其他部分 */} + + + ) +} +``` + +### 配置项 + +- `initialIsOpen: Boolean` + - 设为 `true` 可使开发工具默认展开 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` + - 默认为 `bottom-right` + - 控制 Solid Query 徽标按钮的位置,用于展开/收起开发工具面板 +- `position?: "top" | "bottom" | "left" | "right"` + - 默认为 `bottom` + - 控制开发工具面板的停靠位置 +- `client?: QueryClient` + - 传入自定义 QueryClient。若不设置,则使用最近上下文中的实例 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 用于预定义可触发的查询错误类型。当从 UI 切换该错误时,初始化函数会接收具体查询并返回一个 Error 对象 +- `styleNonce?: string` + - 传递 nonce 给添加到 document head 的 style 标签,适用于使用内容安全策略 (CSP) nonce 允许内联样式的场景 +- `shadowDOMTarget?: ShadowRoot` + - 默认行为会将开发工具样式应用到 DOM 的 head 标签 + - 传入 shadow DOM 目标节点可使样式作用于 shadow DOM 内部,而非 light DOM 的 head 标签 diff --git a/docs/zh-hans/framework/solid/guides/advanced-ssr.md b/docs/zh-hans/framework/solid/guides/advanced-ssr.md new file mode 100644 index 00000000000..1eaa068bb42 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/advanced-ssr.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:19:20.669Z' +id: advanced-ssr +title: 高级 SSR +--- +待更新 diff --git a/docs/zh-hans/framework/solid/guides/background-fetching-indicators.md b/docs/zh-hans/framework/solid/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..b154f07d192 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/background-fetching-indicators.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-03T22:43:30.028Z' +id: background-fetching-indicators +title: Background Fetching Indicators +ref: docs/zh-hans/framework/react/guides/background-fetching-indicators.md +replace: + 'useMutation[(]': 'useMutation(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/caching.md b/docs/zh-hans/framework/solid/guides/caching.md new file mode 100644 index 00000000000..b22612d46a6 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/caching.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.601Z' +id: caching +title: Caching Examples +ref: docs/zh-hans/framework/react/guides/caching.md +--- + diff --git a/docs/zh-hans/framework/solid/guides/default-query-function.md b/docs/zh-hans/framework/solid/guides/default-query-function.md new file mode 100644 index 00000000000..2c3b2eae01f --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/default-query-function.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.564Z' +id: default-query-function +title: Default Query Function +ref: docs/zh-hans/framework/react/guides/default-query-function.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/dependent-queries.md b/docs/zh-hans/framework/solid/guides/dependent-queries.md new file mode 100644 index 00000000000..dd93fb38ab6 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/dependent-queries.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.533Z' +id: dependent-queries +title: Dependent Queries +ref: docs/zh-hans/framework/react/guides/dependent-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/disabling-queries.md b/docs/zh-hans/framework/solid/guides/disabling-queries.md new file mode 100644 index 00000000000..a5cc3abc0b0 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/disabling-queries.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.457Z' +id: disabling-queries +title: Disabling/Pausing Queries +ref: docs/zh-hans/framework/react/guides/disabling-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/solid/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..6eb188c674a --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/does-this-replace-client-state.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.499Z' +id: does-this-replace-client-state +title: Does TanStack Query replace global state managers? +ref: docs/zh-hans/framework/react/guides/does-this-replace-client-state.md +replace: + hook: function +--- + diff --git a/docs/zh-hans/framework/solid/guides/filters.md b/docs/zh-hans/framework/solid/guides/filters.md new file mode 100644 index 00000000000..ba2a424f190 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/filters.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.413Z' +id: filters +title: Filters +ref: docs/zh-hans/framework/react/guides/filters.md +--- + diff --git a/docs/zh-hans/framework/solid/guides/important-defaults.md b/docs/zh-hans/framework/solid/guides/important-defaults.md new file mode 100644 index 00000000000..719ad6e7201 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/important-defaults.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.380Z' +id: important-defaults +title: Important Defaults +ref: docs/zh-hans/framework/react/guides/important-defaults.md +--- + diff --git a/docs/zh-hans/framework/solid/guides/infinite-queries.md b/docs/zh-hans/framework/solid/guides/infinite-queries.md new file mode 100644 index 00000000000..1342dc30d5e --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/infinite-queries.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.344Z' +id: infinite-queries +title: Infinite Queries +ref: docs/zh-hans/framework/react/guides/infinite-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/initial-query-data.md b/docs/zh-hans/framework/solid/guides/initial-query-data.md new file mode 100644 index 00000000000..68c52daed08 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/initial-query-data.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.311Z' +id: initial-query-data +title: Initial Query Data +ref: docs/zh-hans/framework/react/guides/initial-query-data.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/solid/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..db2e2cf8961 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/invalidations-from-mutations.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.271Z' +id: invalidations-from-mutations +title: Invalidations from Mutations +ref: docs/zh-hans/framework/react/guides/invalidations-from-mutations.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/mutations.md b/docs/zh-hans/framework/solid/guides/mutations.md new file mode 100644 index 00000000000..711ea5bc4c5 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/mutations.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.239Z' +id: mutations +title: Mutations +ref: docs/zh-hans/framework/react/guides/mutations.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/network-mode.md b/docs/zh-hans/framework/solid/guides/network-mode.md new file mode 100644 index 00000000000..f3ec9fe6e32 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/network-mode.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.201Z' +id: network-mode +title: Network Mode +ref: docs/zh-hans/framework/react/guides/network-mode.md +--- + diff --git a/docs/zh-hans/framework/solid/guides/optimistic-updates.md b/docs/zh-hans/framework/solid/guides/optimistic-updates.md new file mode 100644 index 00000000000..675bb12f998 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/optimistic-updates.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.168Z' +id: optimistic-updates +title: Optimistic Updates +ref: docs/zh-hans/framework/react/guides/optimistic-updates.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/paginated-queries.md b/docs/zh-hans/framework/solid/guides/paginated-queries.md new file mode 100644 index 00000000000..d26dc443b71 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/paginated-queries.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.119Z' +id: paginated-queries +title: Paginated / Lagged Queries +ref: docs/zh-hans/framework/react/guides/paginated-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/parallel-queries.md b/docs/zh-hans/framework/solid/guides/parallel-queries.md new file mode 100644 index 00000000000..071814d2d95 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/parallel-queries.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.082Z' +id: parallel-queries +title: Parallel Queries +ref: docs/zh-hans/framework/react/guides/parallel-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/placeholder-query-data.md b/docs/zh-hans/framework/solid/guides/placeholder-query-data.md new file mode 100644 index 00000000000..2cdcce7f4fd --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/placeholder-query-data.md @@ -0,0 +1,17 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.054Z' +id: placeholder-query-data +title: Placeholder Query Data +ref: docs/zh-hans/framework/react/guides/placeholder-query-data.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' + useMemo: createMemo +--- + diff --git a/docs/zh-hans/framework/solid/guides/prefetching.md b/docs/zh-hans/framework/solid/guides/prefetching.md new file mode 100644 index 00000000000..8b024e49815 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/prefetching.md @@ -0,0 +1,17 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:51.012Z' +id: prefetching +title: Prefetching +ref: docs/zh-hans/framework/react/guides/prefetching.md +replace: + React.lazy: Solid.lazy + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' + /docs/framework/react/examples/basic-react-query-file-based: /docs/framework/solid/examples/basic-solid-query-file-based +--- + diff --git a/docs/zh-hans/framework/solid/guides/queries.md b/docs/zh-hans/framework/solid/guides/queries.md new file mode 100644 index 00000000000..9554f5a66d5 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/queries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.975Z' +id: queries +title: Queries +ref: docs/zh-hans/framework/react/guides/queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/query-cancellation.md b/docs/zh-hans/framework/solid/guides/query-cancellation.md new file mode 100644 index 00000000000..99039aeef11 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/query-cancellation.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.901Z' +id: query-cancellation +title: Query Cancellation +ref: docs/zh-hans/framework/react/guides/query-cancellation.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + +[//]: # 'Example7' + +```ts +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, +}) + +const queryClient = useQueryClient() + +function onButtonClick() { + queryClient.cancelQueries({ queryKey: ['todos'] }) +} +``` + +[//]: # 'Example7' diff --git a/docs/zh-hans/framework/solid/guides/query-functions.md b/docs/zh-hans/framework/solid/guides/query-functions.md new file mode 100644 index 00000000000..5c8d6cecebf --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/query-functions.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.871Z' +id: query-functions +title: Query Functions +ref: docs/zh-hans/framework/react/guides/query-functions.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/query-invalidation.md b/docs/zh-hans/framework/solid/guides/query-invalidation.md new file mode 100644 index 00000000000..6cca6bc3df1 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/query-invalidation.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.932Z' +id: query-invalidation +title: Query Invalidation +ref: docs/zh-hans/framework/react/guides/query-invalidation.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/query-keys.md b/docs/zh-hans/framework/solid/guides/query-keys.md new file mode 100644 index 00000000000..a0201c880f2 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/query-keys.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.836Z' +id: query-keys +title: Query Keys +ref: docs/zh-hans/framework/react/guides/query-keys.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/query-options.md b/docs/zh-hans/framework/solid/guides/query-options.md new file mode 100644 index 00000000000..e6978edfc11 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/query-options.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.787Z' +id: query-options +title: Query Options +ref: docs/zh-hans/framework/react/guides/query-options.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/query-retries.md b/docs/zh-hans/framework/solid/guides/query-retries.md new file mode 100644 index 00000000000..61885c3b4ce --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/query-retries.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.724Z' +id: query-retries +title: Query Retries +ref: docs/zh-hans/framework/react/guides/query-retries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/request-waterfalls.md b/docs/zh-hans/framework/solid/guides/request-waterfalls.md new file mode 100644 index 00000000000..ec331f4e71a --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/request-waterfalls.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.755Z' +id: request-waterfalls +title: Request Waterfalls +ref: docs/zh-hans/framework/react/guides/request-waterfalls.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/scroll-restoration.md b/docs/zh-hans/framework/solid/guides/scroll-restoration.md new file mode 100644 index 00000000000..7c2d9281d8f --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/scroll-restoration.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.683Z' +id: scroll-restoration +title: Scroll Restoration +ref: docs/zh-hans/framework/react/guides/scroll-restoration.md +--- + diff --git a/docs/zh-hans/framework/solid/guides/ssr.md b/docs/zh-hans/framework/solid/guides/ssr.md new file mode 100644 index 00000000000..ff467cd2bcc --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/ssr.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:17:13.047Z' +id: ssr +title: SSR +--- +即将推出 diff --git a/docs/zh-hans/framework/solid/guides/suspense.md b/docs/zh-hans/framework/solid/guides/suspense.md new file mode 100644 index 00000000000..1bb746ffba8 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/suspense.md @@ -0,0 +1,43 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:17:08.121Z' +id: suspense +title: Suspense +--- +Solid Query 也能与 Solid 的 [Suspense](https://docs.solidjs.com/reference/components/suspense) API 配合使用。 + +为此,你需要用 Vue 提供的 `Suspense` 组件包裹可挂起的组件: + +```tsx +import { Suspense } from 'solid-js' +;}> + + +``` + +你可以使用 `solid-query` 提供的异步 `suspense` 函数: + +```vue + +``` + +## 渲染时获取 (Fetch-on-render) vs 随取随渲染 (Render-as-you-fetch) + +开箱即用的 Solid Query 在 `suspense` 模式下无需额外配置即可作为**渲染时获取 (Fetch-on-render)**方案出色工作。这意味着当你的组件尝试挂载时,它们会触发查询获取并挂起,但只有在你导入并挂载它们之后才会发生。如果你想更上一层楼,实现**随取随渲染 (Render-as-you-fetch)**模式,我们建议在路由回调和/或用户交互事件上实现[预取 (Prefetching)](../prefetching),以便在组件挂载前就开始加载查询,甚至可以在导入或挂载其父组件之前就开始。 diff --git a/docs/zh-hans/framework/solid/guides/testing.md b/docs/zh-hans/framework/solid/guides/testing.md new file mode 100644 index 00000000000..108d55ace4d --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/testing.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.621Z' +id: testing +title: Testing +--- + diff --git a/docs/zh-hans/framework/solid/guides/updates-from-mutation-responses.md b/docs/zh-hans/framework/solid/guides/updates-from-mutation-responses.md new file mode 100644 index 00000000000..7550e8d7412 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/updates-from-mutation-responses.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.653Z' +id: updates-from-mutation-responses +title: Updates from Mutation Responses +ref: docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/guides/window-focus-refetching.md b/docs/zh-hans/framework/solid/guides/window-focus-refetching.md new file mode 100644 index 00000000000..25220db0503 --- /dev/null +++ b/docs/zh-hans/framework/solid/guides/window-focus-refetching.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.593Z' +id: window-focus-refetching +title: Window Focus Refetching +ref: docs/zh-hans/framework/react/guides/window-focus-refetching.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/installation.md b/docs/zh-hans/framework/solid/installation.md new file mode 100644 index 00000000000..0b56bd9755b --- /dev/null +++ b/docs/zh-hans/framework/solid/installation.md @@ -0,0 +1,60 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-06T05:16:47.879Z' +id: installation +title: 安装 +--- +# 安装 + +你可以通过 [NPM](https://npmjs.com/) 安装 Solid Query,或者通过 [ESM.sh](https://esm.sh/) 使用传统的 ` +``` + +### 环境要求 + +Solid Query 针对现代浏览器进行了优化,兼容以下浏览器配置: + +``` +Chrome >= 91 +Firefox >= 90 +Edge >= 91 +Safari >= 15 +iOS >= 15 +Opera >= 77 +``` + +> 根据你的运行环境,可能需要添加 polyfill。如果需要支持旧版浏览器,你需要自行从 `node_modules` 转译该库。 diff --git a/docs/zh-hans/framework/solid/overview.md b/docs/zh-hans/framework/solid/overview.md new file mode 100644 index 00000000000..e1caa743054 --- /dev/null +++ b/docs/zh-hans/framework/solid/overview.md @@ -0,0 +1,139 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:16:29.033Z' +id: overview +title: 概述 +--- +Solid Query 是 TanStack Query 的官方 SolidJS 适配器,它能让你在 Web 应用中轻松实现**数据获取、缓存、同步和更新服务端状态**。 + +## 动机 + +SolidJS 作为一个快速、响应式且声明式的用户界面构建库,正日益受到欢迎。它开箱即用地提供了许多功能。诸如 `createSignal`、`createStore` 等基础功能非常适合管理客户端状态。与其他 UI 库不同,SolidJS 对异步数据管理有着独到的见解。`createResource` API 是处理 SolidJS 应用中服务端状态的强大基础功能。`resource` 是一种特殊的信号 (signal),可在数据加载状态时触发 `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 ( +
    +
    Static Content
    + {/* An error while fetching will be caught by the ErrorBoundary */} + Something went wrong!
    }> + {/* Suspense will trigger a loading state while the data is being fetched */} + Loading...}> +
    {repository()?.updated_at}
    +
    + + + ) +} + +const root = document.getElementById('root') + +render(() => , root!) +``` + +这非常棒!只需几行代码,你就能从 API 获取数据并处理加载和错误状态。但随着应用复杂度的增长,你将需要更多功能来有效管理服务端状态。这是因为**服务端状态与客户端状态完全不同**。首先,服务端状态: + +- 持久化存储在你无法控制或拥有的远程位置 +- 需要通过异步 API 进行获取和更新 +- 意味着共享所有权,可能被他人更改而无需你知晓 +- 如果不加注意,可能会在你的应用中变得“过时” + +一旦理解了应用中服务端状态的本质,**更多挑战将接踵而至**,例如: + +- 缓存...(可能是编程中最难实现的部分) +- 将针对相同数据的多个请求去重为单个请求 +- 在后台更新“过时”数据 +- 判断数据何时“过时” +- 尽可能快地反映数据更新 +- 分页和懒加载数据等性能优化 +- 管理服务端状态的内存和垃圾回收 +- 通过结构共享 (structural sharing) 记忆化查询结果 + +此时 **Solid Query** 应运而生。该库封装了 `createResource`,并提供了一系列钩子和工具来有效管理服务端状态。它**开箱即用、零配置**,并可根据应用增长按需定制。 + +从技术角度来看,Solid Query 能: + +- 帮助你移除应用中**大量**复杂且难以理解的代码,仅用少量 Solid Query 逻辑替代 +- 提升应用可维护性,轻松构建新功能而无需担心接入新的服务端状态数据源 +- 通过使应用感觉更快、响应更迅速,直接影响终端用户体验 +- 可能帮助你节省带宽并提升内存性能 + +## 说够了,直接看代码吧! + +在下面的示例中,你可以看到 Solid Query 最基本、简单的用法,用于获取 TanStack Query 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 ( +
    +
    Static Content
    + {/* An error while fetching will be caught by the ErrorBoundary */} + Something went wrong!
    }> + {/* Suspense will trigger a loading state while the data is being fetched */} + Loading...}> + {/* + The `data` property on a query is a SolidJS resource + so it will work with Suspense and transitions out of the box! + */} +
    {repositoryQuery.data?.updated_at}
    +
    + + + ) +} + +const root = document.getElementById('root') +const client = new QueryClient() + +render( + () => ( + + + + ), + root!, +) +``` + +## 看起来代码量反而更多了? + +确实如此!但正是这几行代码开启了一个全新的可能性世界。在上面的示例中,你的查询会被缓存 5 分钟,这意味着如果应用中任何地方在 5 分钟内挂载了使用相同查询的新组件,它将不会重新获取数据,而是使用缓存数据。这只是 Solid Query 开箱即用提供的众多功能之一。其他功能包括: + +- **自动重新获取**:当查询变得“过时”(根据 `staleTime` 选项判断)时,会自动在后台重新获取 +- **自动缓存**:查询默认被缓存并在整个应用中共享 +- **请求去重**:多个组件可以共享同一查询并只发起一次请求 +- **自动垃圾回收**:不再需要的查询会被自动垃圾回收 +- **窗口焦点重新获取**:当应用重新获得焦点时,查询会自动重新获取 +- **分页**:内置分页支持 +- **请求取消**:自动取消过时或不必要的请求 +- **轮询/实时更新**:通过简单的 `refetchInterval` 选项即可轻松实现轮询或实时更新 +- **服务端渲染 (SSR) 支持**:Solid Query 与服务端渲染完美配合 +- **乐观更新**:轻松通过乐观更新缓存 +- **更多功能...** diff --git a/docs/zh-hans/framework/solid/plugins/broadcastQueryClient.md b/docs/zh-hans/framework/solid/plugins/broadcastQueryClient.md new file mode 100644 index 00000000000..381a420c422 --- /dev/null +++ b/docs/zh-hans/framework/solid/plugins/broadcastQueryClient.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.562Z' +id: broadcastQueryClient +title: broadcastQueryClient (Experimental) +ref: docs/zh-hans/framework/react/plugins/broadcastQueryClient.md +replace: + react-query: vue-query +--- + diff --git a/docs/zh-hans/framework/solid/plugins/createPersister.md b/docs/zh-hans/framework/solid/plugins/createPersister.md new file mode 100644 index 00000000000..b4b54d08bfd --- /dev/null +++ b/docs/zh-hans/framework/solid/plugins/createPersister.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.529Z' +id: createPersister +title: experimental_createPersister +ref: docs/zh-hans/framework/react/plugins/createPersister.md +replace: + react-query: solid-query +--- + diff --git a/docs/zh-hans/framework/solid/quick-start.md b/docs/zh-hans/framework/solid/quick-start.md new file mode 100644 index 00000000000..3160ebb6ab3 --- /dev/null +++ b/docs/zh-hans/framework/solid/quick-start.md @@ -0,0 +1,230 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-03T22:08:58.056Z' +id: quick-start +title: 快速开始 +--- +`@tanstack/solid-query` 包为在 SolidJS 中使用 TanStack Query 提供了一流的 API。 + +## 示例 + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/solid-query' +import { Switch, Match, For } from 'solid-js' + +const queryClient = new QueryClient() + +function Example() { + const query = useQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) + + return ( +
    + + +

    Loading...

    +
    + +

    Error: {query.error.message}

    +
    + + {(todo) =>

    {todo.title}

    }
    +
    +
    +
    + ) +} + +function App() { + return ( + + + + ) +} +``` + +## 可用函数 + +Solid Query 提供了一系列实用的基础功能和函数,可以更轻松地管理 SolidJS 应用中的服务端状态 (server state)。 + +- `useQuery` +- `createQueries` +- `createInfiniteQueries` +- `createMutation` +- `useIsFetching` +- `useIsMutating` +- `useQueryClient` +- `QueryClient` +- `QueryClientProvider` + +## Solid Query 与 React Query 的重要区别 + +Solid Query 提供的 API 与 React Query 类似,但需要注意一些关键差异。 + +- 上述 `solid-query` 基础功能(如 `useQuery`、`createMutation`、`useIsFetching`)的参数是函数,以便在响应式作用域 (reactive scope) 中进行追踪。 + +```tsx +// ❌ React 版本 +useQuery({ + queryKey: ['todos', todo], + queryFn: fetchTodos, +}) + +// ✅ Solid 版本 +useQuery(() => ({ + queryKey: ['todos', todo], + queryFn: fetchTodos, +})) +``` + +- 如果在 `` 边界内访问查询数据,Suspense 会默认生效。 + +```tsx +import { For, Suspense } from 'solid-js' + +function Example() { + const query = useQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) + return ( +
    + {/* ✅ 会触发加载状态,数据在 Suspense 边界内访问。 */} + + {(todo) =>
    {todo.title}
    }
    +
    + {/* ❌ 不会触发加载状态,数据未在 Suspense 边界内访问。 */} + {(todo) =>
    {todo.title}
    }
    +
    + ) +} +``` + +- Solid Query 的基础功能(`createX`)不支持解构。这些函数的返回值是一个存储 (store),其属性仅在响应式上下文中被追踪。 + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/solid-query' +import { Match, Switch } from 'solid-js' + +const queryClient = new QueryClient() + +export default function App() { + return ( + + + + ) +} + +function Example() { + // ❌ React 版本 —— 支持在响应式上下文外解构 + // const { isPending, error, data } = useQuery({ + // queryKey: ['repoData'], + // queryFn: () => + // fetch('https://api.github.com/repos/tannerlinsley/react-query').then( + // (res) => res.json() + // ), + // }) + + // ✅ Solid 版本 —— 不支持在响应式上下文外解构 + const query = useQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + fetch('https://api.github.com/repos/tannerlinsley/react-query').then( + (res) => res.json(), + ), + })) + + // ✅ 在 JSX 响应式上下文中访问查询属性 + return ( + + Loading... + Error: {query.error.message} + +
    +

    {query.data.name}

    +

    {query.data.description}

    + 👀 {query.data.subscribers_count}{' '} + ✨ {query.data.stargazers_count}{' '} + 🍴 {query.data.forks_count} +
    +
    +
    + ) +} +``` + +- 可以直接将信号 (Signal) 和存储值 (store value) 传递给函数参数。Solid Query 会自动更新查询存储 (store)。 + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/solid-query' +import { createSignal, For } from 'solid-js' + +const queryClient = new QueryClient() + +function Example() { + const [enabled, setEnabled] = createSignal(false) + const [todo, setTodo] = createSignal(0) + + // ✅ 直接传递信号是安全的,当信号值变化时观察者会自动更新 + const todosQuery = useQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + enabled: enabled(), + })) + + const todoDetailsQuery = useQuery(() => ({ + queryKey: ['todo', todo()], + queryFn: fetchTodo, + enabled: todo() > 0, + })) + + return ( +
    + + +

    Loading...

    +
    + +

    Error: {todosQuery.error.message}

    +
    + + + {(todo) => ( + + )} + + +
    + +
    + ) +} + +function App() { + return ( + + + + ) +} +``` + +- 可以使用 SolidJS 原生的 `ErrorBoundary` 组件捕获和重置错误。将 `throwOnError` 或 `suspense` 选项设置为 `true` 以确保错误被抛出到 `ErrorBoundary`。 + +- 由于属性追踪是通过 Solid 的细粒度响应式系统处理的,因此不需要 `notifyOnChangeProps` 等选项。 diff --git a/docs/zh-hans/framework/solid/reference/hydration.md b/docs/zh-hans/framework/solid/reference/hydration.md new file mode 100644 index 00000000000..3ebbcdb45e2 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/hydration.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.469Z' +id: hydration +title: hydration +ref: docs/zh-hans/framework/react/reference/hydration.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' +--- + +[//]: # 'HydrationBoundary' +[//]: # 'HydrationBoundary' diff --git a/docs/zh-hans/framework/solid/reference/infiniteQueryOptions.md b/docs/zh-hans/framework/solid/reference/infiniteQueryOptions.md new file mode 100644 index 00000000000..d3dfb16e318 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/infiniteQueryOptions.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.499Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +ref: docs/zh-hans/framework/react/reference/infiniteQueryOptions.md +--- + diff --git a/docs/zh-hans/framework/solid/reference/queryOptions.md b/docs/zh-hans/framework/solid/reference/queryOptions.md new file mode 100644 index 00000000000..1518cb8e229 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/queryOptions.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.436Z' +id: queryOptions +title: queryOptions +ref: docs/zh-hans/framework/react/reference/queryOptions.md +--- + diff --git a/docs/zh-hans/framework/solid/reference/useInfiniteQuery.md b/docs/zh-hans/framework/solid/reference/useInfiniteQuery.md new file mode 100644 index 00000000000..280242985a2 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useInfiniteQuery.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.406Z' +id: useInfiniteQuery +title: useInfiniteQuery +ref: docs/zh-hans/framework/react/reference/useInfiniteQuery.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' + 'useMutation[(]': 'useMutation(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/reference/useIsFetching.md b/docs/zh-hans/framework/solid/reference/useIsFetching.md new file mode 100644 index 00000000000..f13edfc0b17 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useIsFetching.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.261Z' +id: useIsFetching +title: useIsFetching +ref: docs/zh-hans/framework/react/reference/useIsFetching.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' +--- + diff --git a/docs/zh-hans/framework/solid/reference/useIsMutating.md b/docs/zh-hans/framework/solid/reference/useIsMutating.md new file mode 100644 index 00000000000..e01040f5c67 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useIsMutating.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.224Z' +id: useIsMutating +title: useIsMutating +ref: docs/zh-hans/framework/react/reference/useIsMutating.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' +--- + diff --git a/docs/zh-hans/framework/solid/reference/useMutation.md b/docs/zh-hans/framework/solid/reference/useMutation.md new file mode 100644 index 00000000000..b264c7f0590 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useMutation.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.299Z' +id: useMutation +title: useMutation +ref: docs/zh-hans/framework/react/reference/useMutation.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/reference/useMutationState.md b/docs/zh-hans/framework/solid/reference/useMutationState.md new file mode 100644 index 00000000000..7f134df40b7 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useMutationState.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.340Z' +id: useMutationState +title: useMutationState +ref: docs/zh-hans/framework/react/reference/useMutationState.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/reference/useQueries.md b/docs/zh-hans/framework/solid/reference/useQueries.md new file mode 100644 index 00000000000..514731aab50 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useQueries.md @@ -0,0 +1,11 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.154Z' +id: useQueries +title: useQueries +ref: docs/zh-hans/framework/react/reference/useQueries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useQueries[(]': 'useQueries(() => ' +--- + diff --git a/docs/zh-hans/framework/solid/reference/useQuery.md b/docs/zh-hans/framework/solid/reference/useQuery.md new file mode 100644 index 00000000000..71fd914aa02 --- /dev/null +++ b/docs/zh-hans/framework/solid/reference/useQuery.md @@ -0,0 +1,376 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:14:50.192Z' +id: useQuery +title: useQuery +--- + +```tsx +const { + data, + dataUpdatedAt, + error, + errorUpdatedAt, + failureCount, + failureReason, + fetchStatus, + isError, + isFetched, + isFetchedAfterMount, + isFetching, + isInitialLoading, + isLoading, + isLoadingError, + isPaused, + isPending, + isPlaceholderData, + isRefetchError, + isRefetching, + isStale, + isSuccess, + refetch, + status, +} = useQuery( + () => ({ + queryKey, + queryFn, + enabled, + select, + placeholderData, + deferStream, + reconcile, + gcTime, + networkMode, + initialData, + initialDataUpdatedAt, + meta, + queryKeyHashFn, + refetchInterval, + refetchIntervalInBackground, + refetchOnMount, + refetchOnReconnect, + refetchOnWindowFocus, + retry, + retryOnMount, + retryDelay, + staleTime, + throwOnError, + }), + () => queryClient, +) +``` + +## Usage example + +Here are some examples of how to use the `useQuery` primitive in Solid Query. + +### Basic + +The most basic usage of `useQuery` is to create a query that fetches data from an API. + +```tsx +import { useQuery } from '@tanstack/solid-query' + +function App() { + const todos = useQuery(() => ({ + queryKey: 'todos', + queryFn: async () => { + const response = await fetch('/api/todos') + if (!response.ok) { + throw new Error('Failed to fetch todos') + } + return response.json() + }, + })) + + return ( +
    + +
    Error: {todos.error.message}
    +
    + +
    Loading...
    +
    + +
    +
    Todos:
    +
      + {(todo) =>
    • {todo.title}
    • }
      +
    +
    +
    +
    + ) +} +``` + +### Reactive Options + +The reason why `useQuery` accepts a function that returns an object is to allow for reactive options. This is useful when query options depend on other values/signals that might change over time. Solid Query can track the passed function in a reactive scope and re-run it whenever the dependencies change. + +```tsx +import { useQuery } from '@tanstack/solid-query' + +function App() { + const [filter, setFilter] = createSignal('all') + + const todos = useQuery(() => ({ + queryKey: ['todos', filter()], + queryFn: async () => { + const response = await fetch(`/api/todos?filter=${filter()}`) + if (!response.ok) { + throw new Error('Failed to fetch todos') + } + return response.json() + }, + })) + + return ( +
    +
    + + + +
    + +
    Error: {todos.error.message}
    +
    + +
    Loading...
    +
    + +
    +
    Todos:
    +
      + {(todo) =>
    • {todo.title}
    • }
      +
    +
    +
    +
    + ) +} +``` + +### Usage with `Suspense` + +`useQuery` supports triggering SolidJS `Suspense` and `ErrorBoundary` components when the query is in a pending or error state. This allows you to easily handle loading and error states in your components. + +```tsx +import { useQuery } from '@tanstack/solid-query' + +function App() { + const todos = useQuery(() => ({ + queryKey: 'todos', + queryFn: async () => { + const response = await fetch('/api/todos') + if (!response.ok) { + throw new Error('Failed to fetch todos') + } + return response.json() + }, + throwOnError: true, + })) + + return ( + Error: {todos.error.message}}> + Loading...}> +
    +
    Todos:
    +
      + {(todo) =>
    • {todo.title}
    • }
      +
    +
    +
    +
    + ) +} +``` + +## `useQuery` Parameters + +- ### Query Options - `Accessor` + + - ##### `queryKey: unknown[]` + - **Required** + - The query key to use for this query. + - The query key will be hashed into a stable hash. See [Query Keys](../../guides/query-keys) for more information. + - The query will automatically update when this key changes (as long as `enabled` is not set to `false`). + - ##### `queryFn: (context: QueryFunctionContext) => Promise` + - **Required, but only if no default query function has been defined** See [Default Query Function](../../guides/default-query-function) for more information. + - The function that the query will use to request data. + - Receives a [QueryFunctionContext](../../guides/query-functions#queryfunctioncontext) + - Must return a promise that will either resolve data or throw an error. The data cannot be `undefined`. + - ##### `enabled: boolean` + - Set this to `false` to disable this query from automatically running. + - Can be used for [Dependent Queries](../../guides/dependent-queries). + - ##### `select: (data: TData) => unknown` + - Optional + - This option can be used to transform or select a part of the data returned by the query function. It affects the returned `data` value, but does not affect what gets stored in the query cache. + - The `select` function will only run if `data` changed, or if the reference to the `select` function itself changes. To optimize, wrap the function in `useCallback`. + - ##### `placeholderData: TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData` + - Optional + - If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `pending` state. + - `placeholderData` is **not persisted** to the cache + - If you provide a function for `placeholderData`, as a first argument you will receive previously watched query data if available, and the second argument will be the complete previousQuery instance. + - ##### `deferStream: boolean` + - Optional + - Defaults to `false` + - Only applicable while rendering queries on the server with streaming. + - Set `deferStream` to `true` to wait for the query to resolve on the server before flushing the stream. + - This can be useful to avoid sending a loading state to the client before the query has resolved. + - ##### `reconcile: false | string | ((oldData: TData | undefined, newData: TData) => TData)` + - Optional + - Defaults to `false` + - Set this to a string to enable reconciliation between query results based on the string key. + - Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom reconciliation logic. + - ##### `gcTime: number | Infinity` + - Defaults to `5 * 60 * 1000` (5 minutes) or `Infinity` during SSR + - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different garbage collection times are specified, the longest one will be used. + - Note: the maximum allowed time is about 24 days. See [more](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value). + - If set to `Infinity`, will disable garbage collection + - ##### `networkMode: 'online' | 'always' | 'offlineFirst` + - optional + - defaults to `'online'` + - see [Network Mode](../../guides/network-mode) for more information. + - ##### `initialData: TData | () => TData` + - Optional + - If set, this value will be used as the initial data for the query cache (as long as the query hasn't been created or cached yet) + - If set to a function, the function will be called **once** during the shared/root query initialization, and be expected to synchronously return the initialData + - Initial data is considered stale by default unless a `staleTime` has been set. + - `initialData` **is persisted** to the cache + - ##### `initialDataUpdatedAt: number | (() => number | undefined)` + - Optional + - If set, this value will be used as the time (in milliseconds) of when the `initialData` itself was last updated. + - ##### `meta: Record` + - Optional + - If set, stores additional information on the query cache entry that can be used as needed. It will be accessible wherever the `query` is available, and is also part of the `QueryFunctionContext` provided to the `queryFn`. + - ##### `queryKeyHashFn: (queryKey: QueryKey) => string` + - Optional + - If specified, this function is used to hash the `queryKey` to a string. + - ##### `refetchInterval: number | false | ((query: Query) => number | false | undefined)` + - Optional + - If set to a number, all queries will continuously refetch at this frequency in milliseconds + - If set to a function, the function will be executed with the query to compute a frequency + - ##### `refetchIntervalInBackground: boolean` + - Optional + - If set to `true`, queries that are set to continuously refetch with a `refetchInterval` will continue to refetch while their tab/window is in the background + - ##### `refetchOnMount: boolean | "always" | ((query: Query) => boolean | "always")` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on mount if the data is stale. + - If set to `false`, the query will not refetch on mount. + - If set to `"always"`, the query will always refetch on mount. + - If set to a function, the function will be executed with the query to compute the value + - ##### `refetchOnWindowFocus: boolean | "always" | ((query: Query) => boolean | "always")` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on window focus if the data is stale. + - If set to `false`, the query will not refetch on window focus. + - If set to `"always"`, the query will always refetch on window focus. + - If set to a function, the function will be executed with the query to compute the value + - ##### `refetchOnReconnect: boolean | "always" | ((query: Query) => boolean | "always")` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on reconnect if the data is stale. + - If set to `false`, the query will not refetch on reconnect. + - If set to `"always"`, the query will always refetch on reconnect. + - If set to a function, the function will be executed with the query to compute the value + - ##### `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - If `false`, failed queries will not retry by default. + - If `true`, failed queries will retry infinitely. + - If set to a `number`, e.g. `3`, failed queries will retry until the failed query count meets that number. + - defaults to `3` on the client and `0` on the server + - ##### `retryOnMount: boolean` + - If set to `false`, the query will not be retried on mount if it contains an error. Defaults to `true`. + - ##### `retryDelay: number | (retryAttempt: number, error: TError) => number` + - This function receives a `retryAttempt` integer and the actual Error and returns the delay to apply before the next attempt in milliseconds. + - A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential backoff. + - A function like `attempt => attempt * 1000` applies linear backoff. + - ##### `staleTime: number | Infinity` + - Optional + - Defaults to `0` + - The time in milliseconds after data is considered stale. This value only applies to the hook it is defined on. + - If set to `Infinity`, the data will never be considered stale + - ##### `throwOnError: undefined | boolean | (error: TError, query: Query) => boolean` + - Set this to `true` if you want errors to be thrown in the render phase and propagate to the nearest error boundary + - Set this to `false` to disable `suspense`'s default behavior of throwing errors to the error boundary. + - If set to a function, it will be passed the error and the query, and it should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`) + +- ### Query Client - `Accessor` + - Optional + - Use this to use a custom QueryClient. Otherwise, the one from the nearest context will be used. + +## `useQuery` Return Value - `Store>` + +`useQuery` returns a SolidJS store with the following properties: + +- ##### `status: QueryStatus` + - Will be: + - `pending` if there's no cached data and no query attempt was finished yet. + - `error` if the query attempt resulted in an error. The corresponding `error` property has the error received from the attempted fetch + - `success` if the query has received a response with no errors and is ready to display its data. The corresponding `data` property on the query is the data received from the successful fetch or if the query's `enabled` property is set to `false` and has not been fetched yet `data` is the first `initialData` supplied to the query on initialization. +- ##### `isPending: boolean` + - A derived boolean from the `status` variable above, provided for convenience. +- ##### `isSuccess: boolean` + - A derived boolean from the `status` variable above, provided for convenience. +- ##### `isError: boolean` + - A derived boolean from the `status` variable above, provided for convenience. +- ##### `isLoadingError: boolean` + - Will be `true` if the query failed while fetching for the first time. +- ##### `isRefetchError: boolean` + - Will be `true` if the query failed while refetching. +- ##### `data: Resource` + - Defaults to `undefined`. + - The last successfully resolved data for the query. + - **Important**: The `data` property is a SolidJS resource. This means that if the data is accessed underneath a `` component, + it will trigger the Suspense boundary if the data is not available yet. +- ##### `dataUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` as `"success"`. +- ##### `error: null | TError` + - Defaults to `null` + - The error object for the query, if an error was thrown. +- ##### `errorUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` as `"error"`. +- ##### `isStale: boolean` + - Will be `true` if the data in the cache is invalidated or if the data is older than the given `staleTime`. +- ##### `isPlaceholderData: boolean` + - Will be `true` if the data shown is the placeholder data. +- ##### `isFetched: boolean` + - Will be `true` if the query has been fetched. +- ##### `isFetchedAfterMount: boolean` + - Will be `true` if the query has been fetched after the component mounted. + - This property can be used to not show any previously cached data. +- ##### `fetchStatus: FetchStatus` + - `fetching`: Is `true` whenever the queryFn is executing, which includes initial `pending` as well as background refetches. + - `paused`: The query wanted to fetch, but has been `paused`. + - `idle`: The query is not fetching. + - see [Network Mode](../../guides/network-mode) for more information. +- ##### `isFetching: boolean` + - A derived boolean from the `fetchStatus` variable above, provided for convenience. +- ##### `isPaused: boolean` + - A derived boolean from the `fetchStatus` variable above, provided for convenience. +- ##### `isRefetching: boolean` + - Is `true` whenever a background refetch is in-flight, which _does not_ include initial `pending` + - Is the same as `isFetching && !isPending` +- ##### `isLoading: boolean` + - Is `true` whenever the first fetch for a query is in-flight + - Is the same as `isFetching && isPending` +- ##### `isInitialLoading: boolean` + - **deprecated** + - An alias for `isLoading`, will be removed in the next major version. +- ##### `failureCount: number` + - The failure count for the query. + - Incremented every time the query fails. + - Reset to `0` when the query succeeds. +- ##### `failureReason: null | TError` + - The failure reason for the query retry. + - Reset to `null` when the query succeeds. +- ##### `errorUpdateCount: number` + - The sum of all errors. +- ##### `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise` + - A function to manually refetch the query. + - If the query errors, the error will only be logged. If you want an error to be thrown, pass the `throwOnError: true` option + - `cancelRefetch?: boolean` + - Defaults to `true` + - Per default, a currently running request will be cancelled before a new request is made + - When set to `false`, no refetch will be made if there is already a request running. diff --git a/docs/zh-hans/framework/solid/typescript.md b/docs/zh-hans/framework/solid/typescript.md new file mode 100644 index 00000000000..4eaaeaea7d8 --- /dev/null +++ b/docs/zh-hans/framework/solid/typescript.md @@ -0,0 +1,218 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-06T05:19:16.449Z' +id: typescript +title: TypeScript +--- +Solid Query 采用 **TypeScript** 编写,以确保库和您的项目具备类型安全! + +注意事项: + +- 当前类型系统要求使用 TypeScript **v4.7** 或更高版本 +- 本仓库中的类型变更被视为**非破坏性变更**,通常以 **patch** 版本号发布(否则每个类型增强都会导致主版本号变更!) +- **强烈建议您将 solid-query 包版本锁定到特定 patch 版本,并在升级时预见到类型可能在任意版本间被修复或升级** +- Solid Query 中与类型无关的公共 API 仍严格遵循语义化版本规范。 + +## 类型推断 + +Solid Query 中的类型通常能很好地流动,因此您无需自行添加类型注解 + +```tsx +import { useQuery } from '@tanstack/solid-query' + +const query = useQuery(() => ({ + queryKey: ['number'], + queryFn: () => Promise.resolve(5), +})) + +query.data +// ^? (property) data: number | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUPOQR28SYRIBeFOiy4pRABQGAlHA0A+OAYTy4duGuIBpNEQBccANp0WeEACNCOgBdABo4W3tHIgAxFg8TM0sABWoQYDY0ADp0fgEANzQDAFZjeVJjMoU5aKzhLAx5Hh57OAA9AH55brkgA) + +```tsx +import { useQuery } from '@tanstack/solid-query' + +const query = useQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), +})) + +query.data +// ^? (property) data: string | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUPOQR28SYRIBeFOiy4pRABQGAlHA0A+OAYTy4duGuIBpNEQBccANp1sHOgF0AGjhbe0ciADEWDxMzSwAFahBgNjQAOnR+AQA3NAMAVmNA0LtUgTRkGBjhLAxTCzga5jSYCABlGChgFgBzE2K5UmNjeXlwtKaMeR4eezgAPQB+UYU5IA) + +当您的 `queryFn` 具有明确定义的返回类型时效果最佳。请注意大多数数据获取库默认返回 `any`,因此请确保将其提取到具有正确类型的函数中: + +```tsx +const fetchGroups = (): Promise => + axios.get('/groups').then((response) => response.data) + +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.data +// ^? (property) data: Group[] | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play/?ssl=11&ssc=4&pln=6&pc=1#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEiw4GAB7AIbStVp01GtrLnyYRMGjgBxanjBwAvIjgiAXHBZ4QAI0Jl585Ah2eAo0GGQAC2sIWy1HAAoASjcABR1gNjQAHmjbAG0AXQA+BxL9TQA6AHMw+LoeKpswQ0SKmAi0Fnj0Nkh2C3sSnr7MiuEsDET-OUDguElCEkdUTGx8Rfik0rh4hHk4A-mpIgBpNCI3PLpGmOa6AoAaOH3DheIAMRY3UPCoprYHvJSIkpsY5G8iGMJvIeDxDnAAHoAfmm8iAA) + +## 类型收窄 + +Solid Query 使用[可辨识联合类型](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)作为查询结果,通过 `status` 字段和派生的状态布尔标志进行区分。这使您能够检查例如 `success` 状态来确保 `data` 已定义: + +```tsx +const query = useQuery(() => ({ + queryKey: ['number'], + queryFn: () => Promise.resolve(5), +})) + +if (query.isSuccess) { + const data = query.data + // ^? const data: number +} +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEixEKdFjQBRChTTJ45KjXr8hYgFZtZc+cgjt4kwiQC8qzNnxOAFF4CUcZwA+OC8EeTg4R2IAaTQiAC44AG06FjwQACNCOgBdABpwyKkiADEWRL8A4IAFahBgNjQAOnQTADc0LwBWXwK5Ul9feXlgChCooiaGgGU8ZGQ0NjZ-MLkIiNt7OGEsDACipyad5kKInh51iIA9AH55UmHrOSA) + +## 错误字段类型标注 + +错误类型默认为 `Error`,因为这符合大多数用户的预期。 + +```tsx +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: Error | null +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEiw4GAB7AIbStVp01GtrLnyYRMGjgBxanjBwAvIjgiAXHBZ4QAI0Jl585Ah2eAo0GGQAC2sIWy1HAAoASjcABR1gNjQAHmjbAG0AXQA+BxL9TQA6AHMw+LoeKpswQ0SKmAi0Fnj0Nkh2C3sSnr7MiuEsDET-OUDguElCEkdUTGx8Rfik0rh4hHk4A-mpIgBpNCI3PLpGmOa6AoAaOH3DheIAMRY3UPCoprYHvJSIkpsY5G8iBVCNQoPIeDxDnAAHoAfmm8iAA) + +如果您想抛出自定义错误,或根本不是 `Error` 的内容,可以指定错误字段的类型: + +```tsx +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: string | null +``` + +但这样做有个缺点:`useQuery` 所有其他泛型的类型推断将不再工作。通常认为抛出非 `Error` 的内容不是良好实践,因此如果您有像 `AxiosError` 这样的子类,可以使用*类型收窄*来使错误字段更具体: + +```tsx +import axios from 'axios' + +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: Error | null + +if (axios.isAxiosError(query.error)) { + query.error + // ^? (property) error: AxiosError +} +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEiw4GAB7AIbStVp01GtrLnyYRMGjgBxanjBwAvIjgiAXHBZ4QAI0Jl585Ah2eAo0GGQAC2sIWy1HAAoASjcABR1gNjQAHmjbAG0AXQA+BxL9TQA6AHMw+LoeKpswQ0SKmAi0Fnj0Nkh2C3sSnr7MiuEsDET-OUDguElCEkdUTGx8Rfik0rh4hHk4A-mpIgBpNCI3PLpGmOa6AoAaOH3DheIAMRY3UPCoprYHvJSIkpsY5G8iBVCNQoPIeDxDnAAHoAfmmwAoO3KbAqGQAgupNABRKAw+IQqGk6AgxAvA4U6HQOlweGI1FA+RAA) + +## 注册全局 `Error` 类型 + +TanStack Query v5 提供了一种方式来设置全局错误类型,无需在调用处指定泛型,通过扩展 `Register` 接口实现。这将确保类型推断仍然有效,但错误字段将是指定的类型: + +```tsx +import '@tanstack/solid-query' + +declare module '@tanstack/solid-query' { + interface Register { + defaultError: AxiosError + } +} + +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: AxiosError | null +``` + +## 注册全局 `Meta` 类型 + +与注册[全局错误类型](#registering-a-global-error)类似,您也可以注册全局 `Meta` 类型。这确保[查询](../useQuery)和[变更](../createMutation)上的可选 `meta` 字段保持一致且类型安全。注意注册的类型必须扩展 `Record`,以便 `meta` 保持为对象。 + +```ts +import '@tanstack/solid-query' + +interface MyMeta extends Record { + // 您的 meta 类型定义 +} + +declare module '@tanstack/solid-query' { + interface Register { + queryMeta: MyMeta + mutationMeta: MyMeta + } +} +``` + +## 查询选项类型标注 + +如果将查询选项内联到 `useQuery` 中,您将获得自动类型推断。但您可能希望将查询选项提取到单独的函数中,以便在 `useQuery` 和例如 `prefetchQuery` 之间共享。这种情况下,您将失去类型推断。要恢复它,可以使用 `queryOptions` 辅助函数: + +```ts +import { queryOptions } from '@tanstack/solid-query' + +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +useQuery(groupOptions) +queryClient.prefetchQuery(groupOptions()) +``` + +此外,`queryOptions` 返回的 `queryKey` 知道与之关联的 `queryFn`,我们可以利用这些类型信息使像 `queryClient.getQueryData` 这样的函数也能感知这些类型: + +```ts +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +const data = queryClient.getQueryData(groupOptions().queryKey) +// ^? const data: Group[] | undefined +``` + +如果没有 `queryOptions`,`data` 的类型将是 `unknown`,除非我们传递泛型: + +```ts +const data = queryClient.getQueryData(['groups']) +``` + +## 使用 `skipToken` 类型安全地禁用查询 + +如果使用 TypeScript,可以使用 `skipToken` 来禁用查询。这在您想基于条件禁用查询但仍希望保持查询类型安全时非常有用。 + +更多信息请参阅[禁用查询](../disabling-queries)指南。 diff --git a/docs/zh-hans/framework/svelte/devtools.md b/docs/zh-hans/framework/svelte/devtools.md new file mode 100644 index 00000000000..cb66d5a0df9 --- /dev/null +++ b/docs/zh-hans/framework/svelte/devtools.md @@ -0,0 +1,76 @@ +--- +source-updated-at: '2025-03-07T10:54:04.000Z' +translation-updated-at: '2025-05-06T05:23:43.299Z' +id: devtools +title: 开发者工具 +--- +## 安装并导入开发者工具 (Devtools) + +开发者工具是一个独立的包,需要单独安装: + +```bash +npm i @tanstack/svelte-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/svelte-query-devtools +``` + +或 + +```bash +yarn add @tanstack/svelte-query-devtools +``` + +或 + +```bash +bun add @tanstack/svelte-query-devtools +``` + +可以通过以下方式导入开发者工具: + +```ts +import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools' +``` + +## 浮动模式 (Floating Mode) + +浮动模式会将开发者工具作为一个固定的浮动元素挂载到你的应用中,并在屏幕角落提供一个切换按钮来显示或隐藏开发者工具。这个切换状态会被存储在 localStorage 中,并在页面刷新后保持记忆。 + +将以下代码尽可能放在 Svelte 应用的顶层。越靠近页面根节点,效果越好! + +```ts + + + + {/* 应用的其他部分 */} + + +``` + +### 配置选项 + +- `initialIsOpen: Boolean` + - 设置为 `true` 可以让开发者工具默认处于打开状态 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` + - 默认为 `bottom-right` + - TanStack 徽标按钮的位置,用于打开和关闭开发者工具面板 + - 如果设为 `relative`,按钮将渲染在你放置开发者工具的位置 +- `position?: "top" | "bottom" | "left" | "right"` + - 默认为 `bottom` + - Svelte Query 开发者工具面板的位置 +- `client?: QueryClient`, + - 用于指定自定义的 QueryClient。如果不设置,将使用最近上下文中的 QueryClient +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 用于预定义一些可以在查询中触发的错误类型。当从 UI 切换该错误时,初始化器(带有特定查询)将被调用。它必须返回一个 Error 对象 +- `styleNonce?: string` + - 用于向添加到文档头部的 style 标签传递一个 nonce。这在需要使用内容安全策略 (CSP) nonce 来允许内联样式时很有用 +- `shadowDOMTarget?: ShadowRoot` + - 默认行为会将开发者工具的样式应用到 DOM 中的 head 标签 + - 用于向开发者工具传递一个 shadow DOM 目标,这样样式将被应用到 shadow DOM 中,而不是 light DOM 的 head 标签里 diff --git a/docs/zh-hans/framework/svelte/installation.md b/docs/zh-hans/framework/svelte/installation.md new file mode 100644 index 00000000000..3e3da2efcad --- /dev/null +++ b/docs/zh-hans/framework/svelte/installation.md @@ -0,0 +1,35 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-06T05:20:41.410Z' +id: installation +title: 安装 +--- +您可以通过 [NPM](https://npmjs.com) 安装 Svelte Query。 + +> v5 版本目前作为发布候选版本提供。我们预计此后不会再有重大的 API 变更。我们鼓励您尝试使用并报告遇到的任何问题。 + +### NPM + +```bash +npm i @tanstack/svelte-query +``` + +或 + +```bash +pnpm add @tanstack/svelte-query +``` + +或 + +```bash +yarn add @tanstack/svelte-query +``` + +或 + +```bash +bun add @tanstack/svelte-query +``` + +> 想在下载前体验一下吗?试试这个 [基础示例](../examples/basic) 吧! diff --git a/docs/zh-hans/framework/svelte/overview.md b/docs/zh-hans/framework/svelte/overview.md new file mode 100644 index 00000000000..b6f07a5dc27 --- /dev/null +++ b/docs/zh-hans/framework/svelte/overview.md @@ -0,0 +1,75 @@ +--- +source-updated-at: '2025-01-23T16:50:04.000Z' +translation-updated-at: '2025-05-06T05:21:37.348Z' +id: overview +title: 概述 +--- +`@tanstack/svelte-query` 包为通过 Svelte 使用 TanStack Query 提供了一流的 API。 + +## 示例 + +在项目根目录附近引入 QueryClientProvider: + +```svelte + + + + + +``` + +然后在任意组件中调用函数(例如 createQuery): + +```svelte + + +
    + {#if $query.isLoading} +

    Loading...

    + {:else if $query.isError} +

    Error: {$query.error.message}

    + {:else if $query.isSuccess} + {#each $query.data as todo} +

    {todo.title}

    + {/each} + {/if} +
    +``` + +## SvelteKit + +如果使用 SvelteKit,请查看 [服务端渲染 (SSR) 与 SvelteKit](../ssr) 文档。 + +## 可用函数 + +Svelte Query 提供了以下实用函数和组件,可简化 Svelte 应用中的服务端状态管理: + +- `createQuery` +- `createQueries` +- `createInfiniteQuery` +- `createMutation` +- `useQueryClient` +- `useIsFetching` +- `useIsMutating` +- `useHydrate` +- `` +- `` + +## Svelte Query 与 React Query 的重要区别 + +Svelte Query 提供了与 React Query 相似的 API,但需注意以下关键差异: + +- Svelte Query 中的许多函数会返回 Svelte 的 store 对象。要响应式访问这些 store 的值,需要在变量名前添加 `$` 前缀。可通过 [Svelte store 文档](https://learn.svelte.dev/tutorial/writable-stores) 了解更多。 +- 若查询或变更依赖变量,必须使用 store 作为配置项。详见 [响应式文档](../reactivity)。 diff --git a/docs/zh-hans/framework/svelte/reactivity.md b/docs/zh-hans/framework/svelte/reactivity.md new file mode 100644 index 00000000000..68e4694a0b2 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reactivity.md @@ -0,0 +1,50 @@ +--- +source-updated-at: '2024-08-19T12:20:53.000Z' +translation-updated-at: '2025-05-06T05:21:59.065Z' +id: reactivity +title: 响应式 +--- +Svelte 使用编译器构建代码以优化渲染。默认情况下,组件仅运行一次,除非在标记中被引用。若要对选项变化作出响应,您需要使用[存储 (store)](https://svelte.dev/docs/svelte-store)。 + +在以下示例中,`refetchInterval` 选项从绑定到输入框的变量 `intervalMs` 设置。然而由于查询无法响应 `intervalMs` 的变化,当输入值改变时 `refetchInterval` 不会更新。 + +```svelte + + + +``` + +为解决此问题,我们可以将 `intervalMs` 转换为可写存储 (writable store)。查询选项随后可转换为派生存储 (derived store),以真正的响应式特性传入函数。 + +```svelte + + + +``` diff --git a/docs/zh-hans/framework/svelte/reference/classes/hydrationboundary.md b/docs/zh-hans/framework/svelte/reference/classes/hydrationboundary.md new file mode 100644 index 00000000000..1ba842230a4 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/classes/hydrationboundary.md @@ -0,0 +1,276 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.717Z' +id: HydrationBoundary +title: HydrationBoundary +--- + +# Class: HydrationBoundary\ + +Base class for Svelte components with some minor dev-enhancements. Used when dev=true. + +Can be used to create strongly typed Svelte components. + +#### Example: + +You have component library on npm called `component-library`, from which +you export a component called `MyComponent`. For Svelte+TypeScript users, +you want to provide typings. Therefore you create a `index.d.ts`: + +```ts +import { SvelteComponent } from 'svelte' +export class MyComponent extends SvelteComponent<{ foo: string }> {} +``` + +Typing this makes it possible for IDEs like VS Code with the Svelte extension +to provide intellisense and to use the component like this in a Svelte file +with TypeScript: + +```svelte + + + +``` + +## Extends + +- `SvelteComponent_1`\<`Props`, `Events`\> + +## Type Parameters + +• **Props** _extends_ `Record`\<`string`, `any`\> = `any` + +• **Events** _extends_ `Record`\<`string`, `any`\> = `any` + +• **Slots** _extends_ `Record`\<`string`, `any`\> = `any` + +## Indexable + +\[`prop`: `string`\]: `any` + +## Constructors + +### new HydrationBoundary() + +```ts +new HydrationBoundary(options): HydrationBoundary +``` + +#### Parameters + +• **options**: `ComponentConstructorOptions`\<`Props`\> + +#### Returns + +[`HydrationBoundary`](hydrationboundary.md)\<`Props`, `Events`, `Slots`\> + +#### Overrides + +`SvelteComponent_1.constructor` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:144 + +## Properties + +### $$ + +```ts +$$: any +``` + +### PRIVATE API + +Do not use, may change at any time + +#### Inherited from + +`SvelteComponent_1.$$` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:102 + +--- + +### $$events_def + +```ts +$$events_def: Events +``` + +For type checking capabilities only. +Does not exist at runtime. + +### DO NOT USE! + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:158 + +--- + +### $$prop_def + +```ts +$$prop_def: Props +``` + +For type checking capabilities only. +Does not exist at runtime. + +### DO NOT USE! + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:151 + +--- + +### $$set + +```ts +$$set: any +``` + +### PRIVATE API + +Do not use, may change at any time + +#### Inherited from + +`SvelteComponent_1.$$set` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:109 + +--- + +### $$slot_def + +```ts +$$slot_def: Slots +``` + +For type checking capabilities only. +Does not exist at runtime. + +### DO NOT USE! + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:165 + +## Methods + +### $capture_state() + +```ts +$capture_state(): void +``` + +#### Returns + +`void` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:167 + +--- + +### $destroy() + +```ts +$destroy(): void +``` + +#### Returns + +`void` + +#### Inherited from + +`SvelteComponent_1.$destroy` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:111 + +--- + +### $inject_state() + +```ts +$inject_state(): void +``` + +#### Returns + +`void` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:169 + +--- + +### $on() + +```ts +$on(type, callback): () => void +``` + +#### Type Parameters + +• **K** _extends_ `string` + +#### Parameters + +• **type**: `K` + +• **callback**: `undefined` \| `null` \| (`e`) => `void` + +#### Returns + +`Function` + +##### Returns + +`void` + +#### Inherited from + +`SvelteComponent_1.$on` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:113 + +--- + +### $set() + +```ts +$set(props): void +``` + +#### Parameters + +• **props**: `Partial`\<`Props`\> + +#### Returns + +`void` + +#### Inherited from + +`SvelteComponent_1.$set` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:115 diff --git a/docs/zh-hans/framework/svelte/reference/functions/createinfinitequery.md b/docs/zh-hans/framework/svelte/reference/functions/createinfinitequery.md new file mode 100644 index 00000000000..1e667ff3e90 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/createinfinitequery.md @@ -0,0 +1,44 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.582Z' +id: createInfiniteQuery +title: createInfiniteQuery +--- + +# Function: createInfiniteQuery() + +```ts +function createInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(options, queryClient?): CreateInfiniteQueryResult +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`CreateInfiniteQueryOptions`](../type-aliases/createinfinitequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`, `TPageParam`\>\> + +• **queryClient?**: `QueryClient` + +## Returns + +[`CreateInfiniteQueryResult`](../type-aliases/createinfinitequeryresult.md)\<`TData`, `TError`\> + +## Defined in + +[packages/svelte-query/src/createInfiniteQuery.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createInfiniteQuery.ts#L16) diff --git a/docs/zh-hans/framework/svelte/reference/functions/createmutation.md b/docs/zh-hans/framework/svelte/reference/functions/createmutation.md new file mode 100644 index 00000000000..d01e7cbceee --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/createmutation.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.520Z' +id: createMutation +title: createMutation +--- + +# Function: createMutation() + +```ts +function createMutation( + options, + queryClient?, +): CreateMutationResult +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `Error` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`CreateMutationOptions`](../type-aliases/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>\> + +• **queryClient?**: `QueryClient` + +## Returns + +[`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`\> + +## Defined in + +[packages/svelte-query/src/createMutation.ts:13](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createMutation.ts#L13) diff --git a/docs/zh-hans/framework/svelte/reference/functions/createqueries.md b/docs/zh-hans/framework/svelte/reference/functions/createqueries.md new file mode 100644 index 00000000000..d3d8bf97c59 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/createqueries.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.402Z' +id: createQueries +title: createQueries +--- + +# Function: createQueries() + +```ts +function createQueries( + __namedParameters, + queryClient?, +): Readable +``` + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TCombinedResult** = `T` _extends_ [] ? [] : `T` _extends_ [`Head`] ? [`GetCreateQueryResult`\<`Head`\>] : `T` _extends_ [`Head`, `...Tails[]`] ? [`...Tails[]`] _extends_ [] ? [] : [`...Tails[]`] _extends_ [`Head`] ? [`GetCreateQueryResult`\<`Head`\>, `GetCreateQueryResult`\<`Head`\>] : [`...Tails[]`] _extends_ [`Head`, `...Tails[]`] ? [`...Tails[]`] _extends_ [] ? [] : [`...Tails[]`] _extends_ [`Head`] ? [`GetCreateQueryResult`\<`Head`\>, `GetCreateQueryResult`\<`Head`\>, `GetCreateQueryResult`\<`Head`\>] : [`...Tails[]`] _extends_ [`Head`, `...Tails[]`] ? [`...(...)[]`] _extends_ [] ? [] : ... _extends_ ... ? ... : ... : [`...(...)[]`] _extends_ ...[] ? ...[] : ...[] : [`...Tails[]`] _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] : `T` _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] + +## Parameters + +• **\_\_namedParameters** + +• **\_\_namedParameters.combine?** + +• **\_\_namedParameters.queries?**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`...(T extends [] ? [] : T extends [Head] ? [GetQueryObserverOptionsForCreateQueries] : T extends [Head, ...Tails[]] ? [...Tails[]] extends [] ? [] : [...Tails[]] extends [Head] ? [GetQueryObserverOptionsForCreateQueries, GetQueryObserverOptionsForCreateQueries] : [...Tails[]] extends [Head, ...Tails[]] ? [...(...)[]] extends [] ? [] : (...) extends (...) ? (...) : (...) : readonly (...)[] extends [...(...)[]] ? [...(...)[]] : (...) extends (...) ? (...) : (...) : readonly unknown[] extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[])[]`]\> + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`TCombinedResult`\> + +## Defined in + +[packages/svelte-query/src/createQueries.ts:205](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQueries.ts#L205) diff --git a/docs/zh-hans/framework/svelte/reference/functions/createquery.md b/docs/zh-hans/framework/svelte/reference/functions/createquery.md new file mode 100644 index 00000000000..fbfbdd95fc5 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/createquery.md @@ -0,0 +1,107 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.449Z' +id: createQuery +title: createQuery +--- + +# Function: createQuery() + +## createQuery(options, queryClient) + +```ts +function createQuery( + options, + queryClient?, +): DefinedCreateQueryResult +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>\> + +• **queryClient?**: `QueryClient` + +### Returns + +[`DefinedCreateQueryResult`](../type-aliases/definedcreatequeryresult.md)\<`TData`, `TError`\> + +### Defined in + +[packages/svelte-query/src/createQuery.ts:15](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQuery.ts#L15) + +## createQuery(options, queryClient) + +```ts +function createQuery( + options, + queryClient?, +): CreateQueryResult +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>\> + +• **queryClient?**: `QueryClient` + +### Returns + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +### Defined in + +[packages/svelte-query/src/createQuery.ts:27](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQuery.ts#L27) + +## createQuery(options, queryClient) + +```ts +function createQuery( + options, + queryClient?, +): CreateQueryResult +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`CreateQueryOptions`](../type-aliases/createqueryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>\> + +• **queryClient?**: `QueryClient` + +### Returns + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +### Defined in + +[packages/svelte-query/src/createQuery.ts:39](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQuery.ts#L39) diff --git a/docs/zh-hans/framework/svelte/reference/functions/getisrestoringcontext.md b/docs/zh-hans/framework/svelte/reference/functions/getisrestoringcontext.md new file mode 100644 index 00000000000..17c933b29e0 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/getisrestoringcontext.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.365Z' +id: getIsRestoringContext +title: getIsRestoringContext +--- + +# Function: getIsRestoringContext() + +```ts +function getIsRestoringContext(): Readable +``` + +Retrieves a `isRestoring` from Svelte's context + +## Returns + +`Readable`\<`boolean`\> + +## Defined in + +[packages/svelte-query/src/context.ts:28](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L28) diff --git a/docs/zh-hans/framework/svelte/reference/functions/getqueryclientcontext.md b/docs/zh-hans/framework/svelte/reference/functions/getqueryclientcontext.md new file mode 100644 index 00000000000..3aa1c881f34 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/getqueryclientcontext.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.325Z' +id: getQueryClientContext +title: getQueryClientContext +--- + +# Function: getQueryClientContext() + +```ts +function getQueryClientContext(): QueryClient +``` + +Retrieves a Client from Svelte's context + +## Returns + +`QueryClient` + +## Defined in + +[packages/svelte-query/src/context.ts:9](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L9) diff --git a/docs/zh-hans/framework/svelte/reference/functions/infinitequeryoptions.md b/docs/zh-hans/framework/svelte/reference/functions/infinitequeryoptions.md new file mode 100644 index 00000000000..27df1d9d4e9 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/infinitequeryoptions.md @@ -0,0 +1,51 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.282Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +--- + +# Function: infiniteQueryOptions() + +```ts +function infiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>( + options, +): CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam +> +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Parameters + +• **options**: [`CreateInfiniteQueryOptions`](../type-aliases/createinfinitequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`, `TPageParam`\> + +## Returns + +[`CreateInfiniteQueryOptions`](../type-aliases/createinfinitequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`, `TPageParam`\> + +## Defined in + +[packages/svelte-query/src/infiniteQueryOptions.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/infiniteQueryOptions.ts#L4) diff --git a/docs/zh-hans/framework/svelte/reference/functions/queryoptions.md b/docs/zh-hans/framework/svelte/reference/functions/queryoptions.md new file mode 100644 index 00000000000..197d4dbbe89 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/queryoptions.md @@ -0,0 +1,68 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.242Z' +id: queryOptions +title: queryOptions +--- + +# Function: queryOptions() + +## queryOptions(options) + +```ts +function queryOptions( + options, +): DefinedInitialDataOptions & object +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +### Returns + +[`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +### Defined in + +[packages/svelte-query/src/queryOptions.ts:26](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L26) + +## queryOptions(options) + +```ts +function queryOptions( + options, +): UndefinedInitialDataOptions & object +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +### Returns + +[`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +### Defined in + +[packages/svelte-query/src/queryOptions.ts:37](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L37) diff --git a/docs/zh-hans/framework/svelte/reference/functions/setisrestoringcontext.md b/docs/zh-hans/framework/svelte/reference/functions/setisrestoringcontext.md new file mode 100644 index 00000000000..710f5128f8e --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/setisrestoringcontext.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.206Z' +id: setIsRestoringContext +title: setIsRestoringContext +--- + +# Function: setIsRestoringContext() + +```ts +function setIsRestoringContext(isRestoring): void +``` + +Sets a `isRestoring` on Svelte's context + +## Parameters + +• **isRestoring**: `Readable`\<`boolean`\> + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/context.ts:40](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L40) diff --git a/docs/zh-hans/framework/svelte/reference/functions/setqueryclientcontext.md b/docs/zh-hans/framework/svelte/reference/functions/setqueryclientcontext.md new file mode 100644 index 00000000000..dddf1dc47e7 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/setqueryclientcontext.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.163Z' +id: setQueryClientContext +title: setQueryClientContext +--- + +# Function: setQueryClientContext() + +```ts +function setQueryClientContext(client): void +``` + +Sets a QueryClient on Svelte's context + +## Parameters + +• **client**: `QueryClient` + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/context.ts:21](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L21) diff --git a/docs/zh-hans/framework/svelte/reference/functions/usehydrate.md b/docs/zh-hans/framework/svelte/reference/functions/usehydrate.md new file mode 100644 index 00000000000..967c02db23e --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/usehydrate.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.066Z' +id: useHydrate +title: useHydrate +--- + +# Function: useHydrate() + +```ts +function useHydrate(state?, options?, queryClient?): void +``` + +## Parameters + +• **state?**: `unknown` + +• **options?**: `HydrateOptions` + +• **queryClient?**: `QueryClient` + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/useHydrate.ts:8](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useHydrate.ts#L8) diff --git a/docs/zh-hans/framework/svelte/reference/functions/useisfetching.md b/docs/zh-hans/framework/svelte/reference/functions/useisfetching.md new file mode 100644 index 00000000000..d496993af9e --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/useisfetching.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.113Z' +id: useIsFetching +title: useIsFetching +--- + +# Function: useIsFetching() + +```ts +function useIsFetching(filters?, queryClient?): Readable +``` + +## Parameters + +• **filters?**: `QueryFilters` + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`number`\> + +## Defined in + +[packages/svelte-query/src/useIsFetching.ts:10](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useIsFetching.ts#L10) diff --git a/docs/zh-hans/framework/svelte/reference/functions/useismutating.md b/docs/zh-hans/framework/svelte/reference/functions/useismutating.md new file mode 100644 index 00000000000..147588a6ee8 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/useismutating.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:32.013Z' +id: useIsMutating +title: useIsMutating +--- + +# Function: useIsMutating() + +```ts +function useIsMutating(filters?, queryClient?): Readable +``` + +## Parameters + +• **filters?**: `MutationFilters` + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`number`\> + +## Defined in + +[packages/svelte-query/src/useIsMutating.ts:10](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useIsMutating.ts#L10) diff --git a/docs/zh-hans/framework/svelte/reference/functions/useisrestoring.md b/docs/zh-hans/framework/svelte/reference/functions/useisrestoring.md new file mode 100644 index 00000000000..9c162214b4b --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/useisrestoring.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.953Z' +id: useIsRestoring +title: useIsRestoring +--- + +# Function: useIsRestoring() + +```ts +function useIsRestoring(): Readable +``` + +## Returns + +`Readable`\<`boolean`\> + +## Defined in + +[packages/svelte-query/src/useIsRestoring.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useIsRestoring.ts#L4) diff --git a/docs/zh-hans/framework/svelte/reference/functions/usemutationstate.md b/docs/zh-hans/framework/svelte/reference/functions/usemutationstate.md new file mode 100644 index 00000000000..c92f2ad52fd --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/usemutationstate.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.828Z' +id: useMutationState +title: useMutationState +--- + +# Function: useMutationState() + +```ts +function useMutationState(options, queryClient?): Readable +``` + +## Type Parameters + +• **TResult** = `MutationState`\<`unknown`, `Error`, `unknown`, `unknown`\> + +## Parameters + +• **options**: [`MutationStateOptions`](../type-aliases/mutationstateoptions.md)\<`TResult`\> = `{}` + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`TResult`[]\> + +## Defined in + +[packages/svelte-query/src/useMutationState.ts:24](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useMutationState.ts#L24) diff --git a/docs/zh-hans/framework/svelte/reference/functions/usequeryclient.md b/docs/zh-hans/framework/svelte/reference/functions/usequeryclient.md new file mode 100644 index 00000000000..5cfcd024dfd --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/functions/usequeryclient.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.734Z' +id: useQueryClient +title: useQueryClient +--- + +# Function: useQueryClient() + +```ts +function useQueryClient(queryClient?): QueryClient +``` + +## Parameters + +• **queryClient?**: `QueryClient` + +## Returns + +`QueryClient` + +## Defined in + +[packages/svelte-query/src/useQueryClient.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useQueryClient.ts#L4) diff --git a/docs/zh-hans/framework/svelte/reference/index.md b/docs/zh-hans/framework/svelte/reference/index.md new file mode 100644 index 00000000000..55ca8331a22 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/index.md @@ -0,0 +1,59 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.679Z' +id: '@tanstack/svelte-query' +title: '@tanstack/svelte-query' +--- + +# @tanstack/svelte-query + +## References + +### QueryClientProvider + +Renames and re-exports [HydrationBoundary](classes/hydrationboundary.md) + +## Classes + +- [HydrationBoundary](classes/hydrationboundary.md) + +## Type Aliases + +- [CreateBaseMutationResult](type-aliases/createbasemutationresult.md) +- [CreateBaseQueryOptions](type-aliases/createbasequeryoptions.md) +- [CreateBaseQueryResult](type-aliases/createbasequeryresult.md) +- [CreateInfiniteQueryOptions](type-aliases/createinfinitequeryoptions.md) +- [CreateInfiniteQueryResult](type-aliases/createinfinitequeryresult.md) +- [CreateMutateAsyncFunction](type-aliases/createmutateasyncfunction.md) +- [CreateMutateFunction](type-aliases/createmutatefunction.md) +- [CreateMutationOptions](type-aliases/createmutationoptions.md) +- [CreateMutationResult](type-aliases/createmutationresult.md) +- [CreateQueryOptions](type-aliases/createqueryoptions.md) +- [CreateQueryResult](type-aliases/createqueryresult.md) +- [DefinedCreateBaseQueryResult](type-aliases/definedcreatebasequeryresult.md) +- [DefinedCreateQueryResult](type-aliases/definedcreatequeryresult.md) +- [DefinedInitialDataOptions](type-aliases/definedinitialdataoptions.md) +- [MutationStateOptions](type-aliases/mutationstateoptions.md) +- [QueriesOptions](type-aliases/queriesoptions.md) +- [QueriesResults](type-aliases/queriesresults.md) +- [StoreOrVal](type-aliases/storeorval.md) +- [UndefinedInitialDataOptions](type-aliases/undefinedinitialdataoptions.md) + +## Functions + +- [createInfiniteQuery](functions/createinfinitequery.md) +- [createMutation](functions/createmutation.md) +- [createQueries](functions/createqueries.md) +- [createQuery](functions/createquery.md) +- [getIsRestoringContext](functions/getisrestoringcontext.md) +- [getQueryClientContext](functions/getqueryclientcontext.md) +- [infiniteQueryOptions](functions/infinitequeryoptions.md) +- [queryOptions](functions/queryoptions.md) +- [setIsRestoringContext](functions/setisrestoringcontext.md) +- [setQueryClientContext](functions/setqueryclientcontext.md) +- [useHydrate](functions/usehydrate.md) +- [useIsFetching](functions/useisfetching.md) +- [useIsMutating](functions/useismutating.md) +- [useIsRestoring](functions/useisrestoring.md) +- [useMutationState](functions/usemutationstate.md) +- [useQueryClient](functions/usequeryclient.md) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createbasemutationresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createbasemutationresult.md new file mode 100644 index 00000000000..27d6430d240 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createbasemutationresult.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.682Z' +id: CreateBaseMutationResult +title: CreateBaseMutationResult +--- + +# Type Alias: CreateBaseMutationResult\ + +```ts +type CreateBaseMutationResult: Override, object> & object; +``` + +## Type declaration + +### mutateAsync + +```ts +mutateAsync: CreateMutateAsyncFunction +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:113](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L113) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryoptions.md new file mode 100644 index 00000000000..71c4e43e042 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryoptions.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.606Z' +id: CreateBaseQueryOptions +title: CreateBaseQueryOptions +--- + +# Type Alias: CreateBaseQueryOptions\ + +```ts +type CreateBaseQueryOptions: QueryObserverOptions; +``` + +Options for createBaseQuery + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/types.ts:23](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L23) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryresult.md new file mode 100644 index 00000000000..e2fe97a6b0e --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createbasequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.555Z' +id: CreateBaseQueryResult +title: CreateBaseQueryResult +--- + +# Type Alias: CreateBaseQueryResult\ + +```ts +type CreateBaseQueryResult: Readable>; +``` + +Result from createBaseQuery + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:32](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L32) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md new file mode 100644 index 00000000000..b38bbc055c2 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md @@ -0,0 +1,32 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.510Z' +id: CreateInfiniteQueryOptions +title: CreateInfiniteQueryOptions +--- + +# Type Alias: CreateInfiniteQueryOptions\ + +```ts +type CreateInfiniteQueryOptions: InfiniteQueryObserverOptions; +``` + +Options for createInfiniteQuery + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:52](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L52) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryresult.md new file mode 100644 index 00000000000..db141eb4b45 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createinfinitequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.468Z' +id: CreateInfiniteQueryResult +title: CreateInfiniteQueryResult +--- + +# Type Alias: CreateInfiniteQueryResult\ + +```ts +type CreateInfiniteQueryResult: Readable>; +``` + +Result from createInfiniteQuery + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:69](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L69) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createmutateasyncfunction.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutateasyncfunction.md new file mode 100644 index 00000000000..b2bde559290 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutateasyncfunction.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.429Z' +id: CreateMutateAsyncFunction +title: CreateMutateAsyncFunction +--- + +# Type Alias: CreateMutateAsyncFunction\ + +```ts +type CreateMutateAsyncFunction: MutateFunction; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:106](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L106) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createmutatefunction.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutatefunction.md new file mode 100644 index 00000000000..478b2ff3782 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutatefunction.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.374Z' +id: CreateMutateFunction +title: CreateMutateFunction +--- + +# Type Alias: CreateMutateFunction()\ + +```ts +type CreateMutateFunction: (...args) => void; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Parameters + +• ...**args**: `Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TContext`\>\> + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/types.ts:97](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L97) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createmutationoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutationoptions.md new file mode 100644 index 00000000000..203b9e57a13 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutationoptions.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.331Z' +id: CreateMutationOptions +title: CreateMutationOptions +--- + +# Type Alias: CreateMutationOptions\ + +```ts +type CreateMutationOptions: OmitKeyof, "_defaulted">; +``` + +Options for createMutation + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:87](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L87) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createmutationresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutationresult.md new file mode 100644 index 00000000000..ed67191651f --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createmutationresult.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.258Z' +id: CreateMutationResult +title: CreateMutationResult +--- + +# Type Alias: CreateMutationResult\ + +```ts +type CreateMutationResult: Readable>; +``` + +Result from createMutation + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:126](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L126) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createqueryoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createqueryoptions.md new file mode 100644 index 00000000000..5e2d736b0ca --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createqueryoptions.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.208Z' +id: CreateQueryOptions +title: CreateQueryOptions +--- + +# Type Alias: CreateQueryOptions\ + +```ts +type CreateQueryOptions: CreateBaseQueryOptions; +``` + +Options for createQuery + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/types.ts:38](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L38) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/createqueryresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/createqueryresult.md new file mode 100644 index 00000000000..6ceab1fdbd1 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/createqueryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.162Z' +id: CreateQueryResult +title: CreateQueryResult +--- + +# Type Alias: CreateQueryResult\ + +```ts +type CreateQueryResult: CreateBaseQueryResult; +``` + +Result from createQuery + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:46](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L46) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md new file mode 100644 index 00000000000..88f1a801d8c --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.069Z' +id: DefinedCreateBaseQueryResult +title: DefinedCreateBaseQueryResult +--- + +# Type Alias: DefinedCreateBaseQueryResult\ + +```ts +type DefinedCreateBaseQueryResult: Readable>; +``` + +Options for createBaseQuery with initialData + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:75](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L75) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatequeryresult.md b/docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatequeryresult.md new file mode 100644 index 00000000000..4bb3ed64e54 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/definedcreatequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:31.009Z' +id: DefinedCreateQueryResult +title: DefinedCreateQueryResult +--- + +# Type Alias: DefinedCreateQueryResult\ + +```ts +type DefinedCreateQueryResult: DefinedCreateBaseQueryResult; +``` + +Options for createQuery with initialData + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:81](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L81) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/definedinitialdataoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/definedinitialdataoptions.md new file mode 100644 index 00000000000..56c0081fd6e --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/definedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.960Z' +id: DefinedInitialDataOptions +title: DefinedInitialDataOptions +--- + +# Type Alias: DefinedInitialDataOptions\ + +```ts +type DefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData + +```ts +initialData: NonUndefinedGuard | () => NonUndefinedGuard; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/queryOptions.ts:15](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L15) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/mutationstateoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/mutationstateoptions.md new file mode 100644 index 00000000000..944aaba1253 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/mutationstateoptions.md @@ -0,0 +1,44 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.908Z' +id: MutationStateOptions +title: MutationStateOptions +--- + +# Type Alias: MutationStateOptions\ + +```ts +type MutationStateOptions: object; +``` + +Options for useMutationState + +## Type Parameters + +• **TResult** = `MutationState` + +## Type declaration + +### filters? + +```ts +optional filters: MutationFilters; +``` + +### select()? + +```ts +optional select: (mutation) => TResult; +``` + +#### Parameters + +• **mutation**: `Mutation`\<`unknown`, `DefaultError`, `unknown`, `unknown`\> + +#### Returns + +`TResult` + +## Defined in + +[packages/svelte-query/src/types.ts:140](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L140) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/queriesoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/queriesoptions.md new file mode 100644 index 00000000000..820ed0e65aa --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/queriesoptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.857Z' +id: QueriesOptions +title: QueriesOptions +--- + +# Type Alias: QueriesOptions\ + +```ts +type QueriesOptions: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetQueryObserverOptionsForCreateQueries] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions<[...Tails], [...TResults, GetQueryObserverOptionsForCreateQueries], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; +``` + +QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResults** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[packages/svelte-query/src/createQueries.ts:129](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQueries.ts#L129) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/queriesresults.md b/docs/zh-hans/framework/svelte/reference/type-aliases/queriesresults.md new file mode 100644 index 00000000000..aba2599035f --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/queriesresults.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.792Z' +id: QueriesResults +title: QueriesResults +--- + +# Type Alias: QueriesResults\ + +```ts +type QueriesResults: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResult] : T extends [infer Head, ...(infer Tails)] ? QueriesResults<[...Tails], [...TResults, GetCreateQueryResult], [...TDepth, 1]> : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverResult[] : QueryObserverResult[]; +``` + +QueriesResults reducer recursively maps type param to results + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResults** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[packages/svelte-query/src/createQueries.ts:171](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQueries.ts#L171) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/storeorval.md b/docs/zh-hans/framework/svelte/reference/type-aliases/storeorval.md new file mode 100644 index 00000000000..a0e8ebbcb33 --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/storeorval.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.756Z' +id: StoreOrVal +title: StoreOrVal +--- + +# Type Alias: StoreOrVal\ + +```ts +type StoreOrVal: T | Readable; +``` + +Allows a type to be either the base object or a store of that object + +## Type Parameters + +• **T** + +## Defined in + +[packages/svelte-query/src/types.ts:20](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L20) diff --git a/docs/zh-hans/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md b/docs/zh-hans/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md new file mode 100644 index 00000000000..6d791bba86d --- /dev/null +++ b/docs/zh-hans/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-06T05:20:30.638Z' +id: UndefinedInitialDataOptions +title: UndefinedInitialDataOptions +--- + +# Type Alias: UndefinedInitialDataOptions\ + +```ts +type UndefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData? + +```ts +optional initialData: undefined; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/queryOptions.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L4) diff --git a/docs/zh-hans/framework/svelte/ssr.md b/docs/zh-hans/framework/svelte/ssr.md new file mode 100644 index 00000000000..4b397333d3f --- /dev/null +++ b/docs/zh-hans/framework/svelte/ssr.md @@ -0,0 +1,155 @@ +--- +source-updated-at: '2024-04-02T22:46:31.000Z' +translation-updated-at: '2025-05-06T05:22:45.237Z' +id: overview +title: SSR 与 SvelteKit +--- +## 配置 + +SvelteKit 默认使用服务端渲染 (SSR) 来渲染路由。因此,您需要在服务端禁用查询功能。否则,即使在 HTML 已发送至客户端后,您的查询仍会在服务端继续异步执行。 + +推荐的方式是在 `QueryClient` 对象中使用 SvelteKit 的 `browser` 模块。这不会禁用 `queryClient.prefetchQuery()`,该方法会在以下解决方案之一中使用。 + +**src/routes/+layout.svelte** + +```svelte + + + + + +``` + +## 预取数据 + +Svelte Query 支持两种在服务端预取数据并通过 SvelteKit 传递至客户端的方式。 + +如需查看理想的 SSR 配置,请参考 [SSR 示例](../examples/ssr)。 + +### 使用 `initialData` + +结合 SvelteKit 的 [`load`](https://kit.svelte.dev/docs/load) 方法,您可以将服务端加载的数据传入 `createQuery` 的 `initialData` 选项: + +**src/routes/+page.ts** + +```ts +export async function load() { + const posts = await getPosts() + return { posts } +} +``` + +**src/routes/+page.svelte** + +```svelte + +``` + +优点: + +- 此配置简洁,可快速解决部分场景需求 +- 兼容 `+page.ts`/`+layout.ts` 和 `+page.server.ts`/`+layout.server.ts` 的 load 函数 + +缺点: + +- 若在组件树深层调用 `createQuery`,需将 `initialData` 向下传递至该处 +- 若在多个位置使用相同查询调用 `createQuery`,需为所有位置传递 `initialData` +- 无法获知查询在服务端的获取时间,因此 `dataUpdatedAt` 和判断查询是否需要重新获取将基于页面加载时间 + +### 使用 `prefetchQuery` + +Svelte Query 支持在服务端预取查询。通过以下配置,您可以在数据发送至用户浏览器前将其预取并传入 QueryClientProvider。因此,数据已存在于缓存中,客户端不会发生初始获取。 + +**src/routes/+layout.ts** + +```ts +import { browser } from '$app/environment' +import { QueryClient } from '@tanstack/svelte-query' + +export async function load() { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + enabled: browser, + }, + }, + }) + + return { queryClient } +} +``` + +**src/routes/+layout.svelte** + +```svelte + + + + + +``` + +**src/routes/+page.ts** + +```ts +export async function load({ parent, fetch }) { + const { queryClient } = await parent() + + // 此处需使用 SvelteKit 的 fetch 函数 + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: async () => (await fetch('/api/posts')).json(), + }) +} +``` + +**src/routes/+page.svelte** + +```svelte + +``` + +优点: + +- 服务端加载的数据可在任意位置访问,无需属性透传 +- 页面渲染后客户端不会发生初始请求,因为查询缓存保留了包括 `dataUpdatedAt` 在内的完整查询信息 + +缺点: + +- 初始配置需要更多文件 +- 不兼容 `+page.server.ts`/`+layout.server.ts` 的 load 函数(但需注意,与 TanStack Query 配合使用的 API 必须完全暴露给浏览器端) diff --git a/docs/zh-hans/framework/vue/community/community-projects.md b/docs/zh-hans/framework/vue/community/community-projects.md new file mode 100644 index 00000000000..8acd09da4d5 --- /dev/null +++ b/docs/zh-hans/framework/vue/community/community-projects.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2024-08-09T15:33:58.000Z' +translation-updated-at: '2025-05-06T05:27:00.262Z' +id: community-projects +title: 社区项目 +--- +## 社区项目 + +有许多社区项目基于 Vue Query 构建,利用它提供额外功能或增强开发者体验。以下项目按字母顺序排列。如果您有希望添加到列表中的项目,请提交 PR! + +> 请注意,这些项目完全由社区维护。如有相关问题,请联系项目维护者。 + +## Query Key 工厂 + +一个用于创建类型安全的标准化查询键(query keys)的库,适用于 `@tanstack/query` 的缓存管理 + +链接:https://github.com/lukemorales/query-key-factory + +## Model:统一数据源 + +Zova 在 MVC 架构中基于 Vue Query 提供了 Model 机制,通过 Model 封装统一数据源,从而标准化数据使用、简化代码结构,并提升代码可维护性 + +链接:https://github.com/cabloy/zova +文档:https://zova.js.org/guide/techniques/model/introduction.html + +## Query Rewind + +在开发过程中实现状态时间旅行与可视化 + +链接:https://reactqueryrewind.com/ diff --git a/docs/zh-hans/framework/vue/community/tkdodos-blog.md b/docs/zh-hans/framework/vue/community/tkdodos-blog.md new file mode 100644 index 00000000000..a990e83ad9d --- /dev/null +++ b/docs/zh-hans/framework/vue/community/tkdodos-blog.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.914Z' +id: tkdodos-blog +title: TkDodo's Blog +ref: docs/zh-hans/framework/react/community/tkdodos-blog.md +--- + diff --git a/docs/zh-hans/framework/vue/devtools.md b/docs/zh-hans/framework/vue/devtools.md new file mode 100644 index 00000000000..08acad88514 --- /dev/null +++ b/docs/zh-hans/framework/vue/devtools.md @@ -0,0 +1,88 @@ +--- +source-updated-at: '2024-08-21T11:18:15.000Z' +translation-updated-at: '2025-05-06T16:09:05.489Z' +id: devtools +title: 开发者工具 +--- +举起双手欢呼吧,因为 Vue Query 配备了专属的开发工具 (devtools)!🥳 + +当你开始使用 Vue Query 时,这些开发工具将成为得力助手。它们能直观展示 Vue Query 的内部运作机制,在调试困境中为你节省大量时间! + +## 基于组件的开发工具 (Vue 3) + +你可以通过专用包将开发工具组件直接集成到页面中。基于组件的开发工具采用与框架无关的实现方式,始终保持最新状态。 + +开发工具组件是一个独立包,需先安装: + +```bash +npm i @tanstack/vue-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/vue-query-devtools +``` + +或 + +```bash +yarn add @tanstack/vue-query-devtools +``` + +或 + +```bash +bun add @tanstack/vue-query-devtools +``` + +默认情况下,Vue Query 开发工具仅在 `process.env.NODE_ENV === 'development'` 时包含在构建包中,因此无需担心生产环境打包时需手动排除。 + +开发工具会以固定浮动元素的形式挂载到应用中,并在屏幕角落提供显示/隐藏的切换按钮。该切换状态会存储在 localStorage 中,页面刷新后仍会保留。 + +请将以下代码尽可能放置在 Vue 应用的顶层,越靠近页面根节点效果越好: + +```vue + + + +``` + +### 配置选项 + +- `initialIsOpen: Boolean` + - 设为 `true` 可使开发工具默认展开 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` + - 默认为 `bottom-right` + - 控制 React Query 徽标按钮的位置,用于展开/收起开发工具面板 +- `position?: "top" | "bottom" | "left" | "right"` + - 默认为 `bottom` + - 开发工具面板的停靠位置 +- `client?: QueryClient` + - 传入自定义 QueryClient。未指定时使用最近上下文中的实例 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 预定义可在查询中触发的错误类型。当从 UI 触发特定错误时,初始化函数(接收对应查询作为参数)将被调用,需返回一个 Error 对象 +- `styleNonce?: string` + - 用于向添加到 document head 的 style 标签传递 nonce 值。适用于需要使用内容安全策略 (CSP) nonce 允许内联样式的情况 +- `shadowDOMTarget?: ShadowRoot` + - 默认行为会将开发工具样式应用到 DOM 的 head 标签 + - 传入 shadow DOM 目标可使样式应用于 shadow DOM 而非主 DOM 的 head 标签 + +## 传统开发工具 + +Vue Query 可与 [Vue 官方开发工具](https://github.com/vuejs/devtools-next)无缝集成,添加自定义检查器和时间线事件。默认情况下开发工具代码会在生产构建时被 tree-shaking 移除。 + +只需在插件配置中启用即可: + +```ts +app.use(VueQueryPlugin, { + enableDevtoolsV6Plugin: true, +}) +``` + +同时支持 v6 和 v7 版本的开发工具。 diff --git a/docs/zh-hans/framework/vue/graphql.md b/docs/zh-hans/framework/vue/graphql.md new file mode 100644 index 00000000000..edda620bc57 --- /dev/null +++ b/docs/zh-hans/framework/vue/graphql.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:28:32.281Z' +id: graphql +title: GraphQL +--- +由于 Vue Query 的获取机制是基于 Promise 无感知构建的,您实际上可以将 Vue Query 与任何异步数据获取客户端一起使用,包括 GraphQL! + +> 请注意,Vue Query 不支持规范化缓存 (normalized caching)。虽然绝大多数用户实际上并不需要规范化缓存,甚至从中获得的收益比他们想象的要少得多,但在极少数情况下可能需要它,因此请务必先与我们确认,以确保这确实是您所需要的功能! diff --git a/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md b/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..bd9faf6170b --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md @@ -0,0 +1,46 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:14:09.749Z' +id: background-fetching-indicators +title: 后台获取指示器 +--- +## 后台获取状态指示器 + +查询的 `status === 'pending'` 状态足以显示查询的初始加载状态,但有时您可能希望额外显示一个指示器,表明查询正在后台重新获取数据。为此,查询还提供了一个 `isFetching` 布尔值,无论 `status` 变量的状态如何,您都可以用它来显示查询正处于获取状态: + +```vue + + + +``` + +## 显示全局后台获取加载状态 + +除了单个查询的加载状态外,如果您希望在**任何**查询(包括后台查询)正在获取数据时显示全局加载指示器,可以使用 `useIsFetching` 钩子: + +```vue + + + +``` diff --git a/docs/zh-hans/framework/vue/guides/caching.md b/docs/zh-hans/framework/vue/guides/caching.md new file mode 100644 index 00000000000..07c16e218bd --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/caching.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:38:32.883Z' +id: caching +title: Caching Examples +ref: docs/zh-hans/framework/react/guides/caching.md +--- + diff --git a/docs/zh-hans/framework/vue/guides/custom-client.md b/docs/zh-hans/framework/vue/guides/custom-client.md new file mode 100644 index 00000000000..f8a4b06b127 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/custom-client.md @@ -0,0 +1,73 @@ +--- +source-updated-at: '2024-05-06T05:23:35.000Z' +translation-updated-at: '2025-05-06T16:13:50.563Z' +id: custom-client +title: 自定义客户端 +--- +### 自定义客户端 (Custom client) + +Vue Query 允许为 Vue 上下文提供自定义的 `QueryClient`。 + +当您需要预先创建 `QueryClient` 以与其他无法访问 Vue 上下文的库集成时,这个功能会非常有用。 + +因此,`VueQueryPlugin` 接受 `QueryClientConfig` 或 `QueryClient` 作为插件选项。 + +如果您提供 `QueryClientConfig`,`QueryClient` 实例将在内部创建并注入 Vue 上下文。 + +```tsx +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { queries: { staleTime: 3600 } }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +```tsx +const myClient = new QueryClient(queryClientConfig) +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClient: myClient, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +### 自定义上下文键 (Custom context key) + +您还可以自定义 `QueryClient` 在 Vue 上下文中可访问的键名。如果您希望在同一个页面上避免多个 Vue2 应用之间的命名冲突,这会很有帮助。 + +此功能同时适用于默认和自定义的 `QueryClient`。 + +```tsx +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientKey: 'Foo', +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +```tsx +const myClient = new QueryClient(queryClientConfig) +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClient: myClient, + queryClientKey: 'Foo', +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +要使用自定义客户端键,您需要在查询选项中提供它: + +```js +useQuery({ + queryKey: ['query1'], + queryFn: fetcher, + queryClientKey: 'foo', +}) +``` + +在内部,自定义键会与默认查询键作为后缀组合。但用户无需关心这一点。 + +```tsx +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientKey: 'Foo', +} +app.use(VueQueryPlugin, vueQueryPluginOptions) // -> VUE_QUERY_CLIENT:Foo +``` diff --git a/docs/zh-hans/framework/vue/guides/default-query-function.md b/docs/zh-hans/framework/vue/guides/default-query-function.md new file mode 100644 index 00000000000..021c9d40795 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/default-query-function.md @@ -0,0 +1,32 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:13:27.152Z' +id: default-query-function +title: 默认查询函数 +--- +如果你出于某种原因,希望在整个应用中共享同一个查询函数,仅通过查询键 (query key) 来标识应该获取什么数据,那么可以通过为 TanStack Query 提供一个**默认查询函数 (default query function)** 来实现: + +```tsx +// 定义一个默认查询函数,它将接收查询键作为参数 +const defaultQueryFn = async ({ queryKey }) => { + const { data } = await axios.get( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + ) + return data +} + +// 通过 defaultOptions 将默认查询函数提供给应用 +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { queries: { queryFn: defaultQueryFn } }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) + +// 现在你只需要传递一个键即可! +const { status, data, error, isFetching } = useQuery({ + queryKey: [`/posts/${postId}`], +}) +``` + +如果需要覆盖默认的 queryFn,只需像往常一样提供你自己的函数即可。 diff --git a/docs/zh-hans/framework/vue/guides/dependent-queries.md b/docs/zh-hans/framework/vue/guides/dependent-queries.md new file mode 100644 index 00000000000..b3c5e92e294 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/dependent-queries.md @@ -0,0 +1,88 @@ +--- +source-updated-at: '2024-04-08T07:32:55.000Z' +translation-updated-at: '2025-05-06T16:13:12.811Z' +id: dependent-queries +title: 依赖查询 +--- +## 依赖查询 (Dependent Query) + +依赖查询(或称串行查询)需要等待前一个查询完成后才能执行。实现这一功能非常简单,只需使用 `enabled` 选项来指定查询何时可以运行: + +```js +// 获取用户信息 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: () => getUserByEmail(email.value), +}) + +const userId = computed(() => user.value?.id) +const enabled = computed(() => !!user.value?.id) + +// 然后获取该用户的项目 +const { isIdle, data: projects } = useQuery({ + queryKey: ['projects', userId], + queryFn: () => getProjectsByUser(userId.value), + enabled, // 只有当 `enabled == true` 时才会执行查询 +}) +``` + +`projects` 查询初始状态为: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'idle' +``` + +当 `user` 数据可用时,`projects` 查询会被 `enabled` 并转为: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'fetching' +``` + +获取到项目数据后,状态将变为: + +```tsx +status: 'success' +isPending: false +fetchStatus: 'idle' +``` + +## 批量依赖查询 (useQueries Dependent Query) + +动态并行查询 `useQueries` 也可以依赖前一个查询,实现方式如下: + +```tsx +// 获取用户ID列表 +const { data: userIds } = useQuery({ + queryKey: ['users'], + queryFn: getUsersData, + select: (users) => users.map((user) => user.id), +}) + +const queries = computed(() => { + return userIds.value.length + ? userIds.value.map((id) => { + return { + queryKey: ['messages', id], + queryFn: () => getMessagesByUsers(id), + } + }) + : [] +}) + +// 然后获取用户消息 +const usersMessages = useQueries({ + queries, // 如果用户数据未定义,将返回空数组 +}) +``` + +**注意**:`useQueries` 返回的是**查询结果数组** + +## 性能注意事项 + +依赖查询本质上会形成[请求瀑布流 (request waterfall)](./request-waterfalls.md),从而影响性能。假设两个查询耗时相同,串行执行总是比并行执行多花一倍时间,这在客户端高延迟环境下尤为不利。如果可能,最好重构后端 API 使两个查询能够并行获取,尽管实际中可能难以实现。 + +在上面的示例中,与其先获取 `getUserByEmail` 才能调用 `getProjectsByUser`,不如新增一个 `getProjectsByUserEmail` 查询来消除瀑布流。 diff --git a/docs/zh-hans/framework/vue/guides/disabling-queries.md b/docs/zh-hans/framework/vue/guides/disabling-queries.md new file mode 100644 index 00000000000..bbffd772418 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/disabling-queries.md @@ -0,0 +1,103 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T16:12:44.238Z' +id: disabling-queries +title: 禁用/暂停查询 +--- +如果你想阻止某个查询自动执行,可以使用 `enabled = false` 选项。`enabled` 选项也接受返回布尔值的回调函数。 + +当 `enabled` 为 `false` 时: + +- 如果查询存在缓存数据,则该查询会以 `status === 'success'` 或 `isSuccess` 状态初始化 +- 如果查询没有缓存数据,则该查询会以 `status === 'pending'` 和 `fetchStatus === 'idle'` 状态启动 +- 查询不会在挂载时自动获取数据 +- 查询不会在后台自动重新获取数据 +- 查询会忽略查询客户端 `invalidateQueries` 和 `refetchQueries` 的调用(这些调用通常会导致查询重新获取数据) +- 通过 `useQuery` 返回的 `refetch` 可用于手动触发查询获取数据(但无法与 `skipToken` 配合使用) + +> TypeScript 用户可能更倾向于使用 [skipToken](#typesafe-disabling-of-queries-using-skiptoken) 作为 `enabled = false` 的替代方案 + +```vue + + + +``` + +永久禁用查询会失去 TanStack Query 提供的许多优秀特性(如后台重新获取),这也不是惯用做法。它会让你从声明式模式(定义查询运行的依赖条件)转入命令式模式(点击按钮时才获取数据),而且无法向 `refetch` 传递参数。通常你真正需要的是延迟初始获取的惰性查询: + +## 惰性查询 + +`enabled` 选项不仅能永久禁用查询,还可以在后续启用/禁用。典型场景是筛选表单——只有当用户输入筛选值后才发起首次请求: + +```vue + + + +``` + +### isLoading(原名为 `isInitialLoading`) + +惰性查询会从一开始就处于 `status: 'pending'` 状态,因为 `pending` 表示尚无数据。虽然这在技术上是正确的,但由于我们并未实际获取数据(查询未被启用),你可能无法用这个标志来显示加载状态。 + +如果使用禁用或惰性查询,可以改用 `isLoading` 标志。这是一个衍生标志,由以下公式计算得出: + +`isPending && isFetching` + +因此只有当查询首次获取数据时,该标志才会为 `true`。 + +## 使用 `skipToken` 实现类型安全的查询禁用 + +如果使用 TypeScript,可以通过 `skipToken` 禁用查询。这在需要基于条件禁用查询,同时保持类型安全时非常有用。 + +> 重要提示:`useQuery` 返回的 `refetch` 无法与 `skipToken` 配合使用。除此之外,`skipToken` 的行为与 `enabled: false` 完全一致。 + +```vue + + + +``` diff --git a/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..4e9b3713a2e --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:09:32.012Z' +id: does-this-replace-client-state +title: '这会取代 [Vuex, Pinia] 吗?' +--- +首先,让我们明确几个关键点: + +- TanStack Query 是一个 **服务端状态 (server-state)** 库,负责管理服务端与客户端之间的异步操作 +- Vuex、Pinia、Zustand 等属于 **客户端状态 (client-state)** 库,它们_虽然可以存储异步数据,但与 TanStack Query 这类工具相比效率较低_ + +基于以上认知,简短的答案是:TanStack Query **会替换那些用于管理客户端状态中缓存数据的样板代码和相关逻辑,仅需寥寥数行代码即可实现相同功能**。 + +对于绝大多数应用而言,当你将所有异步代码迁移到 TanStack Query 后,真正需要**全局访问的客户端状态**通常所剩无几。 + +> 当然也存在例外情况,例如某些应用可能确实存在大量同步的纯客户端状态(比如可视化设计工具或音乐制作软件),这时你可能仍需使用客户端状态管理工具。需要注意的是,**TanStack Query 并非用于替代本地/客户端状态管理**。不过你可以毫无障碍地将其与大多数客户端状态管理工具配合使用。 + +## 示例分析 + +假设我们有以下通过全局状态库管理的"全局"状态: + +```tsx +const globalState = { + projects, + teams, + tasks, + users, + themeMode, + sidebarStatus, +} +``` + +当前全局状态管理器缓存了 4 类服务端状态:`projects`、`teams`、`tasks` 和 `users`。如果将这些服务端状态迁移到 TanStack Query,剩余的全局状态将简化为: + +```tsx +const globalState = { + themeMode, + sidebarStatus, +} +``` + +这意味着通过调用 `useQuery` 和 `useMutation` 等钩子函数,我们还可以移除所有用于管理服务端状态的样板代码,例如: + +- 连接器 (Connectors) +- 动作创建器 (Action Creators) +- 中间件 (Middlewares) +- 归约器 (Reducers) +- 加载中/错误/结果状态 (Loading/Error/Result states) +- 上下文 (Contexts) + +移除这些内容后,你可能会思考:**"为了这么少量的全局状态,是否还有必要继续使用客户端状态管理器?"** + +**这完全取决于你的选择!** + +但 TanStack Query 的定位非常明确:它能消除应用中异步逻辑的样板代码,仅用少量代码即可实现相同功能。 + +还在等什么?赶快尝试吧! diff --git a/docs/zh-hans/framework/vue/guides/filters.md b/docs/zh-hans/framework/vue/guides/filters.md new file mode 100644 index 00000000000..c5c70963123 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/filters.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:36:21.855Z' +id: filters +title: Filters +ref: docs/zh-hans/framework/react/guides/filters.md +--- + diff --git a/docs/zh-hans/framework/vue/guides/important-defaults.md b/docs/zh-hans/framework/vue/guides/important-defaults.md new file mode 100644 index 00000000000..9e7ef9880d1 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/important-defaults.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:36:21.808Z' +id: important-defaults +title: Important Defaults +ref: docs/zh-hans/framework/react/guides/important-defaults.md +--- + +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/vue/guides/infinite-queries.md b/docs/zh-hans/framework/vue/guides/infinite-queries.md new file mode 100644 index 00000000000..d249db3d8fe --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/infinite-queries.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:36:21.748Z' +id: infinite-queries +title: Infinite Queries +ref: docs/zh-hans/framework/react/guides/infinite-queries.md +--- + +[//]: # 'Example' + +```vue + + + +``` + +[//]: # 'Example' diff --git a/docs/zh-hans/framework/vue/guides/initial-query-data.md b/docs/zh-hans/framework/vue/guides/initial-query-data.md new file mode 100644 index 00000000000..eb8a9dcdc0d --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/initial-query-data.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:36:21.680Z' +id: initial-query-data +title: Initial Query Data +ref: docs/zh-hans/framework/react/guides/initial-query-data.md +--- + +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..f0a1c5148e3 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:03:43.156Z' +id: invalidations-from-mutations +title: 从变更中失效 +--- +## 变更触发的失效机制 + +使查询失效只是成功的一半,而了解**何时**使其失效则是另一半。通常,当应用中的某个变更 (mutation) 成功执行时,应用中极有可能存在与之相关的查询需要失效,甚至可能需要重新获取数据以反映该变更带来的新变化。 + +例如,假设我们有一个用于提交新待办事项的变更: + +```tsx +const mutation = useMutation({ mutationFn: postTodo }) +``` + +当 `postTodo` 变更成功执行后,我们通常希望所有 `todos` 查询都失效,并可能重新获取以显示新增的待办事项。为此,可以利用 `useMutation` 的 `onSuccess` 配置项和 `client` 的 `invalidateQueries` 方法: + +```tsx +import { useMutation, useQueryClient } from '@tanstack/vue-query' + +const queryClient = useQueryClient() + +// 当此变更成功时,使所有带有 `todos` 或 `reminders` 查询键的查询失效 +const mutation = useMutation({ + mutationFn: addTodo, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['todos'] }) + queryClient.invalidateQueries({ queryKey: ['reminders'] }) + }, +}) +``` + +你可以通过 [`useMutation` 钩子](./mutations.md) 提供的任意回调函数来配置失效逻辑。 diff --git a/docs/zh-hans/framework/vue/guides/migrating-to-v5.md b/docs/zh-hans/framework/vue/guides/migrating-to-v5.md new file mode 100644 index 00000000000..bfa4e1e6ff6 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/migrating-to-v5.md @@ -0,0 +1,358 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:08:27.091Z' +id: migrating-to-tanstack-query-5 +title: 迁移到 v5 +--- +## 重大变更 + +v5 是一个主要版本,需要注意以下破坏性变更: + +### 仅支持单一对象签名 + +`useQuery` 及其相关函数在 TypeScript 中曾有多种重载形式——即函数的不同调用方式。这不仅在类型维护上困难,还需要运行时检查第一和第二参数的类型以正确创建选项。 + +现在仅支持对象格式: + +```tsx +useQuery(key, fn, options) // [!code --] +useQuery({ queryKey, queryFn, ...options }) // [!code ++] +useInfiniteQuery(key, fn, options) // [!code --] +useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +useMutation(fn, options) // [!code --] +useMutation({ mutationFn, ...options }) // [!code ++] +useIsFetching(key, filters) // [!code --] +useIsFetching({ queryKey, ...filters }) // [!code ++] +useIsMutating(key, filters) // [!code --] +useIsMutating({ mutationKey, ...filters }) // [!code ++] +``` + +```tsx +queryClient.isFetching(key, filters) // [!code --] +queryClient.isFetching({ queryKey, ...filters }) // [!code ++] +queryClient.ensureQueryData(key, filters) // [!code --] +queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++] +queryClient.getQueriesData(key, filters) // [!code --] +queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++] +queryClient.setQueriesData(key, updater, filters, options) // [!code --] +queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++] +queryClient.removeQueries(key, filters) // [!code --] +queryClient.removeQueries({ queryKey, ...filters }) // [!code ++] +queryClient.resetQueries(key, filters, options) // [!code --] +queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.cancelQueries(key, filters, options) // [!code --] +queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.invalidateQueries(key, filters, options) // [!code --] +queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.refetchQueries(key, filters, options) // [!code --] +queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.fetchQuery(key, fn, options) // [!code --] +queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchQuery(key, fn, options) // [!code --] +queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.fetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +``` + +```tsx +queryCache.find(key, filters) // [!code --] +queryCache.find({ queryKey, ...filters }) // [!code ++] +queryCache.findAll(key, filters) // [!code --] +queryCache.findAll({ queryKey, ...filters }) // [!code ++] +``` + +### `queryClient.getQueryData` 现在仅接受 `queryKey` 作为参数 + +`queryClient.getQueryData` 的参数改为仅接受 `queryKey`: + +```tsx +queryClient.getQueryData(queryKey, filters) // [!code --] +queryClient.getQueryData(queryKey) // [!code ++] +``` + +### `queryClient.getQueryState` 现在仅接受 `queryKey` 作为参数 + +`queryClient.getQueryState` 的参数改为仅接受 `queryKey`: + +```tsx +queryClient.getQueryState(queryKey, filters) // [!code --] +queryClient.getQueryState(queryKey) // [!code ++] +``` + +#### 代码迁移工具 (Codemod) + +为了简化移除重载的迁移过程,v5 提供了一个代码迁移工具。 + +> 该工具会尽力帮助迁移破坏性变更,但请仔细检查生成的代码!此外,有些边缘情况无法通过工具检测,请留意控制台输出。 + +如需对 `.js` 或 `.jsx` 文件运行迁移工具,请使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +如需对 `.ts` 或 `.tsx` 文件运行迁移工具,请使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +注意:对于 `TypeScript`,必须使用 `tsx` 作为解析器,否则迁移工具可能无法正确应用! + +**注意:** 应用迁移工具可能会破坏代码格式,完成后请运行 `prettier` 和/或 `eslint`! + +关于迁移工具的工作原理: + +- 一般情况下,我们会检查第一个参数是否为包含 `queryKey` 或 `mutationKey` 属性的对象表达式(取决于正在转换的钩子/方法调用)。如果是,则代码已符合新签名,无需修改。🎉 +- 如果不满足上述条件,迁移工具会检查第一个参数是否为数组表达式或引用数组表达式的标识符。如果是,则将其放入对象表达式中作为第一个参数。 +- 如果可以推断对象参数,迁移工具会尝试将现有属性复制到新创建的对象中。 +- 如果无法推断用法,控制台会输出提示信息,包含文件名和行号,此时需要手动迁移。 +- 如果转换导致错误,控制台也会输出提示,请手动处理。 + +### `useQuery`(及 `QueryObserver`)的回调函数已被移除 + +`onSuccess`、`onError` 和 `onSettled` 已从查询中移除,但突变 (Mutation) 中仍保留。请参阅 [此 RFC](https://github.com/TanStack/query/discussions/5279) 了解变更动机及替代方案。 + +### `refetchInterval` 回调函数现在仅接收 `query` 参数 + +这统一了回调函数的调用方式(`refetchOnWindowFocus`、`refetchOnMount` 和 `refetchOnReconnect` 回调也仅接收 `query` 参数),并修复了当回调获取通过 `select` 转换的数据时的类型问题。 + +```tsx +- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --] ++ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++] +``` + +仍可通过 `query.state.data` 访问数据,但不会包含通过 `select` 转换后的数据。如需访问转换后的数据,可以对 `query.state.data` 再次调用转换函数。 + +### `useQuery` 中的 `remove` 方法已被移除 + +此前,`remove` 方法用于从 `queryCache` 中移除查询而不通知观察者。通常用于强制移除不再需要的数据,例如用户注销时。 + +但当查询仍处于活动状态时这样做意义不大,因为它会在下次重新渲染时触发硬加载状态。 + +如需移除查询,可使用 `queryClient.removeQueries({queryKey: key})`: + +```tsx +const queryClient = useQueryClient() +const query = useQuery({ queryKey, queryFn }) + +query.remove() // [!code --] +queryClient.removeQueries({ queryKey }) // [!code ++] +``` + +### 最低 TypeScript 版本要求提升至 4.7 + +主要因为修复了一个重要的类型推断问题。详见 [TypeScript 问题](https://github.com/microsoft/TypeScript/issues/43371)。 + +### `useQuery` 中的 `isDataEqual` 选项已被移除 + +此前,该函数用于指示是使用旧数据 (`true`) 还是新数据 (`false`) 作为查询的解析数据。 + +现在可以通过向 `structuralSharing` 传递函数实现相同功能: + +```tsx + import { replaceEqualDeep } from '@tanstack/react-query' + +- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --] ++ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++] +``` + +### 已弃用的自定义日志记录器已被移除 + +自定义日志记录器在 v4 中已弃用,并在本版本中移除。日志记录仅在开发模式下有效,而传递自定义日志记录器并非必要。 + +### 支持的浏览器 + +我们更新了 browserslist 以生成更现代、高效且体积更小的包。详见 [要求说明](../../installation#requirements)。 + +### 私有类字段和方法 + +TanStack Query 的类中一直有私有字段和方法,但它们并非真正的私有——仅在 TypeScript 中标记为私有。现在使用了 [ECMAScript 私有类特性](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields),这些字段在运行时也真正私有,无法从外部访问。 + +### 将 `cacheTime` 重命名为 `gcTime` + +很多人误解了 `cacheTime` 的含义。它听起来像是“数据缓存的时间”,但实际并非如此。 + +`cacheTime` 在查询仍在使用时不生效,仅在查询变为未使用后开始计时。时间到期后,数据会被“垃圾回收”以避免缓存无限增长。 + +`gc` 指“垃圾回收 (garbage collect)”时间。虽然更技术化,但这是计算机科学中 [广为人知的缩写]()。 + +```tsx +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, // [!code --] ++ gcTime: 10 * MINUTE, // [!code ++] + }, + }, +}) +``` + +### `useErrorBoundary` 选项已重命名为 `throwOnError` + +为了使 `useErrorBoundary` 选项更框架无关,并避免与 React 钩子的 `use` 前缀和“ErrorBoundary”组件名称混淆,现更名为 `throwOnError` 以更准确反映其功能。 + +### TypeScript:错误类型默认从 `unknown` 改为 `Error` + +虽然在 JavaScript 中可以抛出任何值(这使得 `unknown` 是最准确的类型),但大多数情况下抛出的都是 `Error`(或其子类)。这一变更使得在 TypeScript 中处理 `error` 字段更加方便。 + +如需抛出非 Error 类型的值,现在需要自行设置泛型: + +```ts +useQuery({ + queryKey: ['some-query'], + queryFn: async () => { + if (Math.random() > 0.5) { + throw 'some error' + } + return 42 + }, +}) +``` + +如需全局设置不同类型的错误,请参阅 [TypeScript 指南](../typescript.md#registering-a-global-error)。 + +### 移除了 `prefer-query-object-syntax` ESLint 规则 + +由于现在仅支持对象语法,此规则不再需要。 + +### 移除 `keepPreviousData`,改用 `placeholderData` 恒等函数 + +我们移除了 `keepPreviousData` 选项和 `isPreviousData` 标志,因为它们的功能与 `placeholderData` 和 `isPlaceholderData` 标志基本相同。 + +为实现与 `keepPreviousData` 相同的功能,我们在 `placeholderData` 中加入了前次查询的 `data` 作为参数,该参数接受一个恒等函数。因此只需向 `placeholderData` 提供恒等函数或使用 TanStack Query 内置的 `keepPreviousData` 函数即可。 + +> 注意:`useQueries` 不会在 `placeholderData` 函数中接收 `previousData` 参数,这是因为传入数组的查询具有动态性,可能导致占位数据与 `queryFn` 返回的结果形状不同。 + +```tsx +import { + useQuery, ++ keepPreviousData // [!code ++] +} from "@tanstack/react-query"; + +const { + data, +- isPreviousData, // [!code --] ++ isPlaceholderData, // [!code ++] +} = useQuery({ + queryKey, + queryFn, +- keepPreviousData: true, // [!code --] ++ placeholderData: keepPreviousData // [!code ++] +}); +``` + +在 TanStack Query 的上下文中,恒等函数指始终返回其输入参数(即数据)不变的函数。 + +```ts +useQuery({ + queryKey, + queryFn, + placeholderData: (previousData, previousQuery) => previousData, // 与 `keepPreviousData` 行为相同的恒等函数 +}) +``` + +但需要注意以下变更带来的影响: + +- `placeholderData` 会始终将查询置于 `success` 状态,而 `keepPreviousData` 会保留前次查询的状态。如果成功获取数据后后台刷新出错,状态可能是 `error`。但由于错误本身未被共享,我们决定保持 `placeholderData` 的行为。 +- `keepPreviousData` 会提供前次数据的 `dataUpdatedAt` 时间戳,而使用 `placeholderData` 时 `dataUpdatedAt` 保持为 `0`。如果需要在界面上持续显示该时间戳,可以通过 `useEffect` 解决: + + ```ts + const [updatedAt, setUpdatedAt] = useState(0) + + const { data, dataUpdatedAt } = useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + }) + + useEffect(() => { + if (dataUpdatedAt > updatedAt) { + setUpdatedAt(dataUpdatedAt) + } + }, [dataUpdatedAt]) + ``` + +### 窗口焦点重新获取不再监听 `focus` 事件 + +现在仅使用 `visibilitychange` 事件,因为我们仅支持支持该事件的浏览器。这修复了 [列出的诸多问题](https://github.com/TanStack/query/pull/4805)。 + +### 网络状态不再依赖 `navigator.onLine` 属性 + +`navigator.onLine` 在基于 Chromium 的浏览器中表现不佳,存在 [许多误报问题](https://bugs.chromium.org/p/chromium/issues/list?q=navigator.online),导致查询被错误标记为 `offline`。 + +为解决此问题,现在初始状态始终为 `online: true`,仅通过监听 `online` 和 `offline` 事件更新状态。 + +这应能减少误报,但对于通过 serviceWorkers 加载的离线应用,可能导致误报(因为这些应用即使没有网络连接也能工作)。 + +### 移除自定义 `context` 属性,改用自定义 `queryClient` 实例 + +在 v4 中,我们引入了向所有 react-query 钩子传递自定义 `context` 的功能,以实现微前端场景下的隔离。 + +但 `context` 是 React 特有的功能,其作用仅是让我们访问 `queryClient`。通过直接传递自定义 `queryClient` 可实现相同的隔离效果,同时使其他框架也能以框架无关的方式获得此功能。 + +```tsx +import { queryClient } from './my-client' + +const { data } = useQuery( + { + queryKey: ['users', id], + queryFn: () => fetch(...), +- context: customContext // [!code --] + }, ++ queryClient, // [!code ++] +) +``` + +### 移除 `refetchPage`,改用 `maxPages` + +在 v4 中,我们引入了通过 `refetchPage` 函数定义无限查询中需要重新获取的页面的功能。 + +但重新获取所有页面可能导致 UI 不一致。此外,该选项在如 `queryClient.refetchQueries` 中也可用,但仅对无限查询有效,对“普通”查询无效。 + +v5 为无限查询新增了 `maxPages` 选项,用于限制存储在查询数据中和重新获取的页面数量。这一新特性解决了最初为 `refetchPage` 设计的用例,同时避免了相关问题。 + +### 新的 `dehydrate` API + +`dehydrate` 的选项已简化。查询和突变始终会根据默认函数实现进行脱水。要改变此行为,可以使用新增的函数选项 `shouldDehydrateQuery` 或 `shouldDehydrateMutation` 替代已移除的布尔选项 `dehydrateMutations` 和 `dehydrateQueries`。如需完全不脱水查询/突变,可传入 `() => false`。 + +```tsx +- dehydrateMutations?: boolean // [!code --] +- dehydrateQueries?: boolean // [!code --] +``` + +### 无限查询现在需要 `initialPageParam` + +此前,我们向 `queryFn` 传递 `undefined` 作为 `pageParam`,并可在 `queryFn` 函数签名中为 `pageParam` 参数设置默认值。这导致在 `queryCache` 中存储了不可序列化的 `undefined`。 + +现在,必须向无限查询选项显式传递 `initialPageParam`,它将作为第一页的 `pageParam`: + +```tsx +useInfiniteQuery({ + queryKey, +- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --] ++ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++] ++ initialPageParam: 0, // [!code ++] + getNextPageParam: (lastPage) => lastPage.next, +}) +``` + +### 无限查询的手动模式已被移除 + +此前,我们允许通过直接向 `fetchNextPage` 或 `fetchPreviousPage` 传递 `pageParam` 值来覆盖 `getNextPageParam` 或 `getPreviousPageParam` 返回的 `pageParams`。此特性在重新获取时完全无效,且使用率不高。这也意味着无限查询现在必须提供 `getNextPageParam`。 + +### 从 `getNextPageParam` 或 `getPreviousPageParam` 返回 `null` 现在表示没有更多页面 + +在 v4 中,需要显式返回 `undefined` 表示没有更多页面。现在这一检查扩展至包含 `null`。 + +### 服务器端不再重试 + +在服务器端,`retry` 现在默认为 `0` 而非 `3`。对于预获取,我们一直默认不 diff --git a/docs/zh-hans/framework/vue/guides/mutations.md b/docs/zh-hans/framework/vue/guides/mutations.md new file mode 100644 index 00000000000..b03359859ab --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/mutations.md @@ -0,0 +1,304 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:11:21.322Z' +id: mutations +title: 变更 +--- +与查询不同,变更 (mutations) 通常用于创建/更新/删除数据或执行服务端副作用。为此,TanStack Query 导出了 `useMutation` 钩子。 + +以下是一个向服务器添加新待办事项的变更示例: + +```vue + + + +``` + +变更在任何时刻只能处于以下状态之一: + +- `isIdle` 或 `status === 'idle'` - 变更当前处于空闲或初始/重置状态 +- `isPending` 或 `status === 'pending'` - 变更正在执行中 +- `isError` 或 `status === 'error'` - 变更遇到错误 +- `isSuccess` 或 `status === 'success'` - 变更成功完成且数据可用 + +除了这些主要状态外,根据变更状态还可获取更多信息: + +- `error` - 如果变更处于 `error` 状态,可通过 `error` 属性获取错误信息 +- `data` - 如果变更处于 `success` 状态,可通过 `data` 属性获取数据 + +在上例中,您还看到可以通过调用 `mutate` 函数并传递**单个变量或对象**来向变更函数传递变量。 + +仅使用变量时,变更并不特别,但当与 `onSuccess` 选项、[Query Client 的 `invalidateQueries` 方法](../../../reference/QueryClient.md#queryclientinvalidatequeries) 和 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 结合使用时,变更就变成了非常强大的工具。 + +## 重置变更状态 + +有时您需要清除变更请求的 `error` 或 `data`。为此,可以使用 `reset` 函数处理: + +```vue + + + +``` + +## 变更副作用 + +`useMutation` 提供了一些辅助选项,允许在变更生命周期的任何阶段快速简单地执行副作用。这些选项对于[变更后使查询失效并重新获取](./invalidations-from-mutations.md) 甚至[乐观更新](./optimistic-updates.md) 都非常有用。 + +```tsx +useMutation({ + mutationFn: addTodo, + onMutate: (variables) => { + // 变更即将发生! + // 可选返回包含数据的上下文,例如用于回滚 + return { id: 1 } + }, + onError: (error, variables, context) => { + // 发生错误! + console.log(`回滚乐观更新,ID: ${context.id}`) + }, + onSuccess: (data, variables, context) => { + // 成功! + }, + onSettled: (data, error, variables, context) => { + // 无论错误还是成功都会执行! + }, +}) +``` + +在任何回调函数中返回 Promise 时,会先等待该 Promise 完成再调用下一个回调: + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: async () => { + console.log("我先执行!") + }, + onSettled: async () => { + console.log("我后执行!") + }, +}) +``` + +您可能希望在调用 `mutate` 时**触发额外的回调**,而不仅限于 `useMutation` 定义的那些。这可用于触发组件特定的副作用。为此,您可以在变更变量之后向 `mutate` 函数提供任何相同的回调选项。支持的选项包括:`onSuccess`、`onError` 和 `onSettled`。请注意,如果组件在变更完成前卸载,这些额外的回调将不会运行。 + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 我会先触发 + }, + onError: (error, variables, context) => { + // 我会先触发 + }, + onSettled: (data, error, variables, context) => { + // 我会先触发 + }, +}) + +mutate(todo, { + onSuccess: (data, variables, context) => { + // 我会后触发! + }, + onError: (error, variables, context) => { + // 我会后触发! + }, + onSettled: (data, error, variables, context) => { + // 我会后触发! + }, +}) +``` + +### 连续变更 + +在处理连续变更时,`onSuccess`、`onError` 和 `onSettled` 回调的行为略有不同。当传递给 `mutate` 函数时,它们只会触发一次,并且仅在组件仍挂载时触发。这是因为每次调用 `mutate` 函数时,变更观察者都会被移除并重新订阅。相反,`useMutation` 的处理程序会为每个 `mutate` 调用执行。 + +> 请注意,传递给 `useMutation` 的 `mutationFn` 很可能是异步的。在这种情况下,变更完成的顺序可能与 `mutate` 函数调用的顺序不同。 + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 会被调用3次 + }, +}) + +const todos = ['待办1', '待办2', '待办3'] +todos.forEach((todo) => { + mutate(todo, { + onSuccess: (data, variables, context) => { + // 只会执行一次,针对最后一个变更(待办3), + // 无论哪个变更先完成 + }, + }) +}) +``` + +## Promise + +使用 `mutateAsync` 替代 `mutate` 可以获取一个 Promise,该 Promise 在成功时解析或在错误时抛出。例如,这可用于组合副作用。 + +```tsx +const mutation = useMutation({ mutationFn: addTodo }) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('完成') +} +``` + +## 重试 + +默认情况下,TanStack Query 不会在错误时重试变更,但可以通过 `retry` 选项启用: + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + retry: 3, +}) +``` + +如果因设备离线导致变更失败,它们将在设备重新连接时按相同顺序重试。 + +## 持久化变更 + +如果需要,可以将变更持久化到存储中,并在以后恢复。这可以通过水合函数实现: + +```tsx +const queryClient = new QueryClient() + +// 定义"addTodo"变更 +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables) => { + // 取消当前的待办列表查询 + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 创建乐观待办事项 + const optimisticTodo = { id: uuid(), title: variables.title } + + // 将乐观待办事项添加到待办列表 + queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // 返回包含乐观待办事项的上下文 + return { optimisticTodo } + }, + onSuccess: (result, variables, context) => { + // 用结果替换待办列表中的乐观待办事项 + queryClient.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === context.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, context) => { + // 从待办列表中移除乐观待办事项 + queryClient.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== context.optimisticTodo.id), + ) + }, + retry: 3, +}) + +// 在某个组件中启动变更: +const mutation = useMutation({ mutationKey: ['addTodo'] }) +mutation.mutate({ title: '标题' }) + +// 如果变更因设备离线等原因被暂停, +// 可以在应用退出时将暂停的变更脱水: +const state = dehydrate(queryClient) + +// 然后可以在应用启动时再次水合: +hydrate(queryClient, state) + +// 恢复暂停的变更: +queryClient.resumePausedMutations() +``` + +### 持久化离线变更 + +如果使用 [persistQueryClient 插件](../plugins/persistQueryClient.md) 持久化离线变更,除非提供默认的变更函数,否则在页面重新加载时无法恢复变更。 + +这是一个技术限制。当持久化到外部存储时,只有变更的状态会被持久化,因为函数无法被序列化。水合后,触发变更的组件可能未挂载,因此调用 `resumePausedMutations` 可能会产生错误:`未找到 mutationFn`。 + +```js +const client = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24小时 + }, + }, +}) + +// 需要一个默认的变更函数,以便页面重新加载后可以恢复暂停的变更 +queryClient.setMutationDefaults({ + mutationKey: ['todos'], + mutationFn: ({ id, data }) => { + return api.updateTodo(id, data) + }, +}) + +const vueQueryOptions: VueQueryPluginOptions = { + queryClient: client, + clientPersister: (queryClient) => { + return persistQueryClient({ + queryClient, + persister: createSyncStoragePersister({ storage: localStorage }), + }) + }, + clientPersisterOnSuccess: (queryClient) => { + queryClient.resumePausedMutations() + }, +} + +createApp(App).use(VueQueryPlugin, vueQueryOptions).mount('#app') +``` + +我们还提供了一个全面的[离线示例](../examples/react/offline),涵盖了查询和变更。 + +## 变更作用域 + +默认情况下,所有变更并行运行 - 即使您多次调用同一变更的 `.mutate()`。可以通过为变更指定带有 `id` 的 `scope` 来避免这种情况。具有相同 `scope.id` 的所有变更将串行运行,这意味着当它们被触发时,如果该作用域已有变更在进行中,它们将以 `isPaused: true` 状态开始。它们将被放入队列,并在轮到它们时自动恢复。 + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +}) +``` diff --git a/docs/zh-hans/framework/vue/guides/network-mode.md b/docs/zh-hans/framework/vue/guides/network-mode.md new file mode 100644 index 00000000000..fddebf372a9 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/network-mode.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:30:13.133Z' +id: network-mode +title: Network Mode +ref: docs/zh-hans/framework/react/guides/network-mode.md +--- + diff --git a/docs/zh-hans/framework/vue/guides/optimistic-updates.md b/docs/zh-hans/framework/vue/guides/optimistic-updates.md new file mode 100644 index 00000000000..f1dc4b087f2 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/optimistic-updates.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:30:13.072Z' +id: optimistic-updates +title: Optimistic Updates +ref: docs/zh-hans/framework/react/guides/optimistic-updates.md +replace: + React: Vue +--- + diff --git a/docs/zh-hans/framework/vue/guides/paginated-queries.md b/docs/zh-hans/framework/vue/guides/paginated-queries.md new file mode 100644 index 00000000000..a5102e5ff14 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/paginated-queries.md @@ -0,0 +1,78 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:11:58.853Z' +id: paginated-queries +title: 分页查询 +--- +分页/滞后查询 + +渲染分页数据是一种非常常见的 UI 模式,在 TanStack Query 中,只需将页码信息包含在查询键 (query key) 中即可"开箱即用": + +```tsx +const result = useQuery({ + queryKey: ['projects', page], + queryFn: fetchProjects, +}) +``` + +然而,当你运行这个简单示例时,可能会注意到一个奇怪的现象: + +**UI 会在 `success` 和 `pending` 状态之间来回跳转,因为每个新页面都被视为一个全新的查询。** + +这种体验并不理想,但不幸的是,这正是当今许多工具的工作方式。但 TanStack Query 不同!正如你可能猜到的,TanStack Query 提供了一个名为 `placeholderData` 的强大功能来解决这个问题。 + +## 使用 `placeholderData` 实现更好的分页查询 + +考虑以下示例,我们理想情况下希望递增查询的 pageIndex(或游标)。如果使用 `useQuery`,**技术上仍然可以正常工作**,但随着为每个页面或游标创建和销毁不同的查询,UI 仍会在 `success` 和 `pending` 状态之间跳转。通过将 `placeholderData` 设置为 `(previousData) => previousData` 或使用 TanStack Query 导出的 `keepPreviousData` 函数,我们可以获得以下新特性: + +- **即使查询键 (query key) 已更改,在请求新数据时仍可访问上次成功获取的数据** +- 当新数据到达时,会无缝切换之前的 `data` 以显示新数据 +- 可通过 `isPlaceholderData` 了解查询当前提供的数据类型 + +```vue + + + +``` + +## 使用 `placeholderData` 实现滞后无限查询结果 + +虽然不太常见,但 `placeholderData` 选项与 `useInfiniteQuery` 钩子也能完美配合,因此你可以无缝地让用户在无限查询键随时间变化时继续查看缓存数据。 diff --git a/docs/zh-hans/framework/vue/guides/parallel-queries.md b/docs/zh-hans/framework/vue/guides/parallel-queries.md new file mode 100644 index 00000000000..b4f327d5ddf --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/parallel-queries.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:04:00.136Z' +id: parallel-queries +title: 并行查询 +--- +"并行 (Parallel)" 查询是指同时执行多个查询以最大化数据获取的并发性。 + +## 手动并行查询 + +当并行查询的数量固定不变时,使用并行查询**无需额外操作**。只需并排使用任意数量的 TanStack Query 的 `useQuery` 和 `useInfiniteQuery` 钩子即可! + +```vue + +``` + +## 使用 `useQueries` 实现动态并行查询 + +如果需要在每次渲染时动态调整并行查询的数量,手动查询的方式将违反钩子规则。此时,TanStack Query 提供了 `useQueries` 钩子,可动态执行任意数量的并行查询。 + +`useQueries` 接收一个包含 **queries 键**的**配置对象**,该键的值是由**查询对象组成的数组**,并返回一个**查询结果数组**: + +```js +const users = computed(...) +const queries = computed(() => users.value.map(user => { + return { + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + } + }) +); +const userQueries = useQueries({queries: queries}) +``` diff --git a/docs/zh-hans/framework/vue/guides/placeholder-query-data.md b/docs/zh-hans/framework/vue/guides/placeholder-query-data.md new file mode 100644 index 00000000000..4e5aae4445e --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/placeholder-query-data.md @@ -0,0 +1,59 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:05:23.340Z' +id: placeholder-query-data +title: 占位查询数据 +--- +## 什么是占位数据? + +占位数据允许查询表现得好像它已经有数据一样,类似于 `initialData` 选项,但**这些数据不会持久化到缓存中**。这在以下场景中非常有用:当实际数据在后台获取时,你已经拥有足够的局部(或模拟)数据来成功渲染查询。 + +> 示例:一篇博客文章的查询可以从父级博客文章列表中提取“预览”数据,该列表仅包含标题和文章正文的一小段。你可能不希望将这些局部数据持久化到单个查询的结果中,但它对于尽快显示内容布局非常有用,而实际查询会继续获取完整对象。 + +有几种方式可以在需要之前为查询提供占位数据: + +- 声明式: + - 为查询提供 `placeholderData`,以便在缓存为空时预填充数据 +- 命令式: + - [使用 `queryClient` 和 `placeholderData` 选项预取或获取数据](./prefetching.md) + +当我们使用 `placeholderData` 时,查询不会处于 `pending` 状态——它会直接从 `success` 状态开始,因为我们有数据可以显示,即使这些数据只是“占位”数据。为了将其与“真实”数据区分开来,查询结果还会将 `isPlaceholderData` 标志设置为 `true`。 + +## 作为值的占位数据 + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData: placeholderTodos, +}) +``` + +## 作为函数的占位数据 + +`placeholderData` 也可以是一个函数,你可以通过它访问“先前”成功查询的数据和查询元信息。这在你想将一个查询的数据用作另一个查询的占位数据时非常有用。当查询键(QueryKey)发生变化时(例如从 `['todos', 1]` 变为 `['todos', 2]`),我们可以继续显示“旧”数据,而不必在数据从一个查询过渡到下一个查询时显示加载动画。更多信息请参阅[分页查询](./paginated-queries.md)。 + +```tsx +const result = useQuery({ + queryKey: ['todos', id], + queryFn: () => fetch(`/todos/${id}`), + placeholderData: (previousData, previousQuery) => previousData, +}) +``` + +### 从缓存中获取占位数据 + +在某些情况下,你可以从另一个查询的缓存结果中为当前查询提供占位数据。一个典型的例子是从博客文章列表查询的缓存数据中搜索文章的预览版本,然后将其用作单个文章查询的占位数据: + +```tsx +const result = useQuery({ + queryKey: ['blogPost', blogPostId], + queryFn: () => fetch(`/blogPosts/${blogPostId}`), + placeholderData: () => { + // 使用 'blogPosts' 查询中较小/预览版本的博客文章作为当前查询的占位数据 + return queryClient + .getQueryData(['blogPosts']) + ?.find((d) => d.id === blogPostId) + }, +}) +``` diff --git a/docs/zh-hans/framework/vue/guides/prefetching.md b/docs/zh-hans/framework/vue/guides/prefetching.md new file mode 100644 index 00000000000..9169feb507e --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/prefetching.md @@ -0,0 +1,50 @@ +--- +source-updated-at: '2024-03-03T09:42:51.000Z' +translation-updated-at: '2025-05-06T16:02:02.404Z' +id: prefetching +title: 预获取 +--- +## 预取 (Prefetching) + +如果足够幸运,您可能对用户即将执行的操作有充分了解,从而能在数据被实际需要之前预先获取!这种情况下,可以使用 `prefetchQuery` 方法预取查询结果并存入缓存: + +[//]: # 'ExamplePrefetching' + +```tsx +const prefetchTodos = async () => { + // 该查询的结果会像普通查询一样被缓存 + await queryClient.prefetchQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) +} +``` + +[//]: # 'ExamplePrefetching' + +- 如果该查询的**最新**数据已在缓存中,则不会发起请求 +- 如果传入了 `staleTime`(例如 `prefetchQuery({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })`)且数据已超过指定的 `staleTime` 时间,则会重新发起查询 +- 如果预取的查询没有对应的 `useQuery` 实例,则会在 `gcTime` 指定的时间后被删除并进行垃圾回收 + +## 预取无限查询 (Prefetching Infinite Queries) + +无限查询 (Infinite Queries) 可以像常规查询一样预取。默认情况下,只会预取查询的第一页数据并存储在给定的 QueryKey 下。如需预取多页数据,可使用 `pages` 选项,此时还需提供 `getNextPageParam` 函数: + +[//]: # 'ExampleInfiniteQuery' + +```tsx +const prefetchProjects = async () => { + // 该查询的结果会像普通查询一样被缓存 + await queryClient.prefetchInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + pages: 3, // 预取前 3 页数据 + }) +} +``` + +[//]: # 'ExampleInfiniteQuery' + +上述代码会按顺序尝试预取 3 页数据,并为每页执行 `getNextPageParam` 以确定下一页的预取参数。如果 `getNextPageParam` 返回 `undefined`,预取过程将停止。 diff --git a/docs/zh-hans/framework/vue/guides/queries.md b/docs/zh-hans/framework/vue/guides/queries.md new file mode 100644 index 00000000000..c9da8297f6f --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/queries.md @@ -0,0 +1,102 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T16:04:51.198Z' +id: queries +title: 查询 +--- +## 查询基础 + +查询是与**唯一键**绑定的、对异步数据源的声明式依赖。查询可用于任何基于 Promise 的方法(包括 GET 和 POST 方法)从服务器获取数据。如果你的方法会修改服务器数据,我们建议改用[变更](./mutations.md)。 + +要在组件或自定义钩子中订阅查询,至少需要调用 `useQuery` 钩子并传入: +- 该查询的**唯一键** +- 一个返回 Promise 的函数,该 Promise 会: + - 解析出数据,或 + - 抛出错误 + +```ts +import { useQuery } from '@tanstack/vue-query' + +const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +``` + +你提供的**唯一键**将在内部用于重新获取、缓存及在整个应用中共享查询。 + +`useQuery` 返回的查询结果包含所有与查询相关的信息,这些信息可用于模板渲染或其他数据处理场景: + +```tsx +const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +``` + +`result` 对象包含若干关键状态,理解这些状态对高效使用至关重要。查询在任意时刻只能处于以下一种状态: +- `isPending` 或 `status === 'pending'` - 查询尚无数据 +- `isError` 或 `status === 'error'` - 查询遇到错误 +- `isSuccess` 或 `status === 'success'` - 查询成功且数据可用 + +除了这些主要状态,根据查询状态还可获取更多信息: +- `error` - 若查询处于 `isError` 状态,可通过 `error` 属性获取错误对象 +- `data` - 若查询处于 `isSuccess` 状态,可通过 `data` 属性获取数据 +- `isFetching` - 在任何状态下,只要查询正在获取数据(包括后台重新获取),`isFetching` 都会为 `true` + +对于**大多数**查询,通常只需先检查 `isPending` 状态,再检查 `isError` 状态,最后即可认为数据已可用并渲染成功状态: + +```vue + + + +``` + +如果不习惯使用布尔值,也可以始终使用 `status` 状态: + +```vue + + + +``` + +如果你在访问 `data` 前已检查过 `pending` 和 `error` 状态,TypeScript 也会正确收窄 `data` 的类型。 + +### 获取状态 (FetchStatus) + +除了 `status` 字段外,你还会获得一个额外的 `fetchStatus` 属性,其可选值为: +- `fetchStatus === 'fetching'` - 查询正在获取数据 +- `fetchStatus === 'paused'` - 查询尝试获取数据但被暂停,详见[网络模式](./network-mode.md)指南 +- `fetchStatus === 'idle'` - 查询当前未进行任何操作 + +### 为何需要两种状态? + +后台重新获取和"过时但可用"逻辑使得 `status` 和 `fetchStatus` 的所有组合都可能出现。例如: +- 处于 `success` 状态的查询通常对应 `idle` 获取状态,但如果正在进行后台重新获取,则可能为 `fetching` +- 刚挂载且无数据的查询通常处于 `pending` 状态和 `fetching` 获取状态,但若无网络连接则可能为 `paused` + +因此要记住:查询可能处于 `pending` 状态但并未实际获取数据。简单来说: +- `status` 提供关于 `data` 的信息:我们是否有数据? +- `fetchStatus` 提供关于 `queryFn` 的信息:它是否正在执行? diff --git a/docs/zh-hans/framework/vue/guides/query-cancellation.md b/docs/zh-hans/framework/vue/guides/query-cancellation.md new file mode 100644 index 00000000000..4144deec281 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/query-cancellation.md @@ -0,0 +1,27 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:26:47.547Z' +id: query-cancellation +title: Query Cancellation +ref: docs/zh-hans/framework/react/guides/query-cancellation.md +--- + +[//]: # 'Example7' + +```ts +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, +}) + +const queryClient = useQueryClient() + +function onButtonClick() { + queryClient.cancelQueries({ queryKey: ['todos'] }) +} +``` + +[//]: # 'Example7' diff --git a/docs/zh-hans/framework/vue/guides/query-functions.md b/docs/zh-hans/framework/vue/guides/query-functions.md new file mode 100644 index 00000000000..287bb710f49 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/query-functions.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:26:47.492Z' +id: query-functions +title: Query Functions +ref: docs/zh-hans/framework/react/guides/query-functions.md +--- + +[//]: # 'Example4' + +```js +const result = useQuery({ + queryKey: ['todos', { status, page }], + queryFn: fetchTodoList, +}) + +// Access the key, status and page variables in your query function! +function fetchTodoList({ queryKey }) { + const [_key, { status, page }] = queryKey + return new Promise() +} +``` + +[//]: # 'Example4' diff --git a/docs/zh-hans/framework/vue/guides/query-invalidation.md b/docs/zh-hans/framework/vue/guides/query-invalidation.md new file mode 100644 index 00000000000..ec6824a59e9 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/query-invalidation.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:26:47.442Z' +id: query-invalidation +title: Query Invalidation +ref: docs/zh-hans/framework/react/guides/query-invalidation.md +replace: + react-query: vue-query +--- + diff --git a/docs/zh-hans/framework/vue/guides/query-keys.md b/docs/zh-hans/framework/vue/guides/query-keys.md new file mode 100644 index 00000000000..633c261d942 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/query-keys.md @@ -0,0 +1,21 @@ +--- +source-updated-at: '2024-12-03T07:43:25.000Z' +translation-updated-at: '2025-05-06T05:26:47.388Z' +id: query-keys +title: Query Keys +ref: docs/zh-hans/framework/react/guides/query-keys.md +--- + +[//]: # 'Example5' + +```js +function useTodos(todoId) { + const queryKey = ['todos', todoId] + return useQuery({ + queryKey, + queryFn: () => fetchTodoById(todoId.value), + }) +} +``` + +[//]: # 'Example5' diff --git a/docs/zh-hans/framework/vue/guides/query-options.md b/docs/zh-hans/framework/vue/guides/query-options.md new file mode 100644 index 00000000000..4b0a057303b --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/query-options.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:26:47.347Z' +id: query-options +title: Query Options +ref: docs/zh-hans/framework/react/guides/query-options.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/guides/query-retries.md b/docs/zh-hans/framework/vue/guides/query-retries.md new file mode 100644 index 00000000000..12f6cb60c9b --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/query-retries.md @@ -0,0 +1,60 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:28:59.848Z' +id: query-retries +title: 查询重试 +--- +当 `useQuery` 查询失败(查询函数抛出错误)时,如果该查询的请求未达到最大连续重试次数(默认为 `3`)或提供了判断是否允许重试的函数,TanStack Query 会自动重试该查询。 + +您可以在全局级别和单个查询级别配置重试行为: + +- 设置 `retry = false` 将禁用重试。 +- 设置 `retry = 6` 会在显示函数抛出的最终错误前重试失败请求 6 次。 +- 设置 `retry = true` 会无限重试失败请求。 +- 设置 `retry = (failureCount, error) => ...` 允许根据请求失败原因自定义逻辑。 + +> 在服务端,重试默认值为 `0` 以确保服务端渲染尽可能快速。 + +```tsx +import { useQuery } from '@tanstack/vue-query' + +// 让特定查询重试指定次数 +const result = useQuery({ + queryKey: ['todos', 1], + queryFn: fetchTodoListPage, + retry: 10, // 将在显示错误前重试失败请求 10 次 +}) +``` + +> 提示:在最后一次重试尝试前,`error` 属性的内容将作为 `failureReason` 响应属性存在于 `useQuery` 中。因此在上例中,前 9 次重试尝试(共 10 次)的任何错误内容都将属于 `failureReason` 属性,最终如果所有重试后错误仍存在,它们将在最后一次尝试后成为 `error` 的一部分。 + +## 重试延迟 + +默认情况下,TanStack Query 不会在请求失败后立即重试。按照标准做法,每次重试尝试会逐渐增加退避延迟。 + +默认 `retryDelay` 设置为每次尝试翻倍(从 `1000` 毫秒开始),但不超过 30 秒: + +```ts +import { VueQueryPlugin } from '@tanstack/vue-query' + +const vueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, + }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +虽然不推荐,但您显然可以在插件和单个查询选项中覆盖 `retryDelay` 函数/整数值。如果设置为整数而非函数,延迟时间将始终保持不变: + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + retryDelay: 1000, // 无论重试多少次,总是等待 1000 毫秒进行重试 +}) +``` diff --git a/docs/zh-hans/framework/vue/guides/scroll-restoration.md b/docs/zh-hans/framework/vue/guides/scroll-restoration.md new file mode 100644 index 00000000000..a293cfd8c06 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/scroll-restoration.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:47.624Z' +id: scroll-restoration +title: Scroll Restoration +ref: docs/zh-hans/framework/react/guides/scroll-restoration.md +--- + diff --git a/docs/zh-hans/framework/vue/guides/ssr.md b/docs/zh-hans/framework/vue/guides/ssr.md new file mode 100644 index 00000000000..da4dfadf9ca --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/ssr.md @@ -0,0 +1,259 @@ +--- +source-updated-at: '2024-09-01T21:12:58.000Z' +translation-updated-at: '2025-05-06T05:30:13.009Z' +id: ssr +title: SSR 与 Nuxt +--- +Vue Query 支持在服务端预取多个查询,并将这些查询 _脱水 (dehydrate)_ 到 queryClient 中。这意味着服务端可以预先渲染页面加载时立即可用的标记,一旦 JS 可用,Vue Query 就能用库的全部功能来升级或 _水合 (hydrate)_ 这些查询。包括在客户端重新获取那些自服务端渲染后已过时的查询。 + +## 使用 Nuxt.js + +### Nuxt 3 + +首先在 `plugins` 目录下创建 `vue-query.ts` 文件,内容如下: + +```ts +import type { + DehydratedState, + VueQueryPluginOptions, +} from '@tanstack/vue-query' +import { + VueQueryPlugin, + QueryClient, + hydrate, + dehydrate, +} from '@tanstack/vue-query' +// Nuxt 3 app aliases +import { defineNuxtPlugin, useState } from '#imports' + +export default defineNuxtPlugin((nuxt) => { + const vueQueryState = useState('vue-query') + + // Modify your Vue Query global settings here + const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: 5000 } }, + }) + const options: VueQueryPluginOptions = { queryClient } + + nuxt.vueApp.use(VueQueryPlugin, options) + + if (import.meta.server) { + nuxt.hooks.hook('app:rendered', () => { + vueQueryState.value = dehydrate(queryClient) + }) + } + + if (import.meta.client) { + hydrate(queryClient, vueQueryState.value) + } +}) +``` + +现在你可以使用 `onServerPrefetch` 在页面中预取数据了。 + +- 使用 `queryClient.prefetchQuery` 或 `suspense` 预取所有需要的查询 + +```ts +export default defineComponent({ + setup() { + const { data, suspense } = useQuery({ + queryKey: ['test'], + queryFn: fetcher, + }) + + onServerPrefetch(async () => { + await suspense() + }) + + return { data } + }, +}) +``` + +### Nuxt 2 + +首先在 `plugins` 目录下创建 `vue-query.js` 文件,内容如下: + +```js +import Vue from 'vue' +import { VueQueryPlugin, QueryClient, hydrate } from '@tanstack/vue-query' + +export default (context) => { + // Modify your Vue Query global settings here + const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: 5000 } }, + }) + + if (process.server) { + context.ssrContext.VueQuery = queryClient + } + + if (process.client) { + Vue.use(VueQueryPlugin, { queryClient }) + + if (context.nuxtState && context.nuxtState.vueQueryState) { + hydrate(queryClient, context.nuxtState.vueQueryState) + } + } +} +``` + +将此插件添加到 `nuxt.config.js` 中: + +```js +module.exports = { + ... + plugins: ['~/plugins/vue-query.js'], +} +``` + +现在你可以使用 `onServerPrefetch` 在页面中预取数据了。 + +- 使用 `useContext` 获取 nuxt 上下文 +- 使用 `useQueryClient` 获取服务端 `queryClient` 实例 +- 使用 `queryClient.prefetchQuery` 或 `suspense` 预取所有需要的查询 +- 将 `queryClient` 脱水到 `nuxtContext` + +```vue +// pages/todos.vue + + + +``` + +如示例所示,可以预取部分查询而让其他查询在 queryClient 上获取。这意味着你可以通过添加或移除特定查询的 `prefetchQuery` 或 `suspense` 来控制服务端渲染的内容。 + +## 使用 Vite SSR + +将 VueQuery 客户端状态与 [vite-ssr](https://github.com/frandiox/vite-ssr) 同步,以便在 DOM 中序列化: + +```js +// main.js (entry point) +import App from './App.vue' +import viteSSR from 'vite-ssr/vue' +import { + QueryClient, + VueQueryPlugin, + hydrate, + dehydrate, +} from '@tanstack/vue-query' + +export default viteSSR(App, { routes: [] }, ({ app, initialState }) => { + // -- This is Vite SSR main hook, which is called once per request + + // Create a fresh VueQuery client + const queryClient = new QueryClient() + + // Sync initialState with the client state + if (import.meta.env.SSR) { + // Indicate how to access and serialize VueQuery state during SSR + initialState.vueQueryState = { toJSON: () => dehydrate(queryClient) } + } else { + // Reuse the existing state in the browser + hydrate(queryClient, initialState.vueQueryState) + } + + // Mount and provide the client to the app components + app.use(VueQueryPlugin, { queryClient }) +}) +``` + +然后,使用 Vue 的 `onServerPrefetch` 在任何组件中调用 VueQuery: + +```html + + + + +``` + +## 技巧、窍门与注意事项 + +### 只有成功的查询会被包含在脱水过程中 + +任何出错的查询会自动排除在脱水过程外。这意味着默认行为是假装这些查询从未在服务端加载过,通常会显示加载状态,并在 queryClient 上重试查询。无论错误如何都会发生这种情况。 + +有时这种行为并不理想,可能你希望在特定错误或查询时渲染带有正确状态码的错误页面。在这些情况下,使用 `fetchQuery` 并捕获错误来手动处理。 + +### 过时性是从查询在服务端获取时开始计算的 + +查询是否过时取决于其 `dataUpdatedAt` 时间。需要注意的是,服务端需要有正确的时间才能正常工作,但使用的是 UTC 时间,因此时区不影响。 + +由于 `staleTime` 默认为 `0`,默认情况下查询会在页面加载时在后台重新获取。你可能希望使用更高的 `staleTime` 来避免这种双重获取,特别是在没有缓存标记时。 + +这种过时查询的重新获取非常适合在 CDN 中缓存标记!可以将页面本身的缓存时间设置得较高以避免在服务端重新渲染页面,但将查询的 `staleTime` 设置得较低以确保数据在用户访问页面时立即在后台重新获取。也许你想将页面缓存一周,但如果数据超过一天就在页面加载时自动重新获取? + +### 服务端高内存消耗 + +如果为每个请求创建 `QueryClient`,Vue Query 会为该客户端创建独立的缓存,该缓存在 `gcTime` 期间保留在内存中。如果在该期间内有大量请求,可能会导致服务端内存消耗过高。 + +在服务端,`gcTime` 默认为 `Infinity`,这会禁用手动垃圾回收,并在请求完成后自动清除内存。如果显式设置了非 Infinity 的 `gcTime`,则需要负责提前清除缓存。 + +为了在不再需要时清除缓存并降低内存消耗,可以在请求处理完毕且脱水状态已发送到客户端后调用 [`queryClient.clear()`](../../../../reference/QueryClient/#queryclientclear)。 + +或者,可以设置较小的 `gcTime`。 diff --git a/docs/zh-hans/framework/vue/guides/suspense.md b/docs/zh-hans/framework/vue/guides/suspense.md new file mode 100644 index 00000000000..acd0cab04d7 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/suspense.md @@ -0,0 +1,55 @@ +--- +source-updated-at: '2024-04-02T22:46:31.000Z' +translation-updated-at: '2025-05-06T05:26:47.297Z' +id: suspense +title: Suspense +--- +> 注意:Vue Query 的 Suspense 模式目前属于实验性功能,与 Vue 自身的 Suspense 特性相同。这些 API 将会发生变化,除非您将 Vue 和 Vue Query 的版本锁定为相互兼容的补丁级别版本,否则不应在生产环境中使用。 + +Vue Query 也可以与 Vue 的新 [Suspense](https://vuejs.org/guide/built-ins/suspense.html) API 结合使用。 + +为此,您需要使用 Vue 提供的 `Suspense` 组件包裹可挂起 (suspendable) 的组件: + +```vue + + + +``` + +并将可挂起组件中的 `setup` 函数改为 `async` 形式。随后您就可以使用 `vue-query` 提供的异步 `suspense` 函数: + +```vue + +``` + +## 渲染时获取 (Fetch-on-render) vs 随取随渲 (Render-as-you-fetch) + +默认情况下,Vue Query 在 `suspense` 模式下无需额外配置即可作为**渲染时获取 (Fetch-on-render)** 解决方案完美工作。这意味着当您的组件尝试挂载时,它们会触发查询获取并挂起,但只有在您导入并挂载它们之后才会发生。如果您想进一步提升并实现**随取随渲 (Render-as-you-fetch)** 模式,我们建议在路由回调和/或用户交互事件上实现[预取 (Prefetching)](../prefetching),以便在组件挂载前——甚至在其父组件开始导入或挂载之前——就开始加载查询。 diff --git a/docs/zh-hans/framework/vue/guides/testing.md b/docs/zh-hans/framework/vue/guides/testing.md new file mode 100644 index 00000000000..c1a846b8e21 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/testing.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-06T05:25:27.162Z' +id: testing +title: Testing +--- + diff --git a/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md b/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md new file mode 100644 index 00000000000..d73056df6b2 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:27.117Z' +id: updates-from-mutation-responses +title: Updates from Mutation Responses +ref: docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md +--- + diff --git a/docs/zh-hans/framework/vue/guides/window-focus-refetching.md b/docs/zh-hans/framework/vue/guides/window-focus-refetching.md new file mode 100644 index 00000000000..efb02178964 --- /dev/null +++ b/docs/zh-hans/framework/vue/guides/window-focus-refetching.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:27.001Z' +id: window-focus-refetching +title: Window Focus Refetching +ref: docs/zh-hans/framework/react/guides/window-focus-refetching.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + +[//]: # 'Example' + +```js +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +[//]: # 'Example' +[//]: # 'ReactNative' +[//]: # 'ReactNative' diff --git a/docs/zh-hans/framework/vue/installation.md b/docs/zh-hans/framework/vue/installation.md new file mode 100644 index 00000000000..df21fe9b53e --- /dev/null +++ b/docs/zh-hans/framework/vue/installation.md @@ -0,0 +1,70 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-06T05:25:47.547Z' +id: installation +title: 安装 +--- +## 安装 + +您可以通过 [NPM](https://npmjs.com) 安装 Vue Query。 + +### NPM + +```bash +npm i @tanstack/vue-query +``` + +或 + +```bash +pnpm add @tanstack/vue-query +``` + +或 + +```bash +yarn add @tanstack/vue-query +``` + +或 + +```bash +bun add @tanstack/vue-query +``` + +> 想在下载前先体验一下?试试这个 [基础示例](../examples/basic) 吧! + +Vue Query 兼容 Vue 2.x 和 3.x 版本 + +> 如果您使用的是 Vue 2.6,请确保同时安装 [@vue/composition-api](https://github.com/vuejs/composition-api) + +### Vue Query 初始化 + +在使用 Vue Query 之前,您需要通过 `VueQueryPlugin` 进行初始化 + +```tsx +import { VueQueryPlugin } from '@tanstack/vue-query' + +app.use(VueQueryPlugin) +``` + +### 在 ` + + +``` diff --git a/docs/zh-hans/framework/vue/overview.md b/docs/zh-hans/framework/vue/overview.md new file mode 100644 index 00000000000..c971dce1999 --- /dev/null +++ b/docs/zh-hans/framework/vue/overview.md @@ -0,0 +1,46 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-06T05:30:39.298Z' +id: overview +title: 概述 +--- +TanStack Query(曾用名 Vue Query)常被称作 Web 应用缺失的数据获取库,用更专业的术语来说,它能让你在 Web 应用中轻松实现**获取、缓存、同步和更新服务端状态 (server state)**。 + +## 动机 + +大多数核心 Web 框架**并未**提供一套完整的数据获取或更新方案。因此开发者要么构建封装了严格数据获取规范的元框架 (meta-frameworks),要么自行发明数据获取方式。这通常意味着拼凑基于组件的状态和副作用,或是使用更通用的状态管理库来存储和提供异步数据。 + +虽然传统状态管理库擅长处理客户端状态 (client state),但**对异步或服务端状态的处理却不尽如人意**。这是因为**服务端状态截然不同**。首先,服务端状态: + +- 持久化存储在不受你控制或拥有的远程位置 +- 需要通过异步 API 进行获取和更新 +- 存在共享所有权,可能被他人未经通知就修改 +- 稍不注意就可能在你的应用中变成"过期数据" + +当你理解了应用中服务端状态的本质后,**更多挑战会接踵而至**,例如: + +- 缓存处理...(可能是编程中最难实现的部分) +- 将重复请求合并为单个请求 (deduping) +- 在后台静默更新"过期数据" +- 准确判断数据何时"过期" +- 尽可能快地反映数据更新 +- 分页和懒加载等性能优化 +- 管理服务端状态的内存和垃圾回收 +- 通过结构共享 (structural sharing) 记忆化查询结果 + +如果你没有被这个清单吓到,那说明你已经解决了所有服务端状态问题,值得颁奖表彰。但如果你和大多数人一样,这些挑战要么尚未全部攻克,要么才刚触及皮毛! + +TanStack Query 无疑是管理服务端状态的最佳库之一。它具备**开箱即用的零配置体验**,并能随着应用增长按需定制。 + +TanStack Query 让你能攻克_服务端状态_的棘手难题,在数据反客为主之前掌控全局。 + +从技术角度而言,TanStack Query 能: + +- 帮你删除应用中**大量**复杂难懂的代码,仅用寥寥数行 TanStack Query 逻辑替代 +- 提升应用可维护性,轻松添加新功能而无需操心新数据源的接入 +- 让终端用户直接感受到应用前所未有的速度和响应性 +- 可能帮你节省带宽并提升内存性能 + +## 成功说服,接下来呢? + +- 通过我们极其详尽的[入门指南](../installation)和[API 参考](../reference/useQuery)按自己的节奏学习 Vue Query diff --git a/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md b/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md new file mode 100644 index 00000000000..f420483f605 --- /dev/null +++ b/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:25:26.953Z' +id: broadcastQueryClient +title: broadcastQueryClient (Experimental) +ref: docs/zh-hans/framework/react/plugins/broadcastQueryClient.md +replace: + react-query: vue-query +--- + diff --git a/docs/zh-hans/framework/vue/plugins/createPersister.md b/docs/zh-hans/framework/vue/plugins/createPersister.md new file mode 100644 index 00000000000..edb7608faca --- /dev/null +++ b/docs/zh-hans/framework/vue/plugins/createPersister.md @@ -0,0 +1,134 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:31:54.871Z' +id: createPersister +title: createPersister (实验性) +--- +## 安装 + +该工具作为一个独立包提供,可通过 `'@tanstack/query-persist-client-core'` 导入使用。 + +```bash +npm install @tanstack/query-persist-client-core +``` + +或 + +```bash +pnpm add @tanstack/query-persist-client-core +``` + +或 + +```bash +yarn add @tanstack/query-persist-client-core +``` + +或 + +```bash +bun add @tanstack/query-persist-client-core +``` + +## 使用方式 + +- 导入 `experimental_createPersister` 函数 +- 创建一个新的 `experimental_createPersister` + - 可传入任何符合 `AsyncStorage` 或 `Storage` 接口的 `storage` 对象 +- 将该 `persister` 作为选项传递给 Query。可通过两种方式实现: + - 传递给 `QueryClient` 的 `defaultOptions` + - 或传递给任意 `useQuery` 钩子实例 + - 若作为 `defaultOptions` 传递,所有查询将被持久化到指定的 `storage`。还可通过 `filters` 进一步筛选。与 `persistClient` 插件不同,此方式不会将整个 QueryClient 作为单个条目持久化,而是分别持久化每个查询,并使用查询哈希 (query hash) 作为键名 + - 若提供给单个 `useQuery` 钩子,则仅该查询会被持久化 + +这种方式无需存储整个 `QueryClient`,而是由您决定应用中哪些数据值得持久化。每个查询会按需恢复(首次使用时)和持久化(每次 `queryFn` 执行后),因此无需节流处理。恢复查询后仍会遵循 `staleTime` 设置——若数据被视为过期 (stale),恢复后将立即重新获取;若数据仍新鲜 (fresh),则不会执行 `queryFn`。 + +从内存中垃圾回收查询**不会**影响持久化数据。这意味着可以缩短查询在内存中的保留时间以提升**内存效率**,当下次使用时直接从持久化存储中恢复即可。 + +```tsx +import { QueryClient } from '@tanstack/vue-query' +import { experimental_createPersister } from '@tanstack/query-persist-client-core' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 30, // 30 秒 + persister: experimental_createPersister({ + storage: localStorage, + maxAge: 1000 * 60 * 60 * 12, // 12 小时 + }), + }, + }, +}) +``` + +### 适配的默认行为 + +`createPersister` 插件在技术上封装了 `queryFn`,因此若 `queryFn` 未执行则不会恢复数据。这种方式使其成为 Query 与网络之间的缓存层。因此,当使用持久化器时,`networkMode` 默认设为 `'offlineFirst'`,以确保即使无网络连接也能从持久化存储恢复数据。 + +## API + +### `experimental_createPersister` + +```tsx +experimental_createPersister(options: StoragePersisterOptions) +``` + +#### `配置项` + +```tsx +export interface StoragePersisterOptions { + /** 用于缓存数据存取操作的存储客户端。 + * SSR 场景请传入 `undefined`。 + */ + storage: AsyncStorage | Storage | undefined | null + /** + * 数据序列化方法。 + * @默认值 `JSON.stringify` + */ + serialize?: (persistedQuery: PersistedQuery) => string + /** + * 数据反序列化方法。 + * @默认值 `JSON.parse` + */ + deserialize?: (cachedString: string) => PersistedQuery + /** + * 用于强制使旧缓存失效的唯一字符串, + * 当与当前字符串不匹配时缓存将被丢弃 + */ + buster?: string + /** + * 缓存的最大允许存活时间(毫秒)。 + * 若发现持久化缓存超过此时限, + * 将被自动丢弃 + * @默认值 24 小时 + */ + maxAge?: number + /** + * 存储键名的前缀。 + * 存储键名由前缀和查询哈希组成,格式为 `prefix-queryHash`。 + */ + prefix?: string + /** + * 用于筛选需要持久化的查询。 + */ + filters?: QueryFilters +} + +interface AsyncStorage { + getItem: (key: string) => Promise + setItem: (key: string, value: string) => Promise + removeItem: (key: string) => Promise +} +``` + +默认配置为: + +```tsx +{ + prefix = 'tanstack-query', + maxAge = 1000 * 60 * 60 * 24, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` diff --git a/docs/zh-hans/framework/vue/quick-start.md b/docs/zh-hans/framework/vue/quick-start.md new file mode 100644 index 00000000000..5c7e1fbf20c --- /dev/null +++ b/docs/zh-hans/framework/vue/quick-start.md @@ -0,0 +1,56 @@ +--- +source-updated-at: '2024-04-02T22:46:31.000Z' +translation-updated-at: '2025-05-03T22:08:17.182Z' +id: quick-start +title: 快速开始 +--- +以下代码片段简要展示了 Vue Query 的 3 个核心概念: + +- [查询 (Queries)](./guides/queries.md) +- [变更 (Mutations)](./guides/mutations.md) +- [查询失效 (Query Invalidation)](./guides/query-invalidation.md) + +如需查看完整可运行的示例,请参考我们的 [基础 codesandbox 示例](../examples/basic) + +```vue + + + +``` + +这三个概念构成了 Vue Query 的核心功能。文档的后续章节将详细讲解每个核心概念。 diff --git a/docs/zh-hans/framework/vue/reactivity.md b/docs/zh-hans/framework/vue/reactivity.md new file mode 100644 index 00000000000..f5d73023afd --- /dev/null +++ b/docs/zh-hans/framework/vue/reactivity.md @@ -0,0 +1,163 @@ +--- +source-updated-at: '2025-02-23T08:40:17.000Z' +translation-updated-at: '2025-05-06T16:03:25.519Z' +id: reactivity +title: 响应式 +--- +Vue 采用 [信号范式 (the signals paradigm)](https://vuejs.org/guide/extras/reactivity-in-depth.html#connection-to-signals) 来处理和追踪响应式数据。该系统的核心特性是响应式系统仅会在特定监听的响应式属性上触发更新。因此需要确保当查询所依赖的值更新时,查询也能同步更新。 + +# 保持查询的响应性 + +当为查询创建组合式函数时,最初可能会这样编写: + +```ts +export function useUserProjects(userId: string) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(userId), + ); +} +``` + +使用时可能如下: + +```ts +// 响应式的用户 ID ref +const userId = ref('1') +// 获取用户 1 的项目数据 +const { data: projects } = useUserProjects(userId.value) + +const onChangeUser = (newUserId: string) => { + // 修改 userId 但查询不会重新触发 + userId.value = newUserId +} +``` + +这段代码无法按预期工作,因为直接从 `userId` ref 中提取了值。Vue-query 并未追踪 `userId` `ref`,因此无法感知值的变化。 + +解决方法很简单:必须在查询键 (query key) 中使值可追踪。直接在组合式函数中接收 `ref` 并将其放入查询键即可: + +```ts +export function useUserProjects(userId: Ref) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(userId.value), + ); +} +``` + +现在当 `userId` 变化时查询会自动重新获取数据: + +```ts +const onChangeUser = (newUserId: string) => { + // 查询会使用新用户 ID 重新获取数据! + userId.value = newUserId +} +``` + +在 Vue Query 中,查询键内的任何响应式属性都会被自动追踪变化。这使得当请求参数变化时,Vue-Query 能自动重新获取数据。 + +## 处理非响应式查询 + +虽然较为少见,但有时故意传递非响应式变量是合理的。例如某些实体只需获取一次无需追踪,或在变更后手动使查询失效。若使用上述自定义组合式函数,这种场景下的用法会显得不够直观: + +```ts +const { data: projects } = useUserProjects(ref('1')) +``` + +必须创建临时 `ref` 仅为了类型兼容。我们可以优化这一点,让组合式函数同时接受普通值和响应式值: + +```ts +export function useUserProjects(userId: MaybeRef) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(toValue(userId)), + ); +} +``` + +现在可以同时使用普通值和 ref: + +```ts +// 获取用户 1 的项目数据,userId 预期不会变化 +const { data: projects } = useUserProjects('1') + +// 获取用户 1 的项目数据,查询会响应 userId 变化 +const userId = ref('1') + +// 修改 userId... + +// 查询会根据 userId 变化重新获取数据 +const { data: projects } = useUserProjects(userId) +``` + +## 在查询中使用派生状态 + +从其他响应式状态派生新状态是很常见的需求。典型场景是处理组件 props。假设 `userId` 是传递给组件的 prop: + +```vue + +``` + +可能会直接在查询中使用 prop: + +```ts +// 不会响应 props.userId 的变化 +const { data: projects } = useUserProjects(props.userId) +``` + +但和第一个例子类似,这不具备响应性。访问 `reactive` 变量的属性会导致响应性丢失。可以通过 `computed` 使派生状态具备响应性: + +```ts +const userId = computed(() => props.userId) + +// 能响应 props.userId 的变化 +const { data: projects } = useUserProjects(userId) +``` + +虽然可行,但此方案并非最优解。除了引入中间变量外,还创建了实际不必要的记忆值。对于简单的属性访问场景,`computed` 的优化并无实质收益。更合适的方案是使用 [响应式 getter (reactive getters)](https://blog.vuejs.org/posts/vue-3-3#better-getter-support-with-toref-and-tovalue)。响应式 getter 是返回基于响应式状态值的函数,工作原理类似 `computed` 但不会记忆值,因此非常适合简单属性访问场景。 + +再次重构组合式函数,使其接受 `ref`、普通值或响应式 getter: + +```ts +export function useUserProjects(userId: MaybeRefOrGetter) { + ... +} +``` + +现在使用响应式 getter: + +```ts +// 响应 props.userId 变化,无需使用 `computed`! +const { data: projects } = useUserProjects(() => props.userId) +``` + +这种写法简洁且具备响应性,同时避免了不必要的记忆化开销。 + +## 其他可追踪的查询选项 + +上文仅涉及了一个追踪响应式依赖的查询选项。实际上除了 `queryKey` 外,`enabled` 也支持响应式值。这在需要基于派生状态控制查询获取时非常有用: + +```ts +export function useUserProjects(userId: MaybeRef) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(toValue(userId)), + enabled: () => userId.value === activeUserId.value, + ); +} +``` + +更多细节请参考 [useQuery 文档](./reference/useQuery.md)。 + +# 核心要点 + +- `enabled` 和 `queryKey` 是支持响应式值的两个查询选项 +- 传递查询选项时,应支持 Vue 的三种值类型:ref、普通值和响应式 getter +- 若需要查询响应所消费值的变化,确保这些值是响应式的(直接传入 ref 或使用响应式 getter) +- 若不需要查询具备响应性,直接传入普通值 +- 对于简单派生状态(如属性访问),考虑用响应式 getter 替代 `computed` diff --git a/docs/zh-hans/framework/vue/reference/hydration.md b/docs/zh-hans/framework/vue/reference/hydration.md new file mode 100644 index 00000000000..a704d965f2d --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/hydration.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:25:26.859Z' +id: hydration +title: hydration +ref: docs/zh-hans/framework/react/reference/hydration.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + +[//]: # 'HydrationBoundary' +[//]: # 'HydrationBoundary' diff --git a/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md b/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md new file mode 100644 index 00000000000..41ee67b18db --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:25:26.815Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +ref: docs/zh-hans/framework/react/reference/infiniteQueryOptions.md +--- + diff --git a/docs/zh-hans/framework/vue/reference/queryOptions.md b/docs/zh-hans/framework/vue/reference/queryOptions.md new file mode 100644 index 00000000000..872140ce201 --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/queryOptions.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.769Z' +id: queryOptions +title: queryOptions +ref: docs/zh-hans/framework/react/reference/queryOptions.md +--- + diff --git a/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md b/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md new file mode 100644 index 00000000000..642dffe1484 --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.716Z' +id: useInfiniteQuery +title: useInfiniteQuery +ref: docs/zh-hans/framework/react/reference/useInfiniteQuery.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useIsFetching.md b/docs/zh-hans/framework/vue/reference/useIsFetching.md new file mode 100644 index 00000000000..fbab79e3d3d --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useIsFetching.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.664Z' +id: useIsFetching +title: useIsFetching +ref: docs/zh-hans/framework/react/reference/useIsFetching.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useIsMutating.md b/docs/zh-hans/framework/vue/reference/useIsMutating.md new file mode 100644 index 00000000000..bf51bbb9bb8 --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useIsMutating.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.628Z' +id: useIsMutating +title: useIsMutating +ref: docs/zh-hans/framework/react/reference/useIsMutating.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useMutation.md b/docs/zh-hans/framework/vue/reference/useMutation.md new file mode 100644 index 00000000000..1e985cc125f --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useMutation.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.582Z' +id: useMutation +title: useMutation +ref: docs/zh-hans/framework/react/reference/useMutation.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useMutationState.md b/docs/zh-hans/framework/vue/reference/useMutationState.md new file mode 100644 index 00000000000..98ee639efc8 --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useMutationState.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.421Z' +id: useMutationState +title: useMutationState +ref: docs/zh-hans/framework/react/reference/useMutationState.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useQueries.md b/docs/zh-hans/framework/vue/reference/useQueries.md new file mode 100644 index 00000000000..a7e20c101fc --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useQueries.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.467Z' +id: useQueries +title: useQueries +ref: docs/zh-hans/framework/react/reference/useQueries.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useQuery.md b/docs/zh-hans/framework/vue/reference/useQuery.md new file mode 100644 index 00000000000..30ca4611741 --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useQuery.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.378Z' +id: useQuery +title: useQuery +ref: docs/zh-hans/framework/react/reference/useQuery.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/reference/useQueryClient.md b/docs/zh-hans/framework/vue/reference/useQueryClient.md new file mode 100644 index 00000000000..e5b9c505342 --- /dev/null +++ b/docs/zh-hans/framework/vue/reference/useQueryClient.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-06T05:25:26.511Z' +id: useQueryClient +title: useQueryClient +ref: docs/zh-hans/framework/react/reference/useQueryClient.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + diff --git a/docs/zh-hans/framework/vue/typescript.md b/docs/zh-hans/framework/vue/typescript.md new file mode 100644 index 00000000000..17387f2bd5c --- /dev/null +++ b/docs/zh-hans/framework/vue/typescript.md @@ -0,0 +1,118 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-06T05:28:24.242Z' +id: typescript +title: TypeScript +--- +Vue Query 现已采用 **TypeScript** 编写,确保库与您的项目具备类型安全! + +注意事项: + +- 当前类型系统要求使用 TypeScript **v4.7** 或更高版本 +- 本仓库中的类型变更视为**非破坏性变更**,通常以 **patch** 版本号发布(否则每个类型增强都会导致主版本号变更!) +- **强烈建议将 vue-query 包版本锁定到特定 patch 版本,并在升级时预见到类型可能在任意版本间被修复或升级** +- Vue Query 的非类型相关公共 API 仍严格遵循语义化版本规范。 + +## 类型推断 + +Vue Query 的类型通常能很好地自动流转,因此您无需自行添加类型注解 + +```tsx +const { data } = useQuery({ + // ^? const data: Ref | Ref + queryKey: ['test'], + queryFn: () => Promise.resolve(5), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUBNYRm8JABN6DInAC8KDNlx4AFAglw4nTocMA9APwG4Q7QGl0eAFxwA2lRjoWVALoAaa1t8ADFGFx0ASjUAPjgABXIQYAwAOigvCAAbbnQdAFYIgPFCCKA) + +```tsx +const { data } = useQuery({ + // ^? const data: Ref | Ref + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUBNYRm8JABN6DInAC8KDNlx4AFAglw4nTodNwAegH4DcIdoDS6PAC44AbSox0LKgF0ANDZ2+ABijK46AJRqAHxwAArkIMAYAHRQ3hAANtzoOgCskYHihhhZ6KwwEYoM0apxNfSpMBAAyjBQwIwA5lHFhJFAA) + +当您的 `queryFn` 具有明确定义的返回类型时效果最佳。请注意大多数数据获取库默认返回 `any` 类型,因此建议将其提取到具有正确定义的函数中: + +```tsx +const fetchGroups = (): Promise => + axios.get('/groups').then((response) => response.data) + +const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const data: Ref | Ref +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUKEiw49AB7AIqUuUpV5i1GPESYeMOjgBxcsjBwAvIjjAAJgC44jZCABGuIhImsIzeCXQYVgALEwgzZSsACgBKRwAFVWAMAB4wswBtAF0APksciThZBSUAOgBzQKiqTnLTMC0Y0phg9EYoqKh0VEhmdBj8uC6e3wxS23oGGK9xHz9rCYYiSxQMbFw8KKQhDYBpdDxHDKo68IaqLIAaOB38ADFGRwCg0PrlQmnxTk4i37gAPQA-EA) + +## 类型收窄 + +Vue Query 使用基于 `status` 字段和派生状态布尔值的[可辨识联合类型](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)作为查询结果。这允许您通过检查例如 `success` 状态来确保 `data` 已定义: + +```tsx +const { data, isSuccess } = reactive( + useQuery({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + }), +) + +if (isSuccess) { + data + // ^? const data: number +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUKEixEcKOnqsYwbuiKlylKr3RUA3BImsIzeEgAm9BgBo4wVAGVkrVulSp1AXjkKlK9AAUaFjCeAEA2lQwbjBUALq2AQCUcJ4AfHAACpr26AB08qgQADaqAQCsSVWGkiRwAfZOLm6oKQgScJ1wlgwSnJydAHoA-BKEEkA) + +## 错误字段类型标注 + +错误类型默认为 `Error`,因为这符合大多数用户的预期: + +```tsx +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: Ref + +if (error.value instanceof Error) { + error.value + // ^? const error: Error +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA) + +若需抛出自定义错误或非 `Error` 对象,可指定错误字段类型: + +但此方法存在缺点:`useQuery` 的其他泛型参数将无法自动推断。通常不建议抛出非 `Error` 对象,若您有类似 `AxiosError` 的子类,可通过*类型收窄*使错误字段更具体: + +### 注册全局错误类型 + +TanStack Query v5 允许通过扩展 `Register` 接口来设置全局错误类型,无需在调用处指定泛型参数。这将确保类型推断仍有效,同时错误字段会保持指定类型: + +## 查询与 Mutation Key 类型标注 + +### 注册查询与 Mutation Key 类型 + +类似于注册[全局错误类型](#registering-a-global-error),您也可以注册全局 `QueryKey` 和 `MutationKey` 类型。这允许您为键提供更符合应用层级结构的类型定义,并确保这些类型在整个库中保持一致。注意注册的类型必须扩展 `Array` 类型,以保持键的数组特性。 + +```ts +import '@tanstack/vue-query' + +type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray] + +declare module '@tanstack/vue-query' { + interface Register { + queryKey: QueryKey + mutationKey: QueryKey + } +} +``` + +## 使用 `skipToken` 实现类型安全的查询禁用 + +若使用 TypeScript,可通过 `skipToken` 禁用查询。这在需要根据条件禁用查询但仍需保持类型安全时非常有用。更多细节请参阅[禁用查询](./guides/disabling-queries.md)指南。 diff --git a/docs/zh-hans/reference/InfiniteQueryObserver.md b/docs/zh-hans/reference/InfiniteQueryObserver.md new file mode 100644 index 00000000000..73195a1f527 --- /dev/null +++ b/docs/zh-hans/reference/InfiniteQueryObserver.md @@ -0,0 +1,27 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-06T03:55:32.417Z' +id: InfiniteQueryObserver +title: InfiniteQueryObserver +--- +## `InfiniteQueryObserver` + +`InfiniteQueryObserver` 可用于观察和切换无限查询 (infinite queries)。 + +```tsx +const observer = new InfiniteQueryObserver(queryClient, { + queryKey: ['posts'], + queryFn: fetchPosts, + getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor, +}) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +**选项** + +`InfiniteQueryObserver` 的选项与 [`useInfiniteQuery`](../../framework/react/reference/useInfiniteQuery) 完全一致。 diff --git a/docs/zh-hans/reference/MutationCache.md b/docs/zh-hans/reference/MutationCache.md new file mode 100644 index 00000000000..a001e3f5f1c --- /dev/null +++ b/docs/zh-hans/reference/MutationCache.md @@ -0,0 +1,99 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-06T03:51:33.721Z' +id: MutationCache +title: MutationCache +--- +`MutationCache` 是用于存储变更 (mutations) 的容器。 + +**通常情况下,您不会直接与 MutationCache 交互,而是通过 `QueryClient` 进行操作。** + +```tsx +import { MutationCache } from '@tanstack/react-query' + +const mutationCache = new MutationCache({ + onError: (error) => { + console.log(error) + }, + onSuccess: (data) => { + console.log(data) + }, +}) +``` + +其提供的方法包括: + +- [`getAll`](#mutationcachegetall) +- [`subscribe`](#mutationcachesubscribe) +- [`clear`](#mutationcacheclear) + +**配置项** + +- `onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` + - 可选 + - 当某个变更操作发生错误时,此函数将被调用。 + - 如果返回 Promise,则会等待其执行完成 +- `onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` + - 可选 + - 当某个变更操作成功时,此函数将被调用。 + - 如果返回 Promise,则会等待其执行完成 +- `onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` + - 可选 + - 当某个变更操作完成(无论成功或失败)时,此函数将被调用。 + - 如果返回 Promise,则会等待其执行完成 +- `onMutate?: (variables: unknown, mutation: Mutation) => Promise | unknown` + - 可选 + - 在某个变更操作执行前,此函数将被调用。 + - 如果返回 Promise,则会等待其执行完成 + +## 全局回调 + +`MutationCache` 上的 `onError`、`onSuccess`、`onSettled` 和 `onMutate` 回调可用于全局处理这些事件。它们与提供给 `QueryClient` 的 `defaultOptions` 不同,原因在于: + +- `defaultOptions` 可以被每个 Mutation 覆盖 —— 而全局回调 **始终** 会被调用。 +- `onMutate` 不允许返回上下文值。 + +## `mutationCache.getAll` + +`getAll` 返回缓存中的所有变更操作。 + +> 注意:大多数应用通常不需要此方法,但在需要获取变更操作的更多信息时可能会派上用场 + +```tsx +const mutations = mutationCache.getAll() +``` + +**返回值** + +- `Mutation[]` + - 缓存中的变更操作实例 + +## `mutationCache.subscribe` + +`subscribe` 方法可用于订阅整个变更缓存,并在缓存发生安全/已知的更新(如变更状态更改或变更操作被更新、添加或删除)时收到通知。 + +```tsx +const callback = (event) => { + console.log(event.type, event.mutation) +} + +const unsubscribe = mutationCache.subscribe(callback) +``` + +**配置项** + +- `callback: (mutation?: MutationCacheNotifyEvent) => void` + - 每当缓存更新时,此函数将被调用并传入变更缓存事件。 + +**返回值** + +- `unsubscribe: Function => void` + - 此函数用于取消订阅变更缓存的回调。 + +## `mutationCache.clear` + +`clear` 方法可用于完全清空缓存并重新开始。 + +```tsx +mutationCache.clear() +``` diff --git a/docs/zh-hans/reference/QueriesObserver.md b/docs/zh-hans/reference/QueriesObserver.md new file mode 100644 index 00000000000..0fc4441e4f1 --- /dev/null +++ b/docs/zh-hans/reference/QueriesObserver.md @@ -0,0 +1,25 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-06T03:50:00.104Z' +id: QueriesObserver +title: QueriesObserver +--- +## `QueriesObserver` + +`QueriesObserver` 可用于观察多个查询。 + +```tsx +const observer = new QueriesObserver(queryClient, [ + { queryKey: ['post', 1], queryFn: fetchPost }, + { queryKey: ['post', 2], queryFn: fetchPost }, +]) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +**选项** + +`QueriesObserver` 的选项与 [`useQueries`](../../framework/react/reference/useQueries) 完全一致。 diff --git a/docs/zh-hans/reference/QueryCache.md b/docs/zh-hans/reference/QueryCache.md new file mode 100644 index 00000000000..0a6fe281859 --- /dev/null +++ b/docs/zh-hans/reference/QueryCache.md @@ -0,0 +1,123 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-06T03:56:14.760Z' +id: QueryCache +title: QueryCache +--- +`QueryCache` 是 TanStack Query 的存储机制,用于存储其包含的所有查询数据、元信息和状态。 + +**通常情况下,您不会直接与 QueryCache 交互,而是通过 `QueryClient` 来操作特定缓存。** + +```tsx +import { QueryCache } from '@tanstack/react-query' + +const queryCache = new QueryCache({ + onError: (error) => { + console.log(error) + }, + onSuccess: (data) => { + console.log(data) + }, + onSettled: (data, error) => { + console.log(data, error) + }, +}) + +const query = queryCache.find(['posts']) +``` + +其提供的方法包括: + +- [`find`](#querycachefind) +- [`findAll`](#querycachefindall) +- [`subscribe`](#querycachesubscribe) +- [`clear`](#querycacheclear) + +**配置项** + +- `onError?: (error: unknown, query: Query) => void` + - 可选 + - 当某个查询发生错误时调用此函数。 +- `onSuccess?: (data: unknown, query: Query) => void` + - 可选 + - 当某个查询成功时调用此函数。 +- `onSettled?: (data: unknown | undefined, error: unknown | null, query: Query) => void` + - 可选 + - 当某个查询完成(无论成功或失败)时调用此函数。 + +## `queryCache.find` + +`find` 是一个略微高级的同步方法,可用于从缓存中获取现有的查询实例。该实例不仅包含查询的**所有**状态,还包括所有实例和查询的底层实现细节。如果查询不存在,则返回 `undefined`。 + +> 注意:大多数应用通常不需要此方法,但在需要获取查询更多信息的罕见场景中会很有用(例如通过检查 `query.state.dataUpdatedAt` 时间戳来决定查询数据是否足够新鲜可用作初始值) + +```tsx +const query = queryCache.find(queryKey) +``` + +**配置项** + +- `filters?: QueryFilters`: [查询过滤器](../../framework/react/guides/filters#query-filters) + +**返回值** + +- `Query` + - 来自缓存的查询实例 + +## `queryCache.findAll` + +`findAll` 是更高级的同步方法,可用于从缓存中获取部分匹配查询键的所有现有查询实例。如果查询不存在,则返回空数组。 + +> 注意:大多数应用通常不需要此方法,但在需要获取查询更多信息的罕见场景中会很有用 + +```tsx +const queries = queryCache.findAll(queryKey) +``` + +**配置项** + +- `queryKey?: QueryKey`: [查询键](../../framework/react/guides/query-keys) +- `filters?: QueryFilters`: [查询过滤器](../../framework/react/guides/filters#query-filters) + +**返回值** + +- `Query[]` + - 来自缓存的查询实例数组 + +## `queryCache.subscribe` + +`subscribe` 方法可用于订阅整个查询缓存,并在缓存发生安全/已知的更新时(如查询状态变更、查询被更新/添加/删除)接收通知 + +```tsx +const callback = (event) => { + console.log(event.type, event.query) +} + +const unsubscribe = queryCache.subscribe(callback) +``` + +**配置项** + +- `callback: (event: QueryCacheNotifyEvent) => void` + - 当缓存通过其跟踪的更新机制(例如 `query.setState`、`queryClient.removeQueries` 等)更新时,会调用此函数。不鼓励对缓存进行超出范围的修改,此类操作不会触发订阅回调 + +**返回值** + +- `unsubscribe: Function => void` + - 调用此函数可取消订阅缓存。 + +## `queryCache.clear` + +`clear` 方法可用于完全清空缓存并重新开始。 + +```tsx +queryCache.clear() +``` + +[//]: # 'Materials' + +## 扩展阅读 + +要更深入理解 QueryCache 的内部工作原理,请参阅社区资源中的 [#18: React Query 内部机制](../../framework/react/community/tkdodos-blog#18-inside-react-query)。 + +[//]: # 'Materials' diff --git a/docs/zh-hans/reference/QueryClient.md b/docs/zh-hans/reference/QueryClient.md new file mode 100644 index 00000000000..872bb22bdf0 --- /dev/null +++ b/docs/zh-hans/reference/QueryClient.md @@ -0,0 +1,459 @@ +--- +source-updated-at: '2025-03-07T03:31:26.000Z' +translation-updated-at: '2025-05-06T03:55:21.509Z' +id: QueryClient +title: QueryClient +--- +## `QueryClient` + +`QueryClient` 可用于与缓存进行交互: + +```tsx +import { QueryClient } from '@tanstack/react-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: Infinity, + }, + }, +}) + +await queryClient.prefetchQuery({ queryKey: ['posts'], queryFn: fetchPosts }) +``` + +其提供的方法包括: + +- [`queryClient.fetchQuery`](#queryclientfetchquery) +- [`queryClient.fetchInfiniteQuery`](#queryclientfetchinfinitequery) +- [`queryClient.prefetchQuery`](#queryclientprefetchquery) +- [`queryClient.prefetchInfiniteQuery`](#queryclientprefetchinfinitequery) +- [`queryClient.getQueryData`](#queryclientgetquerydata) +- [`queryClient.ensureQueryData`](#queryclientensurequerydata) +- [`queryClient.ensureInfiniteQueryData`](#queryclientensureinfinitequerydata) +- [`queryClient.getQueriesData`](#queryclientgetqueriesdata) +- [`queryClient.setQueryData`](#queryclientsetquerydata) +- [`queryClient.getQueryState`](#queryclientgetquerystate) +- [`queryClient.setQueriesData`](#queryclientsetqueriesdata) +- [`queryClient.invalidateQueries`](#queryclientinvalidatequeries) +- [`queryClient.refetchQueries`](#queryclientrefetchqueries) +- [`queryClient.cancelQueries`](#queryclientcancelqueries) +- [`queryClient.removeQueries`](#queryclientremovequeries) +- [`queryClient.resetQueries`](#queryclientresetqueries) +- [`queryClient.isFetching`](#queryclientisfetching) +- [`queryClient.isMutating`](#queryclientismutating) +- [`queryClient.getDefaultOptions`](#queryclientgetdefaultoptions) +- [`queryClient.setDefaultOptions`](#queryclientsetdefaultoptions) +- [`queryClient.getQueryDefaults`](#queryclientgetquerydefaults) +- [`queryClient.setQueryDefaults`](#queryclientsetquerydefaults) +- [`queryClient.getMutationDefaults`](#queryclientgetmutationdefaults) +- [`queryClient.setMutationDefaults`](#queryclientsetmutationdefaults) +- [`queryClient.getQueryCache`](#queryclientgetquerycache) +- [`queryClient.getMutationCache`](#queryclientgetmutationcache) +- [`queryClient.clear`](#queryclientclear) +- [`queryClient.resumePausedMutations`](#queryclientresumepausedmutations) + +**选项** + +- `queryCache?: QueryCache` + - 可选 + - 该客户端关联的查询缓存。 +- `mutationCache?: MutationCache` + - 可选 + - 该客户端关联的变更缓存。 +- `defaultOptions?: DefaultOptions` + - 可选 + - 定义使用此 queryClient 的所有查询和变更的默认选项。 + - 也可用于定义 [hydration](../framework/react/reference/hydration) 的默认值。 + +## `queryClient.fetchQuery` + +`fetchQuery` 是一个异步方法,用于获取并缓存查询。它将返回数据或抛出错误。如果只需要获取查询而不需要结果,请使用 `prefetchQuery` 方法。 + +如果查询存在且数据未失效或未超过给定的 `staleTime`,则返回缓存中的数据。否则会尝试获取最新数据。 + +```tsx +try { + const data = await queryClient.fetchQuery({ queryKey, queryFn }) +} catch (error) { + console.log(error) +} +``` + +指定 `staleTime` 以仅在数据超过一定时间后才重新获取: + +```tsx +try { + const data = await queryClient.fetchQuery({ + queryKey, + queryFn, + staleTime: 10000, + }) +} catch (error) { + console.log(error) +} +``` + +**选项** + +`fetchQuery` 的选项与 [`useQuery`](../framework/react/reference/useQuery) 完全相同,除了以下专用于 useQuery 和 useInfiniteQuery 的选项:`enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, refetchOnMount, notifyOnChangeProps, throwOnError, select, suspense, placeholderData`。更多细节可查看 [源代码](https://github.com/TanStack/query/blob/7cd2d192e6da3df0b08e334ea1cf04cd70478827/packages/query-core/src/types.ts#L119)。 + +**返回值** + +- `Promise` + +## `queryClient.fetchInfiniteQuery` + +`fetchInfiniteQuery` 类似于 `fetchQuery`,但可用于获取并缓存无限查询。 + +```tsx +try { + const data = await queryClient.fetchInfiniteQuery({ queryKey, queryFn }) + console.log(data.pages) +} catch (error) { + console.log(error) +} +``` + +**选项** + +`fetchInfiniteQuery` 的选项与 [`fetchQuery`](#queryclientfetchquery) 完全相同。 + +**返回值** + +- `Promise>` + +## `queryClient.prefetchQuery` + +`prefetchQuery` 是一个异步方法,用于在需要或通过 `useQuery` 等渲染之前预取查询。该方法与 `fetchQuery` 的工作方式相同,只是不会抛出错误或返回任何数据。 + +```tsx +await queryClient.prefetchQuery({ queryKey, queryFn }) +``` + +甚至可以在配置中使用默认的 queryFn! + +```tsx +await queryClient.prefetchQuery({ queryKey }) +``` + +**选项** + +`prefetchQuery` 的选项与 [`fetchQuery`](#queryclientfetchquery) 完全相同。 + +**返回值** + +- `Promise` + - 返回一个 Promise,如果无需获取则立即解析,或在查询执行后解析。不会返回任何数据或抛出错误。 + +## `queryClient.prefetchInfiniteQuery` + +`prefetchInfiniteQuery` 类似于 `prefetchQuery`,但可用于预取并缓存无限查询。 + +```tsx +await queryClient.prefetchInfiniteQuery({ queryKey, queryFn }) +``` + +**选项** + +`prefetchInfiniteQuery` 的选项与 [`fetchQuery`](#queryclientfetchquery) 完全相同。 + +**返回值** + +- `Promise` + - 返回一个 Promise,如果无需获取则立即解析,或在查询执行后解析。不会返回任何数据或抛出错误。 + +## `queryClient.getQueryData` + +`getQueryData` 是一个同步函数,用于获取现有查询的缓存数据。如果查询不存在,则返回 `undefined`。 + +```tsx +const data = queryClient.getQueryData(queryKey) +``` + +**选项** + +- `queryKey: QueryKey`: [查询键 (Query Keys)](../framework/react/guides/query-keys) + +**返回值** + +- `data: TQueryFnData | undefined` + - 缓存查询的数据,如果查询不存在则返回 `undefined`。 + +## `queryClient.ensureQueryData` + +`ensureQueryData` 是一个异步函数,用于获取现有查询的缓存数据。如果查询不存在,则会调用 `queryClient.fetchQuery` 并返回其结果。 + +```tsx +const data = await queryClient.ensureQueryData({ queryKey, queryFn }) +``` + +**选项** + +- 与 [`fetchQuery`](#queryclientfetchquery) 相同的选项 +- `revalidateIfStale: boolean` + - 可选 + - 默认为 `false` + - 如果设置为 `true`,过时的数据会在后台重新获取,但会立即返回缓存数据。 + +**返回值** + +- `Promise` + +## `queryClient.ensureInfiniteQueryData` + +`ensureInfiniteQueryData` 是一个异步函数,用于获取现有无限查询的缓存数据。如果查询不存在,则会调用 `queryClient.fetchInfiniteQuery` 并返回其结果。 + +```tsx +const data = await queryClient.ensureInfiniteQueryData({ + queryKey, + queryFn, + initialPageParam, + getNextPageParam, +}) +``` + +**选项** + +- 与 [`fetchInfiniteQuery`](#queryclientfetchinfinitequery) 相同的选项 +- `revalidateIfStale: boolean` + - 可选 + - 默认为 `false` + - 如果设置为 `true`,过时的数据会在后台重新获取,但会立即返回缓存数据。 + +**返回值** + +- `Promise>` + +## `queryClient.getQueriesData` + +`getQueriesData` 是一个同步函数,用于获取多个查询的缓存数据。仅返回匹配传入的 queryKey 或 queryFilter 的查询。如果没有匹配的查询,则返回空数组。 + +```tsx +const data = queryClient.getQueriesData(filters) +``` + +**选项** + +- `filters: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) + - 如果传入过滤器,则返回匹配过滤器的 queryKeys 的数据 + +**返回值** + +- `[queryKey: QueryKey, data: TQueryFnData | undefined][]` + - 匹配的查询键及其关联数据的元组数组,如果没有匹配项则返回 `[]`。 + +**注意事项** + +由于每个元组中的返回数据结构可能不同(例如,使用过滤器返回“活跃”查询可能返回不同的数据类型),`TData` 泛型默认为 `unknown`。如果为 `TData` 提供更具体的类型,则假定您确定每个元组的数据条目均为相同类型。 + +这一区别主要是为知道将返回哪种结构的 TypeScript 开发者提供的“便利”。 + +## `queryClient.setQueryData` + +`setQueryData` 是一个同步函数,用于立即更新查询的缓存数据。如果查询不存在,则会创建它。**如果查询在默认的 `gcTime`(5 分钟)内未被查询钩子使用,该查询将被垃圾回收**。要同时更新多个查询并部分匹配查询键,需使用 [`queryClient.setQueriesData`](#queryclientsetqueriesdata)。 + +> 使用 `setQueryData` 和 `fetchQuery` 的区别在于,`setQueryData` 是同步的,并假定您已经同步获取了数据。如果需要异步获取数据,建议重新获取查询键或使用 `fetchQuery` 处理异步获取。 + +```tsx +queryClient.setQueryData(queryKey, updater) +``` + +**选项** + +- `queryKey: QueryKey`: [查询键 (Query Keys)](../framework/react/guides/query-keys) +- `updater: TQueryFnData | undefined | ((oldData: TQueryFnData | undefined) => TQueryFnData | undefined)` + - 如果传入非函数值,数据将更新为该值 + - 如果传入函数,它将接收旧数据值并应返回新数据。 + +**使用更新值** + +```tsx +setQueryData(queryKey, newData) +``` + +如果值为 `undefined`,则不会更新查询数据。 + +**使用更新函数** + +为了方便语法,也可以传入一个接收当前数据值并返回新数据的更新函数: + +```tsx +setQueryData(queryKey, (oldData) => newData) +``` + +如果更新函数返回 `undefined`,则不会更新查询数据。如果更新函数接收到 `undefined` 作为输入,可以返回 `undefined` 以取消更新,从而_不_创建新的缓存条目。 + +**不可变性** + +通过 `setQueryData` 进行的更新必须以_不可变_方式执行。**不要**尝试通过直接修改 `oldData` 或通过 `getQueryData` 检索的数据来写入缓存。 + +## `queryClient.getQueryState` + +`getQueryState` 是一个同步函数,用于获取现有查询的状态。如果查询不存在,则返回 `undefined`。 + +```tsx +const state = queryClient.getQueryState(queryKey) +console.log(state.dataUpdatedAt) +``` + +**选项** + +- `queryKey: QueryKey`: [查询键 (Query Keys)](../framework/react/guides/query-keys) + +## `queryClient.setQueriesData` + +`setQueriesData` 是一个同步函数,通过使用过滤器函数或部分匹配查询键,可以立即更新多个查询的缓存数据。仅更新匹配传入的 queryKey 或 queryFilter 的查询——不会创建新的缓存条目。底层会为每个现有查询调用 [`setQueryData`](#queryclientsetquerydata)。 + +```tsx +queryClient.setQueriesData(filters, updater) +``` + +**选项** + +- `filters: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) + - 如果传入过滤器,则更新匹配过滤器的 queryKeys +- `updater: TQueryFnData | (oldData: TQueryFnData | undefined) => TQueryFnData` + - [`setQueryData`](#queryclientsetquerydata) 的更新函数或新数据,将为每个匹配的 queryKey 调用 + +## `queryClient.invalidateQueries` + +`invalidateQueries` 方法可用于根据查询键或查询的其他功能可访问属性/状态,使缓存中的单个或多个查询失效并重新获取。默认情况下,所有匹配的查询会立即标记为失效,并在后台重新获取活跃查询。 + +- 如果**不希望活跃查询重新获取**,而仅标记为失效,可以使用 `refetchType: 'none'` 选项。 +- 如果**希望非活跃查询也重新获取**,使用 `refetchType: 'all'` 选项 + +```tsx +await queryClient.invalidateQueries( + { + queryKey: ['posts'], + exact, + refetchType: 'active', + }, + { throwOnError, cancelRefetch }, +) +``` + +**选项** + +- `filters?: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) + - `queryKey?: QueryKey`: [查询键 (Query Keys)](../framework/react/guides/query-keys) + - `refetchType?: 'active' | 'inactive' | 'all' | 'none'` + - 默认为 `'active'` + - 设置为 `active` 时,仅重新获取匹配重新获取条件且通过 `useQuery` 等正在活跃渲染的查询。 + - 设置为 `inactive` 时,仅重新获取匹配重新获取条件且未通过 `useQuery` 等活跃渲染的查询。 + - 设置为 `all` 时,重新获取所有匹配重新获取条件的查询。 + - 设置为 `none` 时,不重新获取任何查询,仅将匹配重新获取条件的查询标记为失效。 +- `options?: InvalidateOptions`: + - `throwOnError?: boolean` + - 设置为 `true` 时,如果任何查询重新获取任务失败,此方法将抛出错误。 + - `cancelRefetch?: boolean` + - 默认为 `true` + - 默认情况下,在发起新请求前会取消当前正在运行的请求 + - 设置为 `false` 时,如果已有请求正在运行,则不会进行重新获取。 + +## `queryClient.refetchQueries` + +`refetchQueries` 方法可用于根据特定条件重新获取查询。 + +示例: + +```tsx +// 重新获取所有查询: +await queryClient.refetchQueries() + +// 重新获取所有过时的查询: +await queryClient.refetchQueries({ stale: true }) + +// 重新获取所有部分匹配查询键的活跃查询: +await queryClient.refetchQueries({ queryKey: ['posts'], type: 'active' }) + +// 重新获取所有精确匹配查询键的活跃查询: +await queryClient.refetchQueries({ + queryKey: ['posts', 1], + type: 'active', + exact: true, +}) +``` + +**选项** + +- `filters?: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) +- `options?: RefetchOptions`: + - `throwOnError?: boolean` + - 设置为 `true` 时,如果任何查询重新获取任务失败,此方法将抛出错误。 + - `cancelRefetch?: boolean` + - 默认为 `true` + - 默认情况下,在发起新请求前会取消当前正在运行的请求 + - 设置为 `false` 时,如果已有请求正在运行,则不会进行重新获取。 + +**返回值** + +此方法返回一个 Promise,在所有查询完成重新获取后解析。默认情况下,如果任何查询重新获取失败,它**不会**抛出错误,但可以通过将 `throwOnError` 选项设置为 `true` 来配置。 + +## `queryClient.cancelQueries` + +`cancelQueries` 方法可用于根据查询键或查询的其他功能可访问属性/状态,取消正在进行的查询。 + +这在执行乐观更新时非常有用,因为您可能需要取消任何正在进行的查询重新获取,以免它们在解析时覆盖您的乐观更新。 + +```tsx +await queryClient.cancelQueries({ queryKey: ['posts'], exact: true }) +``` + +**选项** + +- `filters?: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) + +**返回值** + +此方法不返回任何内容 + +## `queryClient.removeQueries` + +`removeQueries` 方法可用于根据查询键或查询的其他功能可访问属性/状态,从缓存中移除查询。 + +```tsx +queryClient.removeQueries({ queryKey, exact: true }) +``` + +**选项** + +- `filters?: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) + +**返回值** + +此方法不返回任何内容 + +## `queryClient.resetQueries` + +`resetQueries` 方法可用于根据查询键或查询的其他功能可访问属性/状态,将缓存中的查询重置为其初始状态。 + +这将通知订阅者——与 `clear` 不同,后者会移除所有订阅者——并将查询重置为预加载状态——与 `invalidateQueries` 不同。如果查询有 `initialData`,查询的数据将重置为该值。如果查询是活跃的,则会重新获取。 + +```tsx +queryClient.resetQueries({ queryKey, exact: true }) +``` + +**选项** + +- `filters?: QueryFilters`: [查询过滤器 (Query Filters)](../framework/react/guides/filters#query-filters) +- `options?: ResetOptions`: + - `throwOnError?: boolean` + - 设置为 `true` 时,如果任何查询重新获取任务失败,此方法将抛出错误。 + - `cancelRefetch?: boolean` + - 默认为 `true` + - 默认情况下,在发起新请求前会取消当前正在运行的请求 + - 设置为 `false` 时,如果已有请求正在运行,则不会进行重新获取。 + +**返回值** + +此方法返回一个 Promise,在所有活跃查询完成重新获取后解析。 + +## `queryClient.isFetching` + +`isFetching` 方法返回一个 `integer`,表示缓存中当前正在获取(包括后台获取、加载新页面或加载更多无限查询结果)的查询数量(如果有)。 + +```tsx +if (queryClient.isFetching()) { + console.log('至少有一个查询 diff --git a/docs/zh-hans/reference/QueryObserver.md b/docs/zh-hans/reference/QueryObserver.md new file mode 100644 index 00000000000..f405e9e28d5 --- /dev/null +++ b/docs/zh-hans/reference/QueryObserver.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2025-03-18T11:09:51.000Z' +translation-updated-at: '2025-05-06T03:50:39.683Z' +id: QueryObserver +title: QueryObserver +--- +## QueryObserver + +`QueryObserver` 可用于观察并在多个查询之间切换。 + +```tsx +const observer = new QueryObserver(queryClient, { queryKey: ['posts'] }) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +**配置选项** + +`QueryObserver` 的选项与 [`useQuery`](../../framework/react/reference/useQuery) 的选项完全一致。 diff --git a/docs/zh-hans/reference/focusManager.md b/docs/zh-hans/reference/focusManager.md new file mode 100644 index 00000000000..fd482fa62ed --- /dev/null +++ b/docs/zh-hans/reference/focusManager.md @@ -0,0 +1,77 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-06T03:52:22.525Z' +id: FocusManager +title: focusManager +--- +`FocusManager` 用于管理 TanStack Query 中的焦点状态。 + +它可用于更改默认的事件监听器或手动修改焦点状态。 + +其提供的方法包括: + +- [`setEventListener`](#focusmanagerseteventlistener) +- [`subscribe`](#focusmanagersubscribe) +- [`setFocused`](#focusmanagersetfocused) +- [`isFocused`](#focusmanagerisfocused) + +## `focusManager.setEventListener` + +`setEventListener` 可用于设置自定义事件监听器: + +```tsx +import { focusManager } from '@tanstack/react-query' + +focusManager.setEventListener((handleFocus) => { + // 监听 visibilitychange 事件 + if (typeof window !== 'undefined' && window.addEventListener) { + window.addEventListener('visibilitychange', handleFocus, false) + } + + return () => { + // 设置新监听器时务必取消订阅 + window.removeEventListener('visibilitychange', handleFocus) + } +}) +``` + +## `focusManager.subscribe` + +`subscribe` 可用于订阅可见性状态的变化。它返回一个取消订阅的函数: + +```tsx +import { focusManager } from '@tanstack/react-query' + +const unsubscribe = focusManager.subscribe((isVisible) => { + console.log('isVisible', isVisible) +}) +``` + +## `focusManager.setFocused` + +`setFocused` 可用于手动设置焦点状态。设为 `undefined` 可回退至默认的焦点检查逻辑。 + +```tsx +import { focusManager } from '@tanstack/react-query' + +// 设为聚焦状态 +focusManager.setFocused(true) + +// 设为非聚焦状态 +focusManager.setFocused(false) + +// 回退至默认焦点检查 +focusManager.setFocused(undefined) +``` + +**选项** + +- `focused: boolean | undefined` + +## `focusManager.isFocused` + +`isFocused` 可用于获取当前焦点状态。 + +```tsx +const isFocused = focusManager.isFocused() +``` diff --git a/docs/zh-hans/reference/notifyManager.md b/docs/zh-hans/reference/notifyManager.md new file mode 100644 index 00000000000..dcfc2d2b738 --- /dev/null +++ b/docs/zh-hans/reference/notifyManager.md @@ -0,0 +1,89 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-06T03:50:31.682Z' +id: NotifyManager +title: notifyManager +--- +`notifyManager` 负责在 Tanstack Query 中调度和批量处理回调。 + +它提供以下方法: + +- [batch](#notifymanagerbatch) +- [batchCalls](#notifymanagerbatchcalls) +- [schedule](#notifymanagerschedule) +- [setNotifyFunction](#notifymanagersetnotifyfunction) +- [setBatchNotifyFunction](#notifymanagersetbatchnotifyfunction) +- [setScheduler](#notifymanagersetscheduler) + +## `notifyManager.batch` + +`batch` 可用于批量处理传入回调内所有已调度的更新。该方法主要用于内部优化 queryClient 的更新。 + +```ts +function batch(callback: () => T): T +``` + +## `notifyManager.batchCalls` + +`batchCalls` 是一个高阶函数,接收回调函数并对其进行包装。所有对包装函数的调用都会将该回调调度到下一批次执行。 + +```ts +type BatchCallsCallback> = (...args: T) => void + +function batchCalls>( + callback: BatchCallsCallback, +): BatchCallsCallback +``` + +## `notifyManager.schedule` + +`schedule` 将函数调度到下一批次执行。默认情况下使用 setTimeout 运行批次,但该行为可配置。 + +```ts +function schedule(callback: () => void): void +``` + +## `notifyManager.setNotifyFunction` + +`setNotifyFunction` 用于覆盖通知函数。该函数会在回调应执行时被调用。默认的 notifyFunction 会直接执行回调。 + +例如,可在运行测试时用 `React.act` 包装通知: + +```ts +import { notifyManager } from '@tanstack/react-query' +import { act } from 'react-dom/test-utils' + +notifyManager.setNotifyFunction(act) +``` + +## `notifyManager.setBatchNotifyFunction` + +`setBatchNotifyFunction` 设置用于批量更新的函数 + +如果您的框架支持自定义批量处理函数,可通过调用 notifyManager.setBatchNotifyFunction 告知 TanStack Query。 + +例如,以下是 solid-query 中设置批量函数的方式: + +```ts +import { notifyManager } from '@tanstack/query-core' +import { batch } from 'solid-js' + +notifyManager.setBatchNotifyFunction(batch) +``` + +## `notifyManager.setScheduler` + +`setScheduler` 配置一个自定义回调,用于调度下一批次的运行时机。默认行为是 `setTimeout(callback, 0)`。 + +```ts +import { notifyManager } from '@tanstack/react-query' + +// 在下一个微任务中调度批次 +notifyManager.setScheduler(queueMicrotask) + +// 在下一帧渲染前调度批次 +notifyManager.setScheduler(requestAnimationFrame) + +// 在未来某个时间调度批次 +notifyManager.setScheduler((cb) => setTimeout(cb, 10)) +``` diff --git a/docs/zh-hans/reference/onlineManager.md b/docs/zh-hans/reference/onlineManager.md new file mode 100644 index 00000000000..12c878708fc --- /dev/null +++ b/docs/zh-hans/reference/onlineManager.md @@ -0,0 +1,75 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-06T03:52:00.643Z' +id: OnlineManager +title: onlineManager +--- +`OnlineManager` 负责管理 TanStack Query 中的在线状态。它可用于修改默认的事件监听器或手动更改在线状态。 + +> 默认情况下,`onlineManager` 会假定存在活跃的网络连接,并通过监听 `window` 对象上的 `online` 和 `offline` 事件来检测状态变化。 + +> 在早期版本中,系统使用 `navigator.onLine` 判断网络状态。但该方法在基于 Chromium 的浏览器中存在缺陷,[大量问题](https://bugs.chromium.org/p/chromium/issues/list?q=navigator.online)表明其会产生误报,导致查询被错误标记为 `offline`。 + +> 为解决此问题,现在我们会始终以 `online: true` 作为初始状态,仅通过监听 `online` 和 `offline` 事件来更新状态。 + +> 这种方式能降低误报概率,但对于通过 Service Worker 加载的离线应用可能会产生误判,因为这类应用即使没有网络连接也能正常运行。 + +提供的方法包括: + +- [`setEventListener`](#onlinemanagerseteventlistener) +- [`subscribe`](#onlinemanagersubscribe) +- [`setOnline`](#onlinemanagersetonline) +- [`isOnline`](#onlinemanagerisonline) + +## `onlineManager.setEventListener` + +`setEventListener` 可用于设置自定义事件监听器: + +```tsx +import NetInfo from '@react-native-community/netinfo' +import { onlineManager } from '@tanstack/react-query' + +onlineManager.setEventListener((setOnline) => { + return NetInfo.addEventListener((state) => { + setOnline(!!state.isConnected) + }) +}) +``` + +## `onlineManager.subscribe` + +`subscribe` 可用于订阅在线状态变化。该方法会返回取消订阅的函数: + +```tsx +import { onlineManager } from '@tanstack/react-query' + +const unsubscribe = onlineManager.subscribe((isOnline) => { + console.log('isOnline', isOnline) +}) +``` + +## `onlineManager.setOnline` + +`setOnline` 可用于手动设置在线状态: + +```tsx +import { onlineManager } from '@tanstack/react-query' + +// 设置为在线 +onlineManager.setOnline(true) + +// 设置为离线 +onlineManager.setOnline(false) +``` + +**配置项** + +- `online: boolean` + +## `onlineManager.isOnline` + +`isOnline` 可用于获取当前在线状态: + +```tsx +const isOnline = onlineManager.isOnline() +``` diff --git a/docs/zh-hans/reference/streamedQuery.md b/docs/zh-hans/reference/streamedQuery.md new file mode 100644 index 00000000000..93af0aa4cc0 --- /dev/null +++ b/docs/zh-hans/reference/streamedQuery.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2025-04-02T06:46:03.000Z' +translation-updated-at: '2025-05-06T03:50:56.230Z' +id: streamedQuery +title: streamedQuery +--- +`streamedQuery` 是一个辅助函数,用于创建一个从 [AsyncIterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) 流式传输数据的查询函数。数据将是接收到的所有数据块的数组。在接收到第一个数据块之前,查询将处于 `pending` 状态,但之后会转为 `success` 状态。查询将保持 `fetchStatus` 为 `fetching` 直到流结束。 + +```tsx +const query = queryOptions({ + queryKey: ['data'], + queryFn: streamedQuery({ + queryFn: fetchDataInChunks, + }), +}) +``` + +**选项** + +- `queryFn: (context: QueryFunctionContext) => Promise>` + - **必填** + - 返回一个 Promise 的函数,该 Promise 解析为要流式传输数据的 AsyncIterable。 + - 接收一个 [QueryFunctionContext](../guides/query-functions.md#queryfunctioncontext) +- `refetchMode?: 'append' | 'reset'` + - 可选 + - 设置为 `'reset'` 时,查询会在重新获取数据时清除所有数据并回到 `pending` 状态。 + - 设置为 `'append'` 时,数据会在重新获取时追加。 + - 默认为 `'reset'` From 935304f0c997c658f6dd823f86c923e85e95064e Mon Sep 17 00:00:00 2001 From: xiaoyu2er Date: Tue, 6 May 2025 17:00:47 +0000 Subject: [PATCH 3/7] docs: update documentation translations --- docs/zh-hans/config.json | 2 +- docs/zh-hans/eslint/eslint-plugin-query.md | 1 + docs/zh-hans/eslint/exhaustive-deps.md | 3 +- .../eslint/infinite-query-property-order.md | 7 +- docs/zh-hans/eslint/no-rest-destructuring.md | 3 +- docs/zh-hans/eslint/no-unstable-deps.md | 1 + docs/zh-hans/eslint/no-void-query-fn.md | 41 ++++++ docs/zh-hans/eslint/stable-query-client.md | 1 + ...pclient-and-other-data-fetching-clients.md | 11 +- docs/zh-hans/framework/angular/devtools.md | 1 + .../guides/background-fetching-indicators.md | 1 + .../framework/angular/guides/caching.md | 5 +- .../angular/guides/default-query-function.md | 1 + .../angular/guides/dependent-queries.md | 1 + .../angular/guides/disabling-queries.md | 1 + .../guides/does-this-replace-client-state.md | 1 - .../framework/angular/guides/filters.md | 1 - .../angular/guides/infinite-queries.md | 3 + .../angular/guides/initial-query-data.md | 1 + .../guides/invalidations-from-mutations.md | 1 + .../angular/guides/mutation-options.md | 1 + .../framework/angular/guides/mutations.md | 5 +- .../framework/angular/guides/network-mode.md | 1 - .../angular/guides/optimistic-updates.md | 1 + .../angular/guides/paginated-queries.md | 1 + .../angular/guides/parallel-queries.md | 1 + .../angular/guides/placeholder-query-data.md | 1 + .../framework/angular/guides/queries.md | 1 + .../angular/guides/query-cancellation.md | 5 +- .../angular/guides/query-functions.md | 1 + .../angular/guides/query-invalidation.md | 1 + .../framework/angular/guides/query-keys.md | 1 + .../framework/angular/guides/query-options.md | 1 + .../framework/angular/guides/query-retries.md | 1 + .../angular/guides/scroll-restoration.md | 1 - .../zh-hans/framework/angular/installation.md | 1 + docs/zh-hans/framework/angular/overview.md | 1 + docs/zh-hans/framework/angular/quick-start.md | 1 + .../reference/functions/injectmutation.md | 1 + .../reference/functions/injectquery.md | 1 + docs/zh-hans/framework/angular/typescript.md | 1 + docs/zh-hans/framework/angular/zoneless.md | 1 + .../react/community/community-projects.md | 1 + .../framework/react/community/tkdodos-blog.md | 1 + docs/zh-hans/framework/react/comparison.md | 97 ++++++++------- docs/zh-hans/framework/react/devtools.md | 1 + docs/zh-hans/framework/react/graphql.md | 1 + .../framework/react/guides/advanced-ssr.md | 10 +- .../guides/background-fetching-indicators.md | 1 + .../zh-hans/framework/react/guides/caching.md | 5 + .../react/guides/default-query-function.md | 1 + .../react/guides/dependent-queries.md | 1 + .../react/guides/disabling-queries.md | 1 + .../guides/does-this-replace-client-state.md | 3 +- .../zh-hans/framework/react/guides/filters.md | 1 + .../react/guides/important-defaults.md | 1 + .../react/guides/infinite-queries.md | 1 + .../react/guides/initial-query-data.md | 1 + .../guides/invalidations-from-mutations.md | 1 + .../guides/migrating-to-react-query-3.md | 2 + .../guides/migrating-to-react-query-4.md | 5 + .../framework/react/guides/migrating-to-v5.md | 1 + .../framework/react/guides/mutations.md | 5 +- .../framework/react/guides/network-mode.md | 3 +- .../react/guides/optimistic-updates.md | 1 + .../react/guides/paginated-queries.md | 1 + .../react/guides/parallel-queries.md | 1 + .../react/guides/placeholder-query-data.md | 1 + .../framework/react/guides/prefetching.md | 6 +- .../zh-hans/framework/react/guides/queries.md | 1 + .../react/guides/query-cancellation.md | 5 +- .../framework/react/guides/query-functions.md | 1 + .../react/guides/query-invalidation.md | 1 + .../framework/react/guides/query-keys.md | 1 + .../framework/react/guides/query-options.md | 1 + .../framework/react/guides/query-retries.md | 1 + .../react/guides/render-optimizations.md | 2 + .../react/guides/request-waterfalls.md | 3 +- .../react/guides/scroll-restoration.md | 1 + docs/zh-hans/framework/react/guides/ssr.md | 1 + .../framework/react/guides/suspense.md | 1 + .../zh-hans/framework/react/guides/testing.md | 7 +- .../guides/updates-from-mutation-responses.md | 1 + .../react/guides/window-focus-refetching.md | 1 + docs/zh-hans/framework/react/installation.md | 1 + docs/zh-hans/framework/react/overview.md | 1 + .../react/plugins/broadcastQueryClient.md | 1 + .../plugins/createAsyncStoragePersister.md | 1 + .../react/plugins/createPersister.md | 1 + .../plugins/createSyncStoragePersister.md | 3 +- .../react/plugins/persistQueryClient.md | 1 + docs/zh-hans/framework/react/quick-start.md | 1 + docs/zh-hans/framework/react/react-native.md | 8 +- .../react/reference/QueryClientProvider.md | 1 + .../reference/QueryErrorResetBoundary.md | 1 + .../framework/react/reference/hydration.md | 1 + .../react/reference/infiniteQueryOptions.md | 1 + .../framework/react/reference/queryOptions.md | 1 + .../react/reference/useInfiniteQuery.md | 1 + .../react/reference/useIsFetching.md | 1 + .../react/reference/useIsMutating.md | 1 + .../framework/react/reference/useMutation.md | 1 + .../react/reference/useMutationState.md | 1 + .../reference/usePrefetchInfiniteQuery.md | 1 + .../react/reference/usePrefetchQuery.md | 2 + .../framework/react/reference/useQueries.md | 1 + .../framework/react/reference/useQuery.md | 1 + .../react/reference/useQueryClient.md | 1 + .../reference/useQueryErrorResetBoundary.md | 1 + .../reference/useSuspenseInfiniteQuery.md | 1 + .../react/reference/useSuspenseQueries.md | 3 + .../react/reference/useSuspenseQuery.md | 1 + docs/zh-hans/framework/react/typescript.md | 1 + docs/zh-hans/framework/react/videos.md | 1 + .../solid/community/community-projects.md | 1 - .../framework/solid/community/tkdodos-blog.md | 1 - docs/zh-hans/framework/solid/devtools.md | 75 +++++------ .../framework/solid/guides/advanced-ssr.md | 1 + .../guides/background-fetching-indicators.md | 1 - .../zh-hans/framework/solid/guides/caching.md | 1 - .../solid/guides/default-query-function.md | 1 - .../solid/guides/dependent-queries.md | 1 - .../solid/guides/disabling-queries.md | 1 - .../guides/does-this-replace-client-state.md | 1 - .../zh-hans/framework/solid/guides/filters.md | 1 - .../solid/guides/important-defaults.md | 1 - .../solid/guides/infinite-queries.md | 1 - .../solid/guides/initial-query-data.md | 1 - .../guides/invalidations-from-mutations.md | 1 - .../framework/solid/guides/mutations.md | 1 - .../framework/solid/guides/network-mode.md | 1 - .../solid/guides/optimistic-updates.md | 1 - .../solid/guides/paginated-queries.md | 1 - .../solid/guides/parallel-queries.md | 1 - .../solid/guides/placeholder-query-data.md | 1 - .../framework/solid/guides/prefetching.md | 1 - .../zh-hans/framework/solid/guides/queries.md | 1 - .../framework/solid/guides/query-functions.md | 1 - .../solid/guides/query-invalidation.md | 1 - .../framework/solid/guides/query-keys.md | 1 - .../framework/solid/guides/query-options.md | 1 - .../framework/solid/guides/query-retries.md | 1 - .../solid/guides/request-waterfalls.md | 1 - .../solid/guides/scroll-restoration.md | 1 - docs/zh-hans/framework/solid/guides/ssr.md | 1 + .../framework/solid/guides/suspense.md | 1 + .../zh-hans/framework/solid/guides/testing.md | 1 - .../guides/updates-from-mutation-responses.md | 1 - .../solid/guides/window-focus-refetching.md | 1 - docs/zh-hans/framework/solid/installation.md | 1 + docs/zh-hans/framework/solid/overview.md | 1 + .../solid/plugins/broadcastQueryClient.md | 1 - .../solid/plugins/createPersister.md | 1 - docs/zh-hans/framework/solid/quick-start.md | 1 + .../solid/reference/infiniteQueryOptions.md | 1 - .../framework/solid/reference/queryOptions.md | 1 - .../solid/reference/useInfiniteQuery.md | 1 - .../solid/reference/useIsFetching.md | 1 - .../solid/reference/useIsMutating.md | 1 - .../framework/solid/reference/useMutation.md | 1 - .../solid/reference/useMutationState.md | 1 - .../framework/solid/reference/useQueries.md | 1 - docs/zh-hans/framework/solid/typescript.md | 1 + docs/zh-hans/framework/svelte/devtools.md | 1 + docs/zh-hans/framework/svelte/installation.md | 1 + docs/zh-hans/framework/svelte/overview.md | 1 + docs/zh-hans/framework/svelte/reactivity.md | 1 + docs/zh-hans/framework/svelte/ssr.md | 1 + .../vue/community/community-projects.md | 9 +- .../framework/vue/community/tkdodos-blog.md | 1 - docs/zh-hans/framework/vue/devtools.md | 117 +++++++++--------- docs/zh-hans/framework/vue/graphql.md | 1 + .../guides/background-fetching-indicators.md | 1 + docs/zh-hans/framework/vue/guides/caching.md | 1 - .../framework/vue/guides/custom-client.md | 1 + .../vue/guides/default-query-function.md | 1 + .../framework/vue/guides/dependent-queries.md | 1 + .../framework/vue/guides/disabling-queries.md | 1 + .../guides/does-this-replace-client-state.md | 3 +- docs/zh-hans/framework/vue/guides/filters.md | 1 - .../guides/invalidations-from-mutations.md | 1 + .../framework/vue/guides/migrating-to-v5.md | 1 + .../zh-hans/framework/vue/guides/mutations.md | 5 +- .../framework/vue/guides/network-mode.md | 1 - .../vue/guides/optimistic-updates.md | 1 - .../framework/vue/guides/paginated-queries.md | 1 + .../framework/vue/guides/parallel-queries.md | 1 + .../vue/guides/placeholder-query-data.md | 1 + .../framework/vue/guides/prefetching.md | 1 + docs/zh-hans/framework/vue/guides/queries.md | 7 ++ .../vue/guides/query-invalidation.md | 1 - .../framework/vue/guides/query-options.md | 1 - .../framework/vue/guides/query-retries.md | 1 + .../vue/guides/scroll-restoration.md | 1 - docs/zh-hans/framework/vue/guides/ssr.md | 1 + docs/zh-hans/framework/vue/guides/suspense.md | 1 + docs/zh-hans/framework/vue/guides/testing.md | 1 - .../guides/updates-from-mutation-responses.md | 1 - docs/zh-hans/framework/vue/installation.md | 1 + docs/zh-hans/framework/vue/overview.md | 3 +- .../vue/plugins/broadcastQueryClient.md | 1 - .../framework/vue/plugins/createPersister.md | 13 +- docs/zh-hans/framework/vue/quick-start.md | 7 +- docs/zh-hans/framework/vue/reactivity.md | 1 + .../vue/reference/infiniteQueryOptions.md | 1 - .../framework/vue/reference/queryOptions.md | 1 - .../vue/reference/useInfiniteQuery.md | 1 - .../framework/vue/reference/useIsFetching.md | 1 - .../framework/vue/reference/useIsMutating.md | 1 - .../framework/vue/reference/useMutation.md | 1 - .../vue/reference/useMutationState.md | 1 - .../framework/vue/reference/useQueries.md | 1 - .../framework/vue/reference/useQuery.md | 1 - .../framework/vue/reference/useQueryClient.md | 1 - docs/zh-hans/framework/vue/typescript.md | 1 + .../reference/InfiniteQueryObserver.md | 1 + docs/zh-hans/reference/MutationCache.md | 1 + docs/zh-hans/reference/QueriesObserver.md | 1 + docs/zh-hans/reference/QueryCache.md | 1 + docs/zh-hans/reference/QueryClient.md | 6 +- docs/zh-hans/reference/QueryObserver.md | 1 + docs/zh-hans/reference/focusManager.md | 1 + docs/zh-hans/reference/notifyManager.md | 1 + docs/zh-hans/reference/onlineManager.md | 1 + docs/zh-hans/reference/streamedQuery.md | 1 + 225 files changed, 422 insertions(+), 265 deletions(-) create mode 100644 docs/zh-hans/eslint/no-void-query-fn.md diff --git a/docs/zh-hans/config.json b/docs/zh-hans/config.json index 6a561072a69..c4bcdc9004c 100644 --- a/docs/zh-hans/config.json +++ b/docs/zh-hans/config.json @@ -1369,4 +1369,4 @@ "Nozzle.io", "Uber" ] -} \ No newline at end of file +} diff --git a/docs/zh-hans/eslint/eslint-plugin-query.md b/docs/zh-hans/eslint/eslint-plugin-query.md index 8912b0b9926..6c07a5e1de7 100644 --- a/docs/zh-hans/eslint/eslint-plugin-query.md +++ b/docs/zh-hans/eslint/eslint-plugin-query.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:48:42.197Z' id: eslint-plugin-query title: ESLint 插件 Query --- + TanStack Query 提供了专属的 ESLint 插件。该插件用于强制执行最佳实践,并帮助您避免常见错误。 ## 安装 diff --git a/docs/zh-hans/eslint/exhaustive-deps.md b/docs/zh-hans/eslint/exhaustive-deps.md index 6501bd1df9e..2b974fe8099 100644 --- a/docs/zh-hans/eslint/exhaustive-deps.md +++ b/docs/zh-hans/eslint/exhaustive-deps.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:49:05.078Z' id: exhaustive-deps title: 彻底依赖检查 --- + 应将查询键 (query keys) 视为查询函数 (query function) 的依赖数组:所有在 queryFn 内部使用的变量都应添加到查询键中。这确保了查询能够独立缓存,并在变量变化时自动重新获取数据。 ## 规则详情 @@ -42,5 +43,5 @@ const todoQueries = { ## 属性 -- [x] ✅ 推荐启用 +- [x] ✅ 推荐启用 - [x] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/infinite-query-property-order.md b/docs/zh-hans/eslint/infinite-query-property-order.md index 393ec6d0bdd..c32910e68c2 100644 --- a/docs/zh-hans/eslint/infinite-query-property-order.md +++ b/docs/zh-hans/eslint/infinite-query-property-order.md @@ -4,16 +4,17 @@ translation-updated-at: '2025-05-06T03:48:55.772Z' id: infinite-query-property-order title: 无限查询属性顺序 --- + 对于以下函数,由于类型推断的原因,传入对象的属性顺序至关重要: - `useInfiniteQuery` -- `useSuspenseInfiniteQuery` +- `useSuspenseInfiniteQuery` - `infiniteQueryOptions` 正确的属性顺序应如下: - `queryFn` -- `getPreviousPageParam` +- `getPreviousPageParam` - `getNextPageParam` 其他所有属性对顺序不敏感,因为它们不依赖于类型推断。 @@ -60,5 +61,5 @@ const query = useInfiniteQuery({ ## 特性 -- [x] ✅ 推荐 +- [x] ✅ 推荐 - [x] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/no-rest-destructuring.md b/docs/zh-hans/eslint/no-rest-destructuring.md index 44d4ccd74b3..1f3c2b0f304 100644 --- a/docs/zh-hans/eslint/no-rest-destructuring.md +++ b/docs/zh-hans/eslint/no-rest-destructuring.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:49:19.238Z' id: no-rest-destructuring title: 禁止剩余解构 --- + 对查询结果使用对象剩余解构会自动订阅查询结果的每个字段,可能导致不必要的重新渲染。 此规则确保你仅订阅实际需要的字段。 @@ -42,5 +43,5 @@ const { data: todos } = todosQuery ## 特性 -- [x] ✅ 推荐规则 +- [x] ✅ 推荐规则 - [ ] 🔧 可自动修复 diff --git a/docs/zh-hans/eslint/no-unstable-deps.md b/docs/zh-hans/eslint/no-unstable-deps.md index 656603727cc..1d547cb615c 100644 --- a/docs/zh-hans/eslint/no-unstable-deps.md +++ b/docs/zh-hans/eslint/no-unstable-deps.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:49:37.842Z' id: no-unstable-deps title: 禁止不稳定依赖 --- + 以下查询钩子返回的对象**不具备**引用稳定性: - `useQuery` diff --git a/docs/zh-hans/eslint/no-void-query-fn.md b/docs/zh-hans/eslint/no-void-query-fn.md new file mode 100644 index 00000000000..3ae33ae3297 --- /dev/null +++ b/docs/zh-hans/eslint/no-void-query-fn.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2025-04-07T09:17:45.000Z' +translation-updated-at: '2025-05-06T17:00:01.729Z' +id: no-void-query-fn +title: Disallow returning void from query functions +--- + +查询函数必须返回一个将被 TanStack Query 缓存的值。不返回值的函数(void 函数)可能导致意外行为,并可能表明实现中存在错误。 + +## 规则详情 + +该规则的**错误**代码示例: + +```tsx +/* 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 + }, +}) +``` + +## 属性 + +- [x] ✅ 推荐 +- [ ] 🔧 可修复 diff --git a/docs/zh-hans/eslint/stable-query-client.md b/docs/zh-hans/eslint/stable-query-client.md index e03cc16868b..ffd67f584da 100644 --- a/docs/zh-hans/eslint/stable-query-client.md +++ b/docs/zh-hans/eslint/stable-query-client.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:48:33.223Z' id: stable-query-client title: 稳定的 Query Client --- + QueryClient 包含了 QueryCache,因此你应当只为应用的生命周期创建一个 QueryClient 实例 —— 而非在每次渲染时都创建新实例。 > 例外情况:允许在异步服务端组件 (async Server Component) 内部创建新的 QueryClient,因为该异步函数仅在服务端调用一次。 diff --git a/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md b/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md index 62c8364939c..1d73f763f5a 100644 --- a/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md +++ b/docs/zh-hans/framework/angular/angular-httpclient-and-other-data-fetching-clients.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:50:39.857Z' id: Angular-HttpClient-and-other-data-fetching-clients title: Angular HttpClient 及其他数据获取客户端 --- + 由于 TanStack Query 的请求机制基于 Promise 无感知构建,您实际上可以使用任何异步数据请求客户端,包括浏览器原生的 `fetch` API、`graphql-request` 等。 ## 使用 Angular 的 `HttpClient` 获取数据 @@ -42,8 +43,8 @@ class ExampleComponent { ## 对比表格 -| 数据请求客户端 | 优势 | 局限性 | -| --------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------- | -| **Angular HttpClient** | 功能丰富且与 Angular 深度集成。 | 需将 Observable 转换为 Promise。 | -| **Fetch** | 浏览器原生 API,不增加包体积。 | 功能基础,缺乏高级特性。 | -| **专用库如 `graphql-request`** | 针对特定场景的专用功能。 | 若非 Angular 专用库则框架集成度较差。 | +| 数据请求客户端 | 优势 | 局限性 | +| ------------------------------ | ------------------------------- | ------------------------------------- | +| **Angular HttpClient** | 功能丰富且与 Angular 深度集成。 | 需将 Observable 转换为 Promise。 | +| **Fetch** | 浏览器原生 API,不增加包体积。 | 功能基础,缺乏高级特性。 | +| **专用库如 `graphql-request`** | 针对特定场景的专用功能。 | 若非 Angular 专用库则框架集成度较差。 | diff --git a/docs/zh-hans/framework/angular/devtools.md b/docs/zh-hans/framework/angular/devtools.md index 8f7afafa4f2..cb5b433b3a2 100644 --- a/docs/zh-hans/framework/angular/devtools.md +++ b/docs/zh-hans/framework/angular/devtools.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:52:21.298Z' id: devtools title: 开发者工具 --- + ## 启用开发者工具 (devtools) 开发者工具 (devtools) 可帮助您调试和检查查询 (queries) 与变更 (mutations)。您可以通过在 `provideTanStackQuery` 中添加 `withDevtools` 来启用开发者工具。 diff --git a/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md b/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md index a83cb973470..5b1a516db83 100644 --- a/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md +++ b/docs/zh-hans/framework/angular/guides/background-fetching-indicators.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:08:13.992Z' id: background-fetching-indicators title: 后台获取指示器 --- + ## 后台获取指示器 查询的 `status === 'pending'` 状态足以显示查询的初始硬加载状态,但有时您可能希望额外显示一个指示器,表明查询正在后台重新获取。为此,查询还提供了一个 `isFetching` 布尔值,无论 `status` 变量的状态如何,您都可以用它来显示查询正处于获取状态: diff --git a/docs/zh-hans/framework/angular/guides/caching.md b/docs/zh-hans/framework/angular/guides/caching.md index 267aeb3db5d..f50552973d5 100644 --- a/docs/zh-hans/framework/angular/guides/caching.md +++ b/docs/zh-hans/framework/angular/guides/caching.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:07:51.213Z' id: caching title: 缓存 --- + > 请先完整阅读 [重要默认配置](../important-defaults) 再阅读本指南 ## 基础示例 @@ -21,17 +22,19 @@ title: 缓存 - 由于之前没有使用 `['todos']` 查询键发起过其他查询,该查询会显示硬加载状态并发起网络请求获取数据。 - 当网络请求完成时,返回的数据会被缓存在 `['todos']` 键下。 - 数据会在配置的 `staleTime`(默认为 `0`,即立即)后被标记为过时。 - - 在其他地方初始化第二个 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例: + - 由于缓存中已存在第一个查询的 `['todos']` 键数据,该数据会立即从缓存中返回。 - 新实例会使用其查询函数触发一个新的网络请求。 - 注意:无论两个 `fetchTodos` 查询函数是否相同,两个查询的 [`status`](../../reference/injectQuery)(包括 `isFetching`、`isPending` 等相关值)都会更新,因为它们共享相同的查询键。 - 当请求成功完成时,`['todos']` 键下的缓存数据会更新为新数据,两个实例都会同步到新数据。 - 两个 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例被销毁且不再使用: + - 由于该查询没有活跃实例,系统会基于 `gcTime` 设置垃圾回收超时(默认为 5 分钟)来删除并回收该查询。 - 在缓存超时完成前,另一个 `injectQuery(() => ({ queryKey: ['todos'], queyFn: fetchTodos })` 实例挂载: + - 查询会立即返回可用的缓存数据,同时 `fetchTodos` 函数在后台运行。当成功完成后,会用新数据更新缓存。 - 最后一个 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 实例被销毁: diff --git a/docs/zh-hans/framework/angular/guides/default-query-function.md b/docs/zh-hans/framework/angular/guides/default-query-function.md index f6fc8aa84c1..bcf5112ba70 100644 --- a/docs/zh-hans/framework/angular/guides/default-query-function.md +++ b/docs/zh-hans/framework/angular/guides/default-query-function.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:07:23.242Z' id: default-query-function title: 默认查询函数 --- + 如果你出于某种原因希望在整个应用中共享同一个查询函数,仅通过查询键 (query key) 来标识应该获取什么数据,那么可以通过为 TanStack Query 提供 **默认查询函数 (default query function)** 来实现: ```ts diff --git a/docs/zh-hans/framework/angular/guides/dependent-queries.md b/docs/zh-hans/framework/angular/guides/dependent-queries.md index 34e3c56222e..42f09d7d72f 100644 --- a/docs/zh-hans/framework/angular/guides/dependent-queries.md +++ b/docs/zh-hans/framework/angular/guides/dependent-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:07:06.536Z' id: dependent-queries title: 依赖查询 --- + ## 依赖查询 (Dependent Query) 依赖查询(或串行查询)需要等待前一个查询完成才能执行。实现这一功能非常简单,只需使用 `enabled` 选项来告知查询何时可以运行: diff --git a/docs/zh-hans/framework/angular/guides/disabling-queries.md b/docs/zh-hans/framework/angular/guides/disabling-queries.md index b5d0220538f..f71b46d1a7a 100644 --- a/docs/zh-hans/framework/angular/guides/disabling-queries.md +++ b/docs/zh-hans/framework/angular/guides/disabling-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:06:44.473Z' id: disabling-queries title: 禁用/暂停查询 --- + 如果你希望阻止某个查询自动执行,可以使用 `enabled = false` 选项。该选项也支持传入返回布尔值的回调函数。 当 `enabled` 为 `false` 时: diff --git a/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md index b0cae483abe..0bcd519fecb 100644 --- a/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md +++ b/docs/zh-hans/framework/angular/guides/does-this-replace-client-state.md @@ -9,4 +9,3 @@ replace: useMutation: injectMutation hook: function --- - diff --git a/docs/zh-hans/framework/angular/guides/filters.md b/docs/zh-hans/framework/angular/guides/filters.md index f3416586d69..f2a8878f4b1 100644 --- a/docs/zh-hans/framework/angular/guides/filters.md +++ b/docs/zh-hans/framework/angular/guides/filters.md @@ -5,4 +5,3 @@ id: filters title: Filters ref: docs/zh-hans/framework/react/guides/filters.md --- - diff --git a/docs/zh-hans/framework/angular/guides/infinite-queries.md b/docs/zh-hans/framework/angular/guides/infinite-queries.md index bbe0b43adcd..3522a7faa84 100644 --- a/docs/zh-hans/framework/angular/guides/infinite-queries.md +++ b/docs/zh-hans/framework/angular/guides/infinite-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:05:56.640Z' id: infinite-queries title: 无限查询 --- + ## 无限查询 (Infinite Queries) 能够以增量方式"加载更多"数据到现有数据集或实现"无限滚动"的列表渲染是一种非常常见的 UI 模式。TanStack Query 为此提供了一个名为 `injectInfiniteQuery` 的 `injectQuery` 变体,专门用于查询这类列表。 @@ -38,6 +39,7 @@ fetch('/api/projects?cursor=9') ``` 通过这些信息,我们可以创建一个"加载更多"的 UI: + - 默认情况下等待 `injectInfiniteQuery` 请求第一组数据 - 在 `getNextPageParam` 中返回下一个查询的信息 - 调用 `fetchNextPage` 函数 @@ -200,6 +202,7 @@ queryClient.setQueryData(['projects'], (data) => ({ ## 如何限制页面数量? 在某些使用场景中,您可能希望限制查询数据中存储的页面数量以提高性能和用户体验: + - 当用户可以加载大量页面时(内存使用) - 当需要重新获取包含数十个页面的无限查询时(网络使用:所有页面都会顺序获取) diff --git a/docs/zh-hans/framework/angular/guides/initial-query-data.md b/docs/zh-hans/framework/angular/guides/initial-query-data.md index a60a961cb9b..68a83d021e9 100644 --- a/docs/zh-hans/framework/angular/guides/initial-query-data.md +++ b/docs/zh-hans/framework/angular/guides/initial-query-data.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:04:23.076Z' id: initial-query-data title: 初始查询数据 --- + 在需要之前,有多种方式可以为查询提供初始数据到缓存中: - 声明式: diff --git a/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md index 6fea0ea6e43..c08a9b68ad1 100644 --- a/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md +++ b/docs/zh-hans/framework/angular/guides/invalidations-from-mutations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:03:17.566Z' id: invalidations-from-mutations title: 从变更中失效 --- + ## 变更触发的失效 使查询失效只是成功的一半,而知道**何时**使其失效则是另一半。通常,当应用中的某个变更 (mutation) 成功执行时,应用中很可能存在相关的查询需要失效,并可能需要重新获取数据以反映该变更带来的新变化。 diff --git a/docs/zh-hans/framework/angular/guides/mutation-options.md b/docs/zh-hans/framework/angular/guides/mutation-options.md index bbf30215b1e..fd7cfb9fef2 100644 --- a/docs/zh-hans/framework/angular/guides/mutation-options.md +++ b/docs/zh-hans/framework/angular/guides/mutation-options.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:03:00.167Z' id: query-options title: 变更选项 --- + 在多个地方共享变更选项 (mutation options) 的最佳方式之一,就是使用 `mutationOptions` 辅助函数。在运行时,这个辅助函数会原样返回你传入的内容,但[配合 TypeScript](../../typescript#typing-query-options) 使用时能带来诸多优势。你可以在一个地方定义变更操作的所有可能选项,同时还能获得完整的类型推断和类型安全。 ```ts diff --git a/docs/zh-hans/framework/angular/guides/mutations.md b/docs/zh-hans/framework/angular/guides/mutations.md index e536edb60a8..2e18443f0d2 100644 --- a/docs/zh-hans/framework/angular/guides/mutations.md +++ b/docs/zh-hans/framework/angular/guides/mutations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:02:49.197Z' id: mutations title: 变更 --- + 与查询 (query) 不同,变更 (mutation) 通常用于创建/更新/删除数据或执行服务端副作用。为此,TanStack Query 导出了 `injectMutation` 函数。 以下是一个向服务器添加新待办事项的变更示例: @@ -122,10 +123,10 @@ mutation = injectMutation(() => ({ mutation = injectMutation(() => ({ mutationFn: addTodo, onSuccess: async () => { - console.log("我先执行!") + console.log('我先执行!') }, onSettled: async () => { - console.log("我第二个执行!") + console.log('我第二个执行!') }, })) ``` diff --git a/docs/zh-hans/framework/angular/guides/network-mode.md b/docs/zh-hans/framework/angular/guides/network-mode.md index 6ab614b65e6..d7fcdd93247 100644 --- a/docs/zh-hans/framework/angular/guides/network-mode.md +++ b/docs/zh-hans/framework/angular/guides/network-mode.md @@ -5,4 +5,3 @@ id: network-mode title: Network Mode ref: docs/zh-hans/framework/react/guides/network-mode.md --- - diff --git a/docs/zh-hans/framework/angular/guides/optimistic-updates.md b/docs/zh-hans/framework/angular/guides/optimistic-updates.md index b24df1b71bd..030d9895a5a 100644 --- a/docs/zh-hans/framework/angular/guides/optimistic-updates.md +++ b/docs/zh-hans/framework/angular/guides/optimistic-updates.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:00:55.440Z' id: optimistic-updates title: 乐观更新 --- + Angular Query 提供了两种在变更操作完成前乐观更新 UI 的方式。您既可以使用 `onMutate` 选项直接更新缓存,也可以利用 `injectMutation` 返回的 `variables` 从结果中更新 UI。 ## 通过 UI 更新 diff --git a/docs/zh-hans/framework/angular/guides/paginated-queries.md b/docs/zh-hans/framework/angular/guides/paginated-queries.md index 35ba43f3315..14f627556f7 100644 --- a/docs/zh-hans/framework/angular/guides/paginated-queries.md +++ b/docs/zh-hans/framework/angular/guides/paginated-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:59:52.176Z' id: paginated-queries title: 分页查询 --- + 分页渲染数据是一种非常常见的 UI 模式,在 TanStack Query 中,只需将页码信息包含在查询键 (query key) 中即可实现: ```ts diff --git a/docs/zh-hans/framework/angular/guides/parallel-queries.md b/docs/zh-hans/framework/angular/guides/parallel-queries.md index 0a575aa3fc8..e1031a17cc1 100644 --- a/docs/zh-hans/framework/angular/guides/parallel-queries.md +++ b/docs/zh-hans/framework/angular/guides/parallel-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:59:05.811Z' id: parallel-queries title: 并行查询 --- + "并行 (Parallel)" 查询是指同时执行多个查询,以最大化数据获取的并发性。 ## 手动并行查询 diff --git a/docs/zh-hans/framework/angular/guides/placeholder-query-data.md b/docs/zh-hans/framework/angular/guides/placeholder-query-data.md index aa0464491e0..1f53b3b3b76 100644 --- a/docs/zh-hans/framework/angular/guides/placeholder-query-data.md +++ b/docs/zh-hans/framework/angular/guides/placeholder-query-data.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:58:48.269Z' id: placeholder-query-data title: 占位查询数据 --- + ## 什么是占位数据? 占位数据允许查询表现得好像已经拥有数据,类似于 `initialData` 选项,但**这些数据不会被持久化到缓存中**。这在以下场景中非常有用:当实际数据还在后台获取时,你已经拥有足够的局部(或模拟)数据来成功渲染查询。 diff --git a/docs/zh-hans/framework/angular/guides/queries.md b/docs/zh-hans/framework/angular/guides/queries.md index 8f60f0c28b9..47e6e865b97 100644 --- a/docs/zh-hans/framework/angular/guides/queries.md +++ b/docs/zh-hans/framework/angular/guides/queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:58:12.365Z' id: queries title: 查询 --- + ## 查询基础 查询是与**唯一键**绑定的、对异步数据源的声明式依赖。查询可用于任何基于 Promise 的方法(包括 GET 和 POST 方法)从服务器获取数据。如果您的方法会修改服务器上的数据,建议改用[变更](./mutations.md)。 diff --git a/docs/zh-hans/framework/angular/guides/query-cancellation.md b/docs/zh-hans/framework/angular/guides/query-cancellation.md index 6229deb838d..d5429552935 100644 --- a/docs/zh-hans/framework/angular/guides/query-cancellation.md +++ b/docs/zh-hans/framework/angular/guides/query-cancellation.md @@ -4,13 +4,14 @@ translation-updated-at: '2025-05-06T04:57:20.291Z' id: query-cancellation title: 查询取消 --- + TanStack Query 为每个查询函数提供了一个 [`AbortSignal` 实例](https://developer.mozilla.org/docs/Web/API/AbortSignal)。当查询过期或变为非活跃状态时,该 `signal` 会被中止。这意味着所有查询均可取消,并且您可以根据需要在查询函数内部响应取消操作。最棒的是,它允许您继续使用普通的 async/await 语法,同时获得自动取消的所有优势。 ## 默认行为 -默认情况下,在 Promise 解析之前卸载或不再使用的查询_不会_被取消。这意味着 Promise 解析后,结果数据仍会保留在缓存中。这对于已经开始接收查询但随后在完成前卸载组件的情况非常有用。如果您再次挂载组件且查询尚未被垃圾回收,数据将仍然可用。 +默认情况下,在 Promise 解析之前卸载或不再使用的查询*不会*被取消。这意味着 Promise 解析后,结果数据仍会保留在缓存中。这对于已经开始接收查询但随后在完成前卸载组件的情况非常有用。如果您再次挂载组件且查询尚未被垃圾回收,数据将仍然可用。 -然而,如果您使用了 `AbortSignal`,Promise 将被取消(例如中止 fetch 请求),因此查询也必须被取消。取消查询将导致其状态_回退_到之前的状态。 +然而,如果您使用了 `AbortSignal`,Promise 将被取消(例如中止 fetch 请求),因此查询也必须被取消。取消查询将导致其状态*回退*到之前的状态。 ## 使用 `HttpClient` diff --git a/docs/zh-hans/framework/angular/guides/query-functions.md b/docs/zh-hans/framework/angular/guides/query-functions.md index 48a8c26d012..e967bd3e29a 100644 --- a/docs/zh-hans/framework/angular/guides/query-functions.md +++ b/docs/zh-hans/framework/angular/guides/query-functions.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:56:44.357Z' id: query-functions title: 查询函数 --- + # 查询函数 (Query Functions) 查询函数可以是**任何返回 Promise 的函数**。返回的 Promise 应当**解析数据 (resolve the data)** 或**抛出错误 (throw an error)**。 diff --git a/docs/zh-hans/framework/angular/guides/query-invalidation.md b/docs/zh-hans/framework/angular/guides/query-invalidation.md index 03c9da37184..22460a62fb0 100644 --- a/docs/zh-hans/framework/angular/guides/query-invalidation.md +++ b/docs/zh-hans/framework/angular/guides/query-invalidation.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:53:07.857Z' id: query-invalidation title: 查询失效 --- + 等待查询自动变陈旧(stale)后再重新获取并不总是适用,尤其是当用户操作明确导致某查询数据过期时。为此,`QueryClient` 提供了 `invalidateQueries` 方法,允许您智能地标记查询为陈旧状态,并可能触发重新获取! ```tsx diff --git a/docs/zh-hans/framework/angular/guides/query-keys.md b/docs/zh-hans/framework/angular/guides/query-keys.md index 929e2f161b6..c26b1b354b5 100644 --- a/docs/zh-hans/framework/angular/guides/query-keys.md +++ b/docs/zh-hans/framework/angular/guides/query-keys.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:54:01.961Z' id: query-keys title: 查询键 --- + TanStack Query 的核心是基于查询键 (query keys) 为您管理查询缓存。查询键在顶层必须是一个数组,可以简单到仅包含单个字符串的数组,也可以复杂到包含多个字符串和嵌套对象的数组。只要查询键是可序列化的,并且**能唯一标识查询的数据**,您就可以使用它! ## 简单查询键 diff --git a/docs/zh-hans/framework/angular/guides/query-options.md b/docs/zh-hans/framework/angular/guides/query-options.md index 729c5276cf8..e828e6b2b67 100644 --- a/docs/zh-hans/framework/angular/guides/query-options.md +++ b/docs/zh-hans/framework/angular/guides/query-options.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:53:28.456Z' id: query-options title: 查询选项 --- + 在多个地方共享 `queryKey` 和 `queryFn` 同时保持它们彼此关联的最佳方式之一是使用 `queryOptions` 辅助函数。在运行时,该辅助函数仅返回传入的内容,但[结合 TypeScript 使用时](../typescript.md#typing-query-options)能提供诸多优势。您可以在一个地方定义查询的所有可能选项,并同时获得类型推断和类型安全。 ```ts diff --git a/docs/zh-hans/framework/angular/guides/query-retries.md b/docs/zh-hans/framework/angular/guides/query-retries.md index 0867f36b939..2c2e742bf38 100644 --- a/docs/zh-hans/framework/angular/guides/query-retries.md +++ b/docs/zh-hans/framework/angular/guides/query-retries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:56:03.413Z' id: query-retries title: 查询重试 --- + 当 `injectQuery` 查询失败时(查询函数抛出错误),若该查询请求未达到最大连续重试次数(默认为 `3`)或提供了判断是否允许重试的函数,TanStack Query 将自动重试该查询。 您可以在全局级别和单个查询级别配置重试行为: diff --git a/docs/zh-hans/framework/angular/guides/scroll-restoration.md b/docs/zh-hans/framework/angular/guides/scroll-restoration.md index 25a9b03b8e1..791f8a2337f 100644 --- a/docs/zh-hans/framework/angular/guides/scroll-restoration.md +++ b/docs/zh-hans/framework/angular/guides/scroll-restoration.md @@ -5,4 +5,3 @@ id: scroll-restoration title: Scroll Restoration ref: docs/zh-hans/framework/react/guides/scroll-restoration.md --- - diff --git a/docs/zh-hans/framework/angular/installation.md b/docs/zh-hans/framework/angular/installation.md index c21f97f9b2c..3b8a613c110 100644 --- a/docs/zh-hans/framework/angular/installation.md +++ b/docs/zh-hans/framework/angular/installation.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:50:07.866Z' id: installation title: 安装 --- + > 重要提示:当前该库处于实验阶段。这意味着在次要版本和补丁版本中可能会发生破坏性变更。请谨慎升级。如果您在生产环境中使用此实验阶段的库,请将版本锁定到具体的补丁版本以避免意外的破坏性变更。 ### NPM diff --git a/docs/zh-hans/framework/angular/overview.md b/docs/zh-hans/framework/angular/overview.md index 45cdd2969fc..79af75fce07 100644 --- a/docs/zh-hans/framework/angular/overview.md +++ b/docs/zh-hans/framework/angular/overview.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:51:31.172Z' id: overview title: 概述 --- + > 重要提示:该库目前处于实验阶段。这意味着在次要版本和补丁版本中可能会出现破坏性变更。升级时请谨慎操作。如果您在生产环境中使用此实验阶段的库,请将版本锁定到具体的补丁版本以避免意外的破坏性变更。 `@tanstack/angular-query-experimental` 包为通过 Angular 使用 TanStack Query 提供了一流的 API。 diff --git a/docs/zh-hans/framework/angular/quick-start.md b/docs/zh-hans/framework/angular/quick-start.md index 7c9aea7819f..f2b5036d2d2 100644 --- a/docs/zh-hans/framework/angular/quick-start.md +++ b/docs/zh-hans/framework/angular/quick-start.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-03T22:08:27.205Z' id: quick-start title: 快速开始 --- + > 重要提示:当前该库处于实验阶段。这意味着次要版本和补丁版本都可能包含破坏性变更。升级时请谨慎操作。如果在生产环境中使用实验阶段的版本,请锁定到具体的补丁版本以避免意外的破坏性变更。 [//]: # '示例' diff --git a/docs/zh-hans/framework/angular/reference/functions/injectmutation.md b/docs/zh-hans/framework/angular/reference/functions/injectmutation.md index eed94ef9a5d..5639769da43 100644 --- a/docs/zh-hans/framework/angular/reference/functions/injectmutation.md +++ b/docs/zh-hans/framework/angular/reference/functions/injectmutation.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:09:50.452Z' id: injectMutation title: 函数 / injectMutation --- + # 函数: injectMutation() ```ts diff --git a/docs/zh-hans/framework/angular/reference/functions/injectquery.md b/docs/zh-hans/framework/angular/reference/functions/injectquery.md index 89f5a932fc6..03429a6ca1d 100644 --- a/docs/zh-hans/framework/angular/reference/functions/injectquery.md +++ b/docs/zh-hans/framework/angular/reference/functions/injectquery.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:09:34.795Z' id: injectQuery title: 函数 / injectQuery --- + # 函数: injectQuery() 注入一个查询:声明式依赖与异步数据源的绑定关系,该数据源与唯一键相关联。 diff --git a/docs/zh-hans/framework/angular/typescript.md b/docs/zh-hans/framework/angular/typescript.md index b9b385a7dbf..b038e4b0b9b 100644 --- a/docs/zh-hans/framework/angular/typescript.md +++ b/docs/zh-hans/framework/angular/typescript.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:55:35.083Z' id: typescript title: TypeScript --- + TanStack Query 现已采用 **TypeScript** 编写,确保库与您的项目具备类型安全! 注意事项: diff --git a/docs/zh-hans/framework/angular/zoneless.md b/docs/zh-hans/framework/angular/zoneless.md index ab2b2560ca4..fd2899ec21c 100644 --- a/docs/zh-hans/framework/angular/zoneless.md +++ b/docs/zh-hans/framework/angular/zoneless.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:49:56.860Z' id: zoneless title: 无 Zone --- + 由于 TanStack Query 的 Angular 适配器基于信号机制构建,因此它能完全支持无区域(Zoneless)模式! 无区域模式的优势包括提升性能和改进调试体验。具体细节请参阅 [Angular 官方文档](https://angular.dev/guide/experimental/zoneless)。 diff --git a/docs/zh-hans/framework/react/community/community-projects.md b/docs/zh-hans/framework/react/community/community-projects.md index 6a2857b0fc6..2ae58b6513a 100644 --- a/docs/zh-hans/framework/react/community/community-projects.md +++ b/docs/zh-hans/framework/react/community/community-projects.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:59:00.632Z' id: community-projects title: 社区项目 --- + 有许多社区项目基于 React Query 构建,并利用它提供额外功能或增强开发者体验。以下项目按字母顺序排列。如果您有希望添加到列表中的项目,请提交 PR! > 请注意,这些项目完全由社区维护。如有相关问题,请联系项目维护者。 diff --git a/docs/zh-hans/framework/react/community/tkdodos-blog.md b/docs/zh-hans/framework/react/community/tkdodos-blog.md index 8795e5b0903..3cd65b76d64 100644 --- a/docs/zh-hans/framework/react/community/tkdodos-blog.md +++ b/docs/zh-hans/framework/react/community/tkdodos-blog.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:58:18.522Z' id: tkdodos-blog title: TkDodo 的博客 --- + TanStack Query 的维护者 [TkDodo](https://bsky.app/profile/tkdodo.eu) 撰写了一系列关于该库使用与实践的博客文章。部分文章展示了通用最佳实践,但大多数内容都带有鲜明的个人观点。 ## [#1: 实战 React Query](https://tkdodo.eu/blog/practical-react-query) diff --git a/docs/zh-hans/framework/react/comparison.md b/docs/zh-hans/framework/react/comparison.md index 3fabeb4beec..577e17989d6 100644 --- a/docs/zh-hans/framework/react/comparison.md +++ b/docs/zh-hans/framework/react/comparison.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:32:05.206Z' id: comparison title: 对比 --- + > 本对比表格力求准确公正。若您使用过其中任一库并认为信息有待完善,欢迎通过页面底部的 "Edit this page on Github" 链接提交修改建议(需附说明或证据)。 功能/能力说明: @@ -13,54 +14,54 @@ title: 对比 - 🔶 官方支持且文档完备,但需用户自行编写实现代码 - 🛑 无官方支持或文档 -| | React Query | SWR [_(官网)_][swr] | Apollo Client [_(官网)_][apollo] | RTK-Query [_(官网)_][rtk-query] | React Router [_(官网)_][react-router] | -| -------------------------------------------------- | ---------------------------------------- | ---------------------------------------- | ------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------- | -| GitHub 仓库/星标数 | [![][stars-react-query]][gh-react-query] | [![][stars-swr]][gh-swr] | [![][stars-apollo]][gh-apollo] | [![][stars-rtk-query]][gh-rtk-query] | [![][stars-react-router]][gh-react-router] | -| 平台要求 | React | React | React, GraphQL | Redux | React | -| 官方对比 | | (无) | (无) | [对比][rtk-query-comparison] | (无) | -| 支持的查询语法 | Promise, REST, GraphQL | Promise, REST, GraphQL | GraphQL, 任意 (响应式变量) | Promise, REST, GraphQL | Promise, REST, GraphQL | -| 支持的框架 | React | React | React + 其他 | 任意 | React | -| 缓存策略 | 层级键值对 | 唯一键值对 | 规范化 Schema | 唯一键值对 | 嵌套路由 -> 值 | -| 缓存键策略 | JSON | JSON | GraphQL 查询 | JSON | 路由路径 | -| 缓存变更检测 | 深度比较键值(稳定序列化) | 深度比较键值(稳定序列化) | 深度比较键值(非稳定序列化) | 键值引用相等 (===) | 路由变更 | -| 数据变更检测 | 深度比较 + 结构共享 | 深度比较 (通过 `stable-hash`) | 深度比较(非稳定序列化) | 键值引用相等 (===) | 加载器执行 | -| 数据记忆化 | 完整结构共享 | 标识相等 (===) | 规范化标识 | 标识相等 (===) | 标识相等 (===) | -| 包体积 | [![][bp-react-query]][bpl-react-query] | [![][bp-swr]][bpl-swr] | [![][bp-apollo]][bpl-apollo] | [![][bp-rtk-query]][bpl-rtk-query] | [![][bp-react-router]][bpl-react-router] + [![][bp-history]][bpl-history] | -| API 定义位置 | 组件内,外部配置 | 组件内 | GraphQL Schema | 外部配置 | 路由树配置 | -| 查询 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 缓存持久化 | ✅ | ✅ | ✅ | ✅ | 🛑 仅活跃路由 8 | -| 开发者工具 | ✅ | ✅ | ✅ | ✅ | 🛑 | -| 轮询/间隔查询 | ✅ | ✅ | ✅ | ✅ | 🛑 | -| 并行查询 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 依赖查询 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 分页查询 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 无限查询 | ✅ | ✅ | ✅ | 🛑 | 🛑 | -| 双向无限查询 | ✅ | 🔶 | 🔶 | 🛑 | 🛑 | -| 无限查询重获取 | ✅ | ✅ | 🛑 | 🛑 | 🛑 | -| 滞后查询数据1 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 选择器 | ✅ | 🛑 | ✅ | ✅ | N/A | -| 初始数据 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 滚动恢复 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 缓存操作 | ✅ | ✅ | ✅ | ✅ | 🛑 | -| 过时查询丢弃 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 渲染批处理与优化2 | ✅ | ✅ | 🛑 | ✅ | ✅ | -| 自动垃圾回收 | ✅ | 🛑 | 🛑 | ✅ | N/A | -| 变更钩子 | ✅ | ✅ | ✅ | ✅ | ✅ | -| 离线变更支持 | ✅ | 🛑 | 🟡 | 🛑 | 🛑 | -| 预获取 API | ✅ | ✅ | ✅ | ✅ | ✅ | -| 查询取消 | ✅ | 🛑 | 🛑 | 🛑 | ✅ | -| 部分查询匹配3 | ✅ | 🔶 | ✅ | ✅ | N/A | -| 后台重新验证 (Stale While Revalidate) | ✅ | ✅ | ✅ | ✅ | 🛑 | -| 过期时间配置 | ✅ | 🛑7 | 🛑 | ✅ | 🛑 | -| 预使用查询/变更配置4 | ✅ | 🛑 | ✅ | ✅ | ✅ | -| 窗口聚焦重获取 | ✅ | ✅ | 🛑 | ✅ | 🛑 | -| 网络状态重获取 | ✅ | ✅ | ✅ | ✅ | 🛑 | -| 通用缓存脱水/再水合 | ✅ | 🛑 | ✅ | ✅ | ✅ | -| 离线缓存 | ✅ | 🛑 | ✅ | 🔶 | 🛑 | -| React Suspense 支持 | ✅ | ✅ | ✅ | 🛑 | ✅ | -| 抽象化/框架无关核心 | ✅ | 🛑 | ✅ | ✅ | 🛑 | -| 变更后自动重获取5 | 🔶 | 🔶 | ✅ | ✅ | ✅ | -| 规范化缓存6 | 🛑 | 🛑 | ✅ | 🛑 | 🛑 | +| | React Query | SWR [_(官网)_][swr] | Apollo Client [_(官网)_][apollo] | RTK-Query [_(官网)_][rtk-query] | React Router [_(官网)_][react-router] | +| ------------------------------------- | ---------------------------------------- | ----------------------------- | -------------------------------- | ------------------------------------ | ------------------------------------------------------------------------- | +| GitHub 仓库/星标数 | [![][stars-react-query]][gh-react-query] | [![][stars-swr]][gh-swr] | [![][stars-apollo]][gh-apollo] | [![][stars-rtk-query]][gh-rtk-query] | [![][stars-react-router]][gh-react-router] | +| 平台要求 | React | React | React, GraphQL | Redux | React | +| 官方对比 | | (无) | (无) | [对比][rtk-query-comparison] | (无) | +| 支持的查询语法 | Promise, REST, GraphQL | Promise, REST, GraphQL | GraphQL, 任意 (响应式变量) | Promise, REST, GraphQL | Promise, REST, GraphQL | +| 支持的框架 | React | React | React + 其他 | 任意 | React | +| 缓存策略 | 层级键值对 | 唯一键值对 | 规范化 Schema | 唯一键值对 | 嵌套路由 -> 值 | +| 缓存键策略 | JSON | JSON | GraphQL 查询 | JSON | 路由路径 | +| 缓存变更检测 | 深度比较键值(稳定序列化) | 深度比较键值(稳定序列化) | 深度比较键值(非稳定序列化) | 键值引用相等 (===) | 路由变更 | +| 数据变更检测 | 深度比较 + 结构共享 | 深度比较 (通过 `stable-hash`) | 深度比较(非稳定序列化) | 键值引用相等 (===) | 加载器执行 | +| 数据记忆化 | 完整结构共享 | 标识相等 (===) | 规范化标识 | 标识相等 (===) | 标识相等 (===) | +| 包体积 | [![][bp-react-query]][bpl-react-query] | [![][bp-swr]][bpl-swr] | [![][bp-apollo]][bpl-apollo] | [![][bp-rtk-query]][bpl-rtk-query] | [![][bp-react-router]][bpl-react-router] + [![][bp-history]][bpl-history] | +| API 定义位置 | 组件内,外部配置 | 组件内 | GraphQL Schema | 外部配置 | 路由树配置 | +| 查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 缓存持久化 | ✅ | ✅ | ✅ | ✅ | 🛑 仅活跃路由 8 | +| 开发者工具 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 轮询/间隔查询 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 并行查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 依赖查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 分页查询 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 无限查询 | ✅ | ✅ | ✅ | 🛑 | 🛑 | +| 双向无限查询 | ✅ | 🔶 | 🔶 | 🛑 | 🛑 | +| 无限查询重获取 | ✅ | ✅ | 🛑 | 🛑 | 🛑 | +| 滞后查询数据1 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 选择器 | ✅ | 🛑 | ✅ | ✅ | N/A | +| 初始数据 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 滚动恢复 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 缓存操作 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 过时查询丢弃 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 渲染批处理与优化2 | ✅ | ✅ | 🛑 | ✅ | ✅ | +| 自动垃圾回收 | ✅ | 🛑 | 🛑 | ✅ | N/A | +| 变更钩子 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 离线变更支持 | ✅ | 🛑 | 🟡 | 🛑 | 🛑 | +| 预获取 API | ✅ | ✅ | ✅ | ✅ | ✅ | +| 查询取消 | ✅ | 🛑 | 🛑 | 🛑 | ✅ | +| 部分查询匹配3 | ✅ | 🔶 | ✅ | ✅ | N/A | +| 后台重新验证 (Stale While Revalidate) | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 过期时间配置 | ✅ | 🛑7 | 🛑 | ✅ | 🛑 | +| 预使用查询/变更配置4 | ✅ | 🛑 | ✅ | ✅ | ✅ | +| 窗口聚焦重获取 | ✅ | ✅ | 🛑 | ✅ | 🛑 | +| 网络状态重获取 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 通用缓存脱水/再水合 | ✅ | 🛑 | ✅ | ✅ | ✅ | +| 离线缓存 | ✅ | 🛑 | ✅ | 🔶 | 🛑 | +| React Suspense 支持 | ✅ | ✅ | ✅ | 🛑 | ✅ | +| 抽象化/框架无关核心 | ✅ | 🛑 | ✅ | ✅ | 🛑 | +| 变更后自动重获取5 | 🔶 | 🔶 | ✅ | ✅ | ✅ | +| 规范化缓存6 | 🛑 | 🛑 | ✅ | 🛑 | 🛑 | ### 注解 diff --git a/docs/zh-hans/framework/react/devtools.md b/docs/zh-hans/framework/react/devtools.md index 96a59607710..9a8e9ea28c3 100644 --- a/docs/zh-hans/framework/react/devtools.md +++ b/docs/zh-hans/framework/react/devtools.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:27:37.043Z' id: devtools title: 开发者工具 --- + # Devtools 欢呼雀跃吧,因为 React Query 配备了专属开发者工具!🥳 diff --git a/docs/zh-hans/framework/react/graphql.md b/docs/zh-hans/framework/react/graphql.md index 03e221ef74d..2425c8172f4 100644 --- a/docs/zh-hans/framework/react/graphql.md +++ b/docs/zh-hans/framework/react/graphql.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:24:54.134Z' id: graphql title: GraphQL --- + 由于 React Query 的获取机制是基于 Promise 无感知构建的,你可以将 React Query 与任何异步数据获取客户端一起使用,包括 GraphQL! > 请注意,React Query 不支持规范化缓存。虽然绝大多数用户实际上并不需要规范化缓存,甚至从中获得的收益比他们想象的要少得多,但在极少数情况下可能需要它。因此,请务必先与我们确认,以确保这确实是您所需要的功能! diff --git a/docs/zh-hans/framework/react/guides/advanced-ssr.md b/docs/zh-hans/framework/react/guides/advanced-ssr.md index 70a8f29277c..57aeeecb1fd 100644 --- a/docs/zh-hans/framework/react/guides/advanced-ssr.md +++ b/docs/zh-hans/framework/react/guides/advanced-ssr.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:16:25.641Z' id: advanced-ssr title: 高级服务端渲染 --- + 欢迎阅读高级服务端渲染 (Advanced Server Rendering) 指南,这里您将全面了解如何在流式渲染、服务器组件 (Server Components) 和 Next.js 应用路由 (app router) 中使用 React Query。 建议先阅读[服务端渲染与注水 (Server Rendering & Hydration)](./ssr.md) 指南了解 SSR 基础,以及[性能与请求瀑布流 (Performance & Request Waterfalls)](./request-waterfalls.md) 和[预取与路由集成 (Prefetching & Router Integration)](./prefetching.md) 获取背景知识。 @@ -18,7 +19,7 @@ title: 高级服务端渲染 ### 术语说明 -目前我们一直使用_服务端_和_客户端_这两个术语。需要注意的是,这与_服务器组件_和_客户端组件_并非一一对应。服务器组件保证只在服务端运行,但客户端组件实际上可以在两端运行,因为它们也会在初始_服务端渲染_阶段执行。 +目前我们一直使用*服务端*和*客户端*这两个术语。需要注意的是,这与*服务器组件*和*客户端组件*并非一一对应。服务器组件保证只在服务端运行,但客户端组件实际上可以在两端运行,因为它们也会在初始*服务端渲染*阶段执行。 可以理解为:服务器组件的渲染发生在"加载阶段"(始终在服务端),而客户端组件运行在"应用阶段"。这个应用阶段既可能在 SSR 时运行于服务端,也可能在浏览器中运行。具体运行位置和是否参与 SSR 取决于不同框架的实现。 @@ -41,7 +42,7 @@ function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { - // SSR 时通常需要设置默认 staleTime + // SSR 时通常需要设置默认 staleTime // 大于 0 以避免客户端立即重新获取 staleTime: 60 * 1000, }, @@ -313,6 +314,7 @@ export default async function PostsPage() { 当客户端重新验证数据时,服务组件中的 `文章数` 不会更新。如果设置 `staleTime: Infinity` 可以避免此问题,但这样就失去了使用 React Query 的意义。 适合使用 React Query 与服务器组件的场景: + - 已有 React Query 应用需要迁移到服务器组件 - 需要结合服务器组件优势的特定用例 - 框架提供的数据获取工具无法满足需求 @@ -399,9 +401,7 @@ export function Providers({ children }) { const queryClient = getQueryClient() return ( - - {children} - + {children} ) } diff --git a/docs/zh-hans/framework/react/guides/background-fetching-indicators.md b/docs/zh-hans/framework/react/guides/background-fetching-indicators.md index 98a88b04b22..c36c11a7412 100644 --- a/docs/zh-hans/framework/react/guides/background-fetching-indicators.md +++ b/docs/zh-hans/framework/react/guides/background-fetching-indicators.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:14:14.853Z' id: background-fetching-indicators title: 后台获取指示器 --- + # 后台获取指示器 查询的 `status === 'pending'` 状态足以显示查询的初始硬加载状态,但有时您可能希望额外显示一个指示器,表明查询正在后台重新获取。为此,查询还提供了一个 `isFetching` 布尔值,您可以用它来显示查询正处于获取状态,而无论 `status` 变量的状态如何: diff --git a/docs/zh-hans/framework/react/guides/caching.md b/docs/zh-hans/framework/react/guides/caching.md index 3c2e26d76f1..f8296c1ced6 100644 --- a/docs/zh-hans/framework/react/guides/caching.md +++ b/docs/zh-hans/framework/react/guides/caching.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:14:25.884Z' id: caching title: 缓存 --- + > 在阅读本指南前,请务必先通读[重要默认配置](./important-defaults.md) ## 基础示例 @@ -18,20 +19,24 @@ title: 缓存 假设我们使用默认的 `gcTime`(**5分钟**)和默认的 `staleTime`(`0`)。 1. 首次挂载 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })`: + - 由于此前没有使用 `['todos']` 查询键 (query key) 发起过其他查询,该查询会显示硬加载状态 (hard loading state) 并通过网络请求获取数据。 - 当网络请求完成时,返回的数据会缓存在 `['todos']` 键下。 - 在配置的 `staleTime`(默认为 `0`,即立即)后,数据会被标记为过时 (stale)。 2. 在其他位置挂载第二个 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例: + - 由于缓存中已存在第一个查询的 `['todos']` 键数据,会立即从缓存返回该数据。 - 新实例会使用其查询函数触发新的网络请求。 - 注意:无论两个 `fetchTodos` 查询函数是否相同,只要查询键相同,两个查询的 [`status`](../reference/useQuery.md)(包括 `isFetching`、`isPending` 等相关值)都会同步更新。 - 当请求成功完成时,`['todos']` 键下的缓存数据会更新,两个实例都会接收到新数据。 3. 当两个 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例都卸载且不再使用时: + - 由于该查询没有活跃实例,会使用 `gcTime`(默认为 **5分钟**)设置垃圾回收超时,之后该查询将被删除并回收。 4. 在缓存超时完成前,再次挂载 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })`: + - 查询会立即返回可用的缓存数据,同时在后台执行 `fetchTodos` 函数。成功完成后会用新数据更新缓存。 5. 最后一个 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 实例卸载。 diff --git a/docs/zh-hans/framework/react/guides/default-query-function.md b/docs/zh-hans/framework/react/guides/default-query-function.md index 3a6a6b7930a..28f408def8d 100644 --- a/docs/zh-hans/framework/react/guides/default-query-function.md +++ b/docs/zh-hans/framework/react/guides/default-query-function.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:13:52.792Z' id: default-query-function title: 默认查询函数 --- + 如果你出于任何原因,希望在整个应用中共享同一个查询函数,仅通过查询键 (query key) 来标识应该获取的数据,可以通过为 TanStack Query 提供一个 **默认查询函数 (default query function)** 来实现: [//]: # '示例' diff --git a/docs/zh-hans/framework/react/guides/dependent-queries.md b/docs/zh-hans/framework/react/guides/dependent-queries.md index 41b74eba0c2..4a0930bd43d 100644 --- a/docs/zh-hans/framework/react/guides/dependent-queries.md +++ b/docs/zh-hans/framework/react/guides/dependent-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:14:02.726Z' id: dependent-queries title: 依赖查询 --- + ## useQuery 依赖查询 依赖(或串行)查询需要等待前一个查询完成才能执行。实现这一功能非常简单,只需使用 `enabled` 选项来指定查询何时可以运行: diff --git a/docs/zh-hans/framework/react/guides/disabling-queries.md b/docs/zh-hans/framework/react/guides/disabling-queries.md index b3ae549bac9..ccd341f9985 100644 --- a/docs/zh-hans/framework/react/guides/disabling-queries.md +++ b/docs/zh-hans/framework/react/guides/disabling-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:14:23.656Z' id: disabling-queries title: 禁用/暂停查询 --- + 若需阻止查询自动执行,可通过设置 `enabled = false` 选项实现。该选项也支持传入返回布尔值的回调函数。 当 `enabled` 为 `false` 时: diff --git a/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md index a6076c6f51c..bdfbef44134 100644 --- a/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md +++ b/docs/zh-hans/framework/react/guides/does-this-replace-client-state.md @@ -4,10 +4,11 @@ translation-updated-at: '2025-05-06T04:14:00.417Z' id: does-this-replace-client-state title: '这会取代 [Redux, MobX 等] 吗?' --- + 首先,我们需要明确几个关键点: - TanStack Query 是一个**服务端状态 (server-state)** 库,负责管理服务器与客户端之间的异步操作 -- Redux、MobX、Zustand 等属于**客户端状态 (client-state)** 库,它们_虽然可以用来存储异步数据,但与 TanStack Query 这类工具相比效率较低_ +- Redux、MobX、Zustand 等属于**客户端状态 (client-state)** 库,它们*虽然可以用来存储异步数据,但与 TanStack Query 这类工具相比效率较低* 基于以上认知,简短的答案是:TanStack Query **取代了那些用于管理客户端状态中缓存数据的样板代码和相关连接逻辑,仅需寥寥数行代码即可实现相同功能**。 diff --git a/docs/zh-hans/framework/react/guides/filters.md b/docs/zh-hans/framework/react/guides/filters.md index 4f5bc799520..c191b471710 100644 --- a/docs/zh-hans/framework/react/guides/filters.md +++ b/docs/zh-hans/framework/react/guides/filters.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:14:12.171Z' id: filters title: 过滤器 --- + TanStack Query 中的部分方法接受 `QueryFilters` 或 `MutationFilters` 对象作为参数。 ## `查询过滤器 (Query Filters)` diff --git a/docs/zh-hans/framework/react/guides/important-defaults.md b/docs/zh-hans/framework/react/guides/important-defaults.md index c477560dca5..f31af8891e2 100644 --- a/docs/zh-hans/framework/react/guides/important-defaults.md +++ b/docs/zh-hans/framework/react/guides/important-defaults.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:12:55.170Z' id: important-defaults title: 重要默认设置 --- + 开箱即用,TanStack Query 采用了**激进但合理**的默认配置。**这些默认设置有时会让新用户措手不及,如果用户不了解它们,可能会增加学习或调试的难度。**在继续学习和使用 TanStack Query 时,请牢记以下要点: - 通过 `useQuery` 或 `useInfiniteQuery` 创建的查询实例默认**将缓存数据视为过时数据**。 diff --git a/docs/zh-hans/framework/react/guides/infinite-queries.md b/docs/zh-hans/framework/react/guides/infinite-queries.md index 14e85967558..287e3a4c7d2 100644 --- a/docs/zh-hans/framework/react/guides/infinite-queries.md +++ b/docs/zh-hans/framework/react/guides/infinite-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:13:56.367Z' id: infinite-queries title: 无限查询 --- + ## 无限查询 (Infinite Queries) 能够以增量方式"加载更多"数据到现有数据集或实现"无限滚动"的列表渲染,是一种非常常见的 UI 模式。TanStack Query 为此提供了一个名为 `useInfiniteQuery` 的特殊版本 `useQuery` 来查询这类列表。 diff --git a/docs/zh-hans/framework/react/guides/initial-query-data.md b/docs/zh-hans/framework/react/guides/initial-query-data.md index 43194997a15..49e3408fae5 100644 --- a/docs/zh-hans/framework/react/guides/initial-query-data.md +++ b/docs/zh-hans/framework/react/guides/initial-query-data.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:13:31.868Z' id: initial-query-data title: 初始查询数据 --- + 在需要之前,有多种方式可以为查询缓存提供初始数据: - 声明式: diff --git a/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md index cc49cf5d950..919c7f5b05a 100644 --- a/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md +++ b/docs/zh-hans/framework/react/guides/invalidations-from-mutations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:12:43.101Z' id: invalidations-from-mutations title: 从变更中失效 --- + ## 变更触发的失效机制 仅使查询失效只是成功的一半,而明确**何时**触发失效才是关键。通常,当应用中的某个变更 (mutation) 成功执行时,极有可能存在与之关联的查询需要被标记为失效状态,甚至重新获取数据以反映变更带来的新变化。 diff --git a/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md b/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md index ddf589495b7..0dde03e9db1 100644 --- a/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md +++ b/docs/zh-hans/framework/react/guides/migrating-to-react-query-3.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:15:18.467Z' id: migrating-to-react-query-3 title: 迁移到 v3 --- + 以下是翻译内容: React Query 的早期版本已经非常出色,为库带来了许多令人惊叹的新特性、更多魔法以及整体上更优的体验。它们也推动了该库的大规模采用,同时为库带来了大量优化建议(问题/贡献),并揭示了一些需要进一步打磨以使库更完善的地方。v3 版本正是这些打磨的成果。 @@ -539,3 +540,4 @@ const unsubscribe = observer.subscribe((result) => { ```tsx const observer = new QueriesObserver(queryClient, [ { queryKey: ['post', 1], queryFn: fetch +``` diff --git a/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md b/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md index e6dcb20493f..6ced895189a 100644 --- a/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md +++ b/docs/zh-hans/framework/react/guides/migrating-to-react-query-4.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:15:18.700Z' id: migrating-to-react-query-4 title: 迁移到 v4 --- + ## 重大变更 v4 是一个主要版本,需要注意以下破坏性变更: @@ -35,6 +36,7 @@ npm install @tanstack/react-query-devtools 可通过以下命令之一运行迁移: 针对 `.js` 或 `.jsx` 文件: + ``` npx jscodeshift ./path/to/src/ \ --extensions=js,jsx \ @@ -42,6 +44,7 @@ npx jscodeshift ./path/to/src/ \ ``` 针对 `.ts` 或 `.tsx` 文件: + ``` npx jscodeshift ./path/to/src/ \ --extensions=ts,tsx \ @@ -77,6 +80,7 @@ v3 中查询键和变更键可以是字符串或数组。React Query 内部始 可通过以下命令之一运行迁移: 针对 `.js` 或 `.jsx` 文件: + ``` npx jscodeshift ./path/to/src/ \ --extensions=js,jsx \ @@ -84,6 +88,7 @@ npx jscodeshift ./path/to/src/ \ ``` 针对 `.ts` 或 `.tsx` 文件: + ``` npx jscodeshift ./path/to/src/ \ --extensions=ts,tsx \ diff --git a/docs/zh-hans/framework/react/guides/migrating-to-v5.md b/docs/zh-hans/framework/react/guides/migrating-to-v5.md index 6e949340842..3621d166c9f 100644 --- a/docs/zh-hans/framework/react/guides/migrating-to-v5.md +++ b/docs/zh-hans/framework/react/guides/migrating-to-v5.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:15:15.834Z' id: migrating-to-tanstack-query-5 title: 迁移到 v5 --- + ## 重大变更 v5 是一个主要版本,因此需要注意以下重大变更: diff --git a/docs/zh-hans/framework/react/guides/mutations.md b/docs/zh-hans/framework/react/guides/mutations.md index b0346baebdd..9b464085c90 100644 --- a/docs/zh-hans/framework/react/guides/mutations.md +++ b/docs/zh-hans/framework/react/guides/mutations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:14:31.297Z' id: mutations title: 变更 --- + 与查询不同,变更 (mutations) 通常用于创建/更新/删除数据或执行服务端副作用。为此,TanStack Query 导出了 `useMutation` 钩子。 以下是一个向服务器添加新待办事项的变更示例: @@ -172,10 +173,10 @@ useMutation({ useMutation({ mutationFn: addTodo, onSuccess: async () => { - console.log("我是第一个!") + console.log('我是第一个!') }, onSettled: async () => { - console.log("我是第二个!") + console.log('我是第二个!') }, }) ``` diff --git a/docs/zh-hans/framework/react/guides/network-mode.md b/docs/zh-hans/framework/react/guides/network-mode.md index 0e3bea25b64..ac58dfba947 100644 --- a/docs/zh-hans/framework/react/guides/network-mode.md +++ b/docs/zh-hans/framework/react/guides/network-mode.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:13:05.806Z' id: network-mode title: 网络模式 --- + TanStack Query 提供了三种不同的网络模式,用于区分在无网络连接时[查询(Queries)](./queries.md)和[变更(Mutations)](./mutations.md)的行为。该模式可以针对每个查询/变更单独设置,也可以通过查询/变更的全局默认值进行配置。 由于 TanStack Query 最常与数据获取库配合使用,其默认网络模式为[在线模式(online)](#network-mode-online)。 @@ -38,7 +39,7 @@ TanStack Query 提供了三种不同的网络模式,用于区分在无网络 ## 开发者工具(Devtools) -[TanStack Query开发者工具](../devtools.md)会显示处于`paused`状态的查询——这些查询本应获取数据但因断网而暂停。工具还提供_模拟离线行为_的切换按钮。请注意该按钮不会实际干扰网络连接(您可在浏览器开发者工具中操作),而是会将[OnlineManager](../../../reference/onlineManager.md)设置为离线状态。 +[TanStack Query开发者工具](../devtools.md)会显示处于`paused`状态的查询——这些查询本应获取数据但因断网而暂停。工具还提供*模拟离线行为*的切换按钮。请注意该按钮不会实际干扰网络连接(您可在浏览器开发者工具中操作),而是会将[OnlineManager](../../../reference/onlineManager.md)设置为离线状态。 ## 函数签名(Signature) diff --git a/docs/zh-hans/framework/react/guides/optimistic-updates.md b/docs/zh-hans/framework/react/guides/optimistic-updates.md index 30fdeeae1ff..2581b5a524b 100644 --- a/docs/zh-hans/framework/react/guides/optimistic-updates.md +++ b/docs/zh-hans/framework/react/guides/optimistic-updates.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:12:25.239Z' id: optimistic-updates title: 乐观更新 --- + React Query 提供了两种在变更操作完成前乐观更新 UI 的方式。你可以直接使用 `onMutate` 选项更新缓存,或者利用 `useMutation` 返回的 `variables` 来更新 UI。 ## 通过 UI 更新 diff --git a/docs/zh-hans/framework/react/guides/paginated-queries.md b/docs/zh-hans/framework/react/guides/paginated-queries.md index 5fa353857f8..a74ce04546f 100644 --- a/docs/zh-hans/framework/react/guides/paginated-queries.md +++ b/docs/zh-hans/framework/react/guides/paginated-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:51.300Z' id: paginated-queries title: 分页查询 --- + 分页数据的渲染是一种非常常见的 UI 模式,在 TanStack Query 中,只需将页码信息包含在查询键 (query key) 中即可"开箱即用": [//]: # 'Example' diff --git a/docs/zh-hans/framework/react/guides/parallel-queries.md b/docs/zh-hans/framework/react/guides/parallel-queries.md index 0a35d63a414..f75c9fa138c 100644 --- a/docs/zh-hans/framework/react/guides/parallel-queries.md +++ b/docs/zh-hans/framework/react/guides/parallel-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:37.356Z' id: parallel-queries title: 并行查询 --- + "并行 (Parallel)" 查询是指同时执行多个查询,以最大化数据获取的并发性。 ## 手动并行查询 diff --git a/docs/zh-hans/framework/react/guides/placeholder-query-data.md b/docs/zh-hans/framework/react/guides/placeholder-query-data.md index 0de9edd1494..b90b647c2fd 100644 --- a/docs/zh-hans/framework/react/guides/placeholder-query-data.md +++ b/docs/zh-hans/framework/react/guides/placeholder-query-data.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:43.190Z' id: placeholder-query-data title: 占位查询数据 --- + ## 什么是占位数据 (Placeholder Data)? 占位数据允许查询表现得好像已经拥有数据,类似于 `initialData` 选项,但**这些数据不会被持久化到缓存中**。这在以下场景中非常有用:当实际数据还在后台获取时,你已经拥有部分(或模拟)数据可以成功渲染查询结果。 diff --git a/docs/zh-hans/framework/react/guides/prefetching.md b/docs/zh-hans/framework/react/guides/prefetching.md index db929dcb7e5..9af3111b678 100644 --- a/docs/zh-hans/framework/react/guides/prefetching.md +++ b/docs/zh-hans/framework/react/guides/prefetching.md @@ -4,13 +4,14 @@ translation-updated-at: '2025-05-06T04:11:22.321Z' id: prefetching title: 预获取与路由集成 --- + 当您预知或推测某块数据即将被需要时,可以通过预取 (prefetching) 提前将该数据填充到缓存中,从而获得更快的用户体验。 预取存在几种不同的实现模式: 1. 在事件处理器中预取 -2. 在组件中预取 -3. 通过路由集成预取 +2. 在组件中预取 +3. 通过路由集成预取 4. 在服务端渲染时预取(路由集成的另一种形式) 本指南将探讨前三种模式,而第四种模式将在[《服务端渲染与注水指南》](./ssr.md)和[《高级服务端渲染指南》](./advanced-ssr.md)中深入讲解。 @@ -254,6 +255,7 @@ useEffect(() => { ``` 总结组件内预取的四种方案(根据场景选择): + - 使用 `usePrefetchQuery` 或 `usePrefetchInfiniteQuery` 在 Suspense 边界前预取 - 使用 `useQuery` 或 `useSuspenseQueries` 并忽略结果 - 在查询函数内预取 diff --git a/docs/zh-hans/framework/react/guides/queries.md b/docs/zh-hans/framework/react/guides/queries.md index 4f977493dc6..eb75d1a54b5 100644 --- a/docs/zh-hans/framework/react/guides/queries.md +++ b/docs/zh-hans/framework/react/guides/queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:52.666Z' id: queries title: 查询 --- + ## 查询基础 查询是与**唯一键**绑定的、对异步数据源的声明式依赖。查询可用于任何基于 Promise 的方法(包括 GET 和 POST 方法)从服务器获取数据。如果您的方法会修改服务器上的数据,建议改用[变更](./mutations.md)。 diff --git a/docs/zh-hans/framework/react/guides/query-cancellation.md b/docs/zh-hans/framework/react/guides/query-cancellation.md index 366c120e687..bb0291bd59d 100644 --- a/docs/zh-hans/framework/react/guides/query-cancellation.md +++ b/docs/zh-hans/framework/react/guides/query-cancellation.md @@ -4,15 +4,16 @@ translation-updated-at: '2025-05-06T04:09:59.909Z' id: query-cancellation title: 查询取消 --- + TanStack Query 为每个查询函数提供了一个 [`AbortSignal` 实例](https://developer.mozilla.org/docs/Web/API/AbortSignal)。当查询过期或变为非活跃状态时,该 `signal` 将被中止。这意味着所有查询均可取消,您可以根据需要在查询函数内部响应取消操作。最棒的是,您可以在享受自动取消带来的所有优势的同时,继续使用普通的 async/await 语法。 `AbortController` API 在[大多数运行时环境](https://developer.mozilla.org/docs/Web/API/AbortController#browser_compatibility)中可用,但如果您的运行时环境不支持,则需要提供 polyfill。现有[多种 polyfill 可选](https://www.npmjs.com/search?q=abortcontroller%20polyfill)。 ## 默认行为 -默认情况下,在 Promise 解析前卸载或不再使用的查询_不会_被取消。这意味着 Promise 解析后,结果数据仍会保留在缓存中。这对于已经开始接收查询但尚未完成就卸载组件的情况非常有用。如果您再次挂载组件且查询尚未被垃圾回收,数据将仍然可用。 +默认情况下,在 Promise 解析前卸载或不再使用的查询*不会*被取消。这意味着 Promise 解析后,结果数据仍会保留在缓存中。这对于已经开始接收查询但尚未完成就卸载组件的情况非常有用。如果您再次挂载组件且查询尚未被垃圾回收,数据将仍然可用。 -但如果消费了 `AbortSignal`,Promise 将被取消(例如中止 fetch 请求),因此查询也必须取消。取消查询将导致其状态_回退_到先前的状态。 +但如果消费了 `AbortSignal`,Promise 将被取消(例如中止 fetch 请求),因此查询也必须取消。取消查询将导致其状态*回退*到先前的状态。 ## 使用 `fetch` diff --git a/docs/zh-hans/framework/react/guides/query-functions.md b/docs/zh-hans/framework/react/guides/query-functions.md index d4062f898af..04e8d20cd89 100644 --- a/docs/zh-hans/framework/react/guides/query-functions.md +++ b/docs/zh-hans/framework/react/guides/query-functions.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:46.316Z' id: query-functions title: 查询函数 --- + # 查询函数 (Query Functions) 查询函数可以是**任何返回 Promise 的函数**。返回的 Promise 应当**解析数据 (resolve the data)** 或**抛出错误 (throw an error)**。 diff --git a/docs/zh-hans/framework/react/guides/query-invalidation.md b/docs/zh-hans/framework/react/guides/query-invalidation.md index 3c1128ad84e..96872646514 100644 --- a/docs/zh-hans/framework/react/guides/query-invalidation.md +++ b/docs/zh-hans/framework/react/guides/query-invalidation.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:50.856Z' id: query-invalidation title: 查询失效 --- + ## 查询失效 (Query Invalidation) 单纯等待查询因陈旧而重新获取并不总是有效,特别是当您明确知道由于用户操作导致某查询数据已过期时。为此,`QueryClient` 提供了 `invalidateQueries` 方法,允许您智能地将查询标记为陈旧状态,并可能触发重新获取! diff --git a/docs/zh-hans/framework/react/guides/query-keys.md b/docs/zh-hans/framework/react/guides/query-keys.md index 915148e43dc..84afe70d858 100644 --- a/docs/zh-hans/framework/react/guides/query-keys.md +++ b/docs/zh-hans/framework/react/guides/query-keys.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:40.213Z' id: query-keys title: 查询键 --- + TanStack Query 的核心是基于查询键 (query keys) 为你管理查询缓存。查询键在顶层必须是一个数组,可以简单到只包含单个字符串的数组,也可以复杂到包含多个字符串和嵌套对象的数组。只要查询键是可序列化的,并且**能唯一标识查询的数据**,你就可以使用它! ## 简单查询键 diff --git a/docs/zh-hans/framework/react/guides/query-options.md b/docs/zh-hans/framework/react/guides/query-options.md index 95f89d8fafc..31bd4307785 100644 --- a/docs/zh-hans/framework/react/guides/query-options.md +++ b/docs/zh-hans/framework/react/guides/query-options.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:06:24.141Z' id: query-options title: 查询选项 --- + ## 查询选项 (Query Options) 在多个地方共享 `queryKey` 和 `queryFn` 同时保持它们彼此关联的最佳方式之一是使用 `queryOptions` 辅助函数。在运行时,这个辅助函数仅返回你传入的内容,但[配合 TypeScript 使用时](../typescript.md#typing-query-options)它能带来诸多优势。你可以在一个地方定义查询的所有可能选项,并同时获得完整的类型推断和类型安全。 diff --git a/docs/zh-hans/framework/react/guides/query-retries.md b/docs/zh-hans/framework/react/guides/query-retries.md index 20567c030c9..4b964ad9422 100644 --- a/docs/zh-hans/framework/react/guides/query-retries.md +++ b/docs/zh-hans/framework/react/guides/query-retries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:04:04.683Z' id: query-retries title: 查询重试 --- + 当 `useQuery` 查询失败(查询函数抛出错误)时,如果该查询的请求未达到最大连续重试次数(默认为 `3`)或提供了判断是否允许重试的函数,TanStack Query 会自动重试该查询。 您可以在全局级别和单个查询级别配置重试行为: diff --git a/docs/zh-hans/framework/react/guides/render-optimizations.md b/docs/zh-hans/framework/react/guides/render-optimizations.md index 5f4bd27989f..9fcd0acf831 100644 --- a/docs/zh-hans/framework/react/guides/render-optimizations.md +++ b/docs/zh-hans/framework/react/guides/render-optimizations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:04:41.928Z' id: render-optimizations title: 渲染优化 --- + React Query 自动应用多项优化策略,确保组件仅在真正需要时重新渲染。这主要通过以下方式实现: ## 结构共享 (structural sharing) @@ -49,6 +50,7 @@ export const useTodoCount = () => { ### 记忆化 (memoization) `select` 函数仅在以下情况重新执行: + - 函数引用发生变化 - `data` 发生变化 diff --git a/docs/zh-hans/framework/react/guides/request-waterfalls.md b/docs/zh-hans/framework/react/guides/request-waterfalls.md index c4abc043815..178aeacfa5f 100644 --- a/docs/zh-hans/framework/react/guides/request-waterfalls.md +++ b/docs/zh-hans/framework/react/guides/request-waterfalls.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:11:08.176Z' id: request-waterfalls title: 性能与请求瀑布流 --- + 应用性能是一个广泛而复杂的领域,虽然 React Query 无法让你的 API 变得更快,但在使用 React Query 时仍需注意一些事项以确保最佳性能。 使用 React Query 或任何允许在组件内部获取数据的库时,最大的性能隐患是**请求瀑布流**。本页剩余部分将解释什么是请求瀑布流、如何发现它们,以及如何重构应用或 API 来避免它们。 @@ -46,7 +47,7 @@ title: 性能与请求瀑布流 发现和分析请求瀑布流的最佳方式通常是打开浏览器开发者工具的“网络 (Network)”选项卡。 -每个瀑布流至少代表一次与服务器的往返通信(除非资源被本地缓存)。因此,请求瀑布流的负面影响高度依赖用户的网络延迟。以前面的三重瀑布流为例,它实际上代表 4 次服务器往返。在 250ms 延迟(3G 网络或恶劣网络条件下很常见)的情况下,仅计算延迟时间就达到 4*250=1000ms。如果能将其优化为第一个示例中的仅 2 次往返,延迟时间将降至 500ms,背景图片的加载时间可能缩短一半! +每个瀑布流至少代表一次与服务器的往返通信(除非资源被本地缓存)。因此,请求瀑布流的负面影响高度依赖用户的网络延迟。以前面的三重瀑布流为例,它实际上代表 4 次服务器往返。在 250ms 延迟(3G 网络或恶劣网络条件下很常见)的情况下,仅计算延迟时间就达到 4\*250=1000ms。如果能将其优化为第一个示例中的仅 2 次往返,延迟时间将降至 500ms,背景图片的加载时间可能缩短一半! ## 请求瀑布流与 React Query diff --git a/docs/zh-hans/framework/react/guides/scroll-restoration.md b/docs/zh-hans/framework/react/guides/scroll-restoration.md index 236f8a897a2..039cb6b7caa 100644 --- a/docs/zh-hans/framework/react/guides/scroll-restoration.md +++ b/docs/zh-hans/framework/react/guides/scroll-restoration.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:15.717Z' id: scroll-restoration title: 滚动恢复 --- + ## 滚动恢复 (Scroll Restoration) 传统上,当你在网页浏览器中导航回之前访问过的页面时,会发现页面会自动滚动到你上次离开时的位置。这一功能被称为 **滚动恢复 (scroll restoration)**。但随着现代 Web 应用逐渐转向客户端数据获取 (client side data fetching),这一特性曾出现了一定程度的退化。不过在使用 TanStack Query 时,情况就完全不同了。 diff --git a/docs/zh-hans/framework/react/guides/ssr.md b/docs/zh-hans/framework/react/guides/ssr.md index 918f18f9827..de6e57e69df 100644 --- a/docs/zh-hans/framework/react/guides/ssr.md +++ b/docs/zh-hans/framework/react/guides/ssr.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:09:15.592Z' id: ssr title: 服务端渲染与注水 --- + # 服务端渲染与注水 (Server Rendering & Hydration) 在本指南中,您将学习如何结合服务端渲染使用 React Query。 diff --git a/docs/zh-hans/framework/react/guides/suspense.md b/docs/zh-hans/framework/react/guides/suspense.md index 17f3748d27f..e1f5315fe40 100644 --- a/docs/zh-hans/framework/react/guides/suspense.md +++ b/docs/zh-hans/framework/react/guides/suspense.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:06:04.912Z' id: suspense title: Suspense --- + React Query 也可以与 React 的 Suspense for Data Fetching APIs 配合使用。为此,我们提供了专用的钩子: - [useSuspenseQuery](../reference/useSuspenseQuery.md) diff --git a/docs/zh-hans/framework/react/guides/testing.md b/docs/zh-hans/framework/react/guides/testing.md index 1850d2de5f2..f680afdd986 100644 --- a/docs/zh-hans/framework/react/guides/testing.md +++ b/docs/zh-hans/framework/react/guides/testing.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:03:34.392Z' id: testing title: 测试 --- + React Query 通过钩子(hooks)机制工作——无论是我们提供的钩子还是围绕它们封装的自定义钩子。 在 React 17 或更早版本中,可以使用 [React Hooks Testing Library](https://react-hooks-testing-library.com/) 库为这些自定义钩子编写单元测试。 @@ -16,7 +17,7 @@ npm install @testing-library/react-hooks react-test-renderer --save-dev (`react-test-renderer` 库是 `@testing-library/react-hooks` 的 peer 依赖项,其版本需与当前使用的 React 版本对应。) -*注意*:在 React 18 或更高版本中,`renderHook` 可直接通过 `@testing-library/react` 包使用,不再需要 `@testing-library/react-hooks`。 +_注意_:在 React 18 或更高版本中,`renderHook` 可直接通过 `@testing-library/react` 包使用,不再需要 `@testing-library/react-hooks`。 ## 第一个测试 @@ -107,7 +108,7 @@ await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(result.current.data).toEqual({ answer: 42 }) ``` -这里使用 `waitFor` 等待查询状态变为成功,确保钩子已完成处理并返回正确数据。*注意*:在 React 18 中,`waitFor` 的语义有所变化。 +这里使用 `waitFor` 等待查询状态变为成功,确保钩子已完成处理并返回正确数据。_注意_:在 React 18 中,`waitFor` 的语义有所变化。 ## 测试加载更多/无限滚动 @@ -161,7 +162,7 @@ await waitFor(() => expectation.done() ``` -*注意*:React 18 中 `waitFor` 的语义变化同上文所述。 +_注意_:React 18 中 `waitFor` 的语义变化同上文所述。 ## 延伸阅读 diff --git a/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md b/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md index 99555615cec..11880b8cd03 100644 --- a/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md +++ b/docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:02:08.804Z' id: updates-from-mutation-responses title: 从变更响应中更新 --- + 处理那些**更新**服务器上对象的变更(mutations)时,变更响应中通常会自动返回新对象。与其重新获取该条目的查询并浪费网络请求去获取已有的数据,不如利用变更函数返回的对象,通过 [Query Client 的 `setQueryData`](../../../reference/QueryClient.md#queryclientsetquerydata) 方法立即用新数据更新现有查询: [//]: # '示例' diff --git a/docs/zh-hans/framework/react/guides/window-focus-refetching.md b/docs/zh-hans/framework/react/guides/window-focus-refetching.md index b427df98aaf..717dedb686c 100644 --- a/docs/zh-hans/framework/react/guides/window-focus-refetching.md +++ b/docs/zh-hans/framework/react/guides/window-focus-refetching.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:02:39.658Z' id: window-focus-refetching title: 窗口焦点重新获取 --- + 如果用户离开应用后返回,且查询数据已过时,**TanStack Query 会自动在后台为你请求最新数据**。你可以通过 `refetchOnWindowFocus` 选项全局或按查询禁用此行为: #### 全局禁用 diff --git a/docs/zh-hans/framework/react/installation.md b/docs/zh-hans/framework/react/installation.md index 2ae09b460fc..b273de6b612 100644 --- a/docs/zh-hans/framework/react/installation.md +++ b/docs/zh-hans/framework/react/installation.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T04:26:26.668Z' id: installation title: 安装 --- + # 安装 您可以通过 [NPM](https://npmjs.com/) 安装 React Query,或者通过 [ESM.sh](https://esm.sh/) 使用传统的 ` +请将以下代码尽可能放置在 Vue 应用的顶层,越靠近页面根节点效果越好: - -``` +```vue + -### 配置选项 + +``` -- `initialIsOpen: Boolean` - - 设为 `true` 可使开发工具默认展开 -- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` - - 默认为 `bottom-right` - - 控制 React Query 徽标按钮的位置,用于展开/收起开发工具面板 -- `position?: "top" | "bottom" | "left" | "right"` - - 默认为 `bottom` - - 开发工具面板的停靠位置 -- `client?: QueryClient` - - 传入自定义 QueryClient。未指定时使用最近上下文中的实例 -- `errorTypes?: { name: string; initializer: (query: Query) => TError}` - - 预定义可在查询中触发的错误类型。当从 UI 触发特定错误时,初始化函数(接收对应查询作为参数)将被调用,需返回一个 Error 对象 -- `styleNonce?: string` - - 用于向添加到 document head 的 style 标签传递 nonce 值。适用于需要使用内容安全策略 (CSP) nonce 允许内联样式的情况 -- `shadowDOMTarget?: ShadowRoot` - - 默认行为会将开发工具样式应用到 DOM 的 head 标签 - - 传入 shadow DOM 目标可使样式应用于 shadow DOM 而非主 DOM 的 head 标签 +### 配置选项 -## 传统开发工具 +- `initialIsOpen: Boolean` + - 设为 `true` 可使开发工具默认展开 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` + - 默认为 `bottom-right` + - 控制 React Query 徽标按钮的位置,用于展开/收起开发工具面板 +- `position?: "top" | "bottom" | "left" | "right"` + - 默认为 `bottom` + - 开发工具面板的停靠位置 +- `client?: QueryClient` + - 传入自定义 QueryClient。未指定时使用最近上下文中的实例 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 预定义可在查询中触发的错误类型。当从 UI 触发特定错误时,初始化函数(接收对应查询作为参数)将被调用,需返回一个 Error 对象 +- `styleNonce?: string` + - 用于向添加到 document head 的 style 标签传递 nonce 值。适用于需要使用内容安全策略 (CSP) nonce 允许内联样式的情况 +- `shadowDOMTarget?: ShadowRoot` + - 默认行为会将开发工具样式应用到 DOM 的 head 标签 + - 传入 shadow DOM 目标可使样式应用于 shadow DOM 而非主 DOM 的 head 标签 -Vue Query 可与 [Vue 官方开发工具](https://github.com/vuejs/devtools-next)无缝集成,添加自定义检查器和时间线事件。默认情况下开发工具代码会在生产构建时被 tree-shaking 移除。 +## 传统开发工具 -只需在插件配置中启用即可: +Vue Query 可与 [Vue 官方开发工具](https://github.com/vuejs/devtools-next)无缝集成,添加自定义检查器和时间线事件。默认情况下开发工具代码会在生产构建时被 tree-shaking 移除。 -```ts -app.use(VueQueryPlugin, { - enableDevtoolsV6Plugin: true, -}) -``` +只需在插件配置中启用即可: + +```ts +app.use(VueQueryPlugin, { + enableDevtoolsV6Plugin: true, +}) +``` 同时支持 v6 和 v7 版本的开发工具。 diff --git a/docs/zh-hans/framework/vue/graphql.md b/docs/zh-hans/framework/vue/graphql.md index edda620bc57..dcfbf6ece6b 100644 --- a/docs/zh-hans/framework/vue/graphql.md +++ b/docs/zh-hans/framework/vue/graphql.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:28:32.281Z' id: graphql title: GraphQL --- + 由于 Vue Query 的获取机制是基于 Promise 无感知构建的,您实际上可以将 Vue Query 与任何异步数据获取客户端一起使用,包括 GraphQL! > 请注意,Vue Query 不支持规范化缓存 (normalized caching)。虽然绝大多数用户实际上并不需要规范化缓存,甚至从中获得的收益比他们想象的要少得多,但在极少数情况下可能需要它,因此请务必先与我们确认,以确保这确实是您所需要的功能! diff --git a/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md b/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md index bd9faf6170b..e4be67b8c0d 100644 --- a/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md +++ b/docs/zh-hans/framework/vue/guides/background-fetching-indicators.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:14:09.749Z' id: background-fetching-indicators title: 后台获取指示器 --- + ## 后台获取状态指示器 查询的 `status === 'pending'` 状态足以显示查询的初始加载状态,但有时您可能希望额外显示一个指示器,表明查询正在后台重新获取数据。为此,查询还提供了一个 `isFetching` 布尔值,无论 `status` 变量的状态如何,您都可以用它来显示查询正处于获取状态: diff --git a/docs/zh-hans/framework/vue/guides/caching.md b/docs/zh-hans/framework/vue/guides/caching.md index 07c16e218bd..515aecb72a2 100644 --- a/docs/zh-hans/framework/vue/guides/caching.md +++ b/docs/zh-hans/framework/vue/guides/caching.md @@ -5,4 +5,3 @@ id: caching title: Caching Examples ref: docs/zh-hans/framework/react/guides/caching.md --- - diff --git a/docs/zh-hans/framework/vue/guides/custom-client.md b/docs/zh-hans/framework/vue/guides/custom-client.md index f8a4b06b127..38f5888ebe2 100644 --- a/docs/zh-hans/framework/vue/guides/custom-client.md +++ b/docs/zh-hans/framework/vue/guides/custom-client.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:13:50.563Z' id: custom-client title: 自定义客户端 --- + ### 自定义客户端 (Custom client) Vue Query 允许为 Vue 上下文提供自定义的 `QueryClient`。 diff --git a/docs/zh-hans/framework/vue/guides/default-query-function.md b/docs/zh-hans/framework/vue/guides/default-query-function.md index 021c9d40795..2ccddb107b8 100644 --- a/docs/zh-hans/framework/vue/guides/default-query-function.md +++ b/docs/zh-hans/framework/vue/guides/default-query-function.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:13:27.152Z' id: default-query-function title: 默认查询函数 --- + 如果你出于某种原因,希望在整个应用中共享同一个查询函数,仅通过查询键 (query key) 来标识应该获取什么数据,那么可以通过为 TanStack Query 提供一个**默认查询函数 (default query function)** 来实现: ```tsx diff --git a/docs/zh-hans/framework/vue/guides/dependent-queries.md b/docs/zh-hans/framework/vue/guides/dependent-queries.md index b3c5e92e294..2aec1235dcc 100644 --- a/docs/zh-hans/framework/vue/guides/dependent-queries.md +++ b/docs/zh-hans/framework/vue/guides/dependent-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:13:12.811Z' id: dependent-queries title: 依赖查询 --- + ## 依赖查询 (Dependent Query) 依赖查询(或称串行查询)需要等待前一个查询完成后才能执行。实现这一功能非常简单,只需使用 `enabled` 选项来指定查询何时可以运行: diff --git a/docs/zh-hans/framework/vue/guides/disabling-queries.md b/docs/zh-hans/framework/vue/guides/disabling-queries.md index bbffd772418..37970cfba51 100644 --- a/docs/zh-hans/framework/vue/guides/disabling-queries.md +++ b/docs/zh-hans/framework/vue/guides/disabling-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:12:44.238Z' id: disabling-queries title: 禁用/暂停查询 --- + 如果你想阻止某个查询自动执行,可以使用 `enabled = false` 选项。`enabled` 选项也接受返回布尔值的回调函数。 当 `enabled` 为 `false` 时: diff --git a/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md b/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md index 4e9b3713a2e..e2a97a8849a 100644 --- a/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md +++ b/docs/zh-hans/framework/vue/guides/does-this-replace-client-state.md @@ -4,10 +4,11 @@ translation-updated-at: '2025-05-06T16:09:32.012Z' id: does-this-replace-client-state title: '这会取代 [Vuex, Pinia] 吗?' --- + 首先,让我们明确几个关键点: - TanStack Query 是一个 **服务端状态 (server-state)** 库,负责管理服务端与客户端之间的异步操作 -- Vuex、Pinia、Zustand 等属于 **客户端状态 (client-state)** 库,它们_虽然可以存储异步数据,但与 TanStack Query 这类工具相比效率较低_ +- Vuex、Pinia、Zustand 等属于 **客户端状态 (client-state)** 库,它们*虽然可以存储异步数据,但与 TanStack Query 这类工具相比效率较低* 基于以上认知,简短的答案是:TanStack Query **会替换那些用于管理客户端状态中缓存数据的样板代码和相关逻辑,仅需寥寥数行代码即可实现相同功能**。 diff --git a/docs/zh-hans/framework/vue/guides/filters.md b/docs/zh-hans/framework/vue/guides/filters.md index c5c70963123..fc28c3d2613 100644 --- a/docs/zh-hans/framework/vue/guides/filters.md +++ b/docs/zh-hans/framework/vue/guides/filters.md @@ -5,4 +5,3 @@ id: filters title: Filters ref: docs/zh-hans/framework/react/guides/filters.md --- - diff --git a/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md b/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md index f0a1c5148e3..42fe84e423a 100644 --- a/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md +++ b/docs/zh-hans/framework/vue/guides/invalidations-from-mutations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:03:43.156Z' id: invalidations-from-mutations title: 从变更中失效 --- + ## 变更触发的失效机制 使查询失效只是成功的一半,而了解**何时**使其失效则是另一半。通常,当应用中的某个变更 (mutation) 成功执行时,应用中极有可能存在与之相关的查询需要失效,甚至可能需要重新获取数据以反映该变更带来的新变化。 diff --git a/docs/zh-hans/framework/vue/guides/migrating-to-v5.md b/docs/zh-hans/framework/vue/guides/migrating-to-v5.md index bfa4e1e6ff6..cfd6b4cb70b 100644 --- a/docs/zh-hans/framework/vue/guides/migrating-to-v5.md +++ b/docs/zh-hans/framework/vue/guides/migrating-to-v5.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:08:27.091Z' id: migrating-to-tanstack-query-5 title: 迁移到 v5 --- + ## 重大变更 v5 是一个主要版本,需要注意以下破坏性变更: diff --git a/docs/zh-hans/framework/vue/guides/mutations.md b/docs/zh-hans/framework/vue/guides/mutations.md index b03359859ab..4d91003cc03 100644 --- a/docs/zh-hans/framework/vue/guides/mutations.md +++ b/docs/zh-hans/framework/vue/guides/mutations.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:11:21.322Z' id: mutations title: 变更 --- + 与查询不同,变更 (mutations) 通常用于创建/更新/删除数据或执行服务端副作用。为此,TanStack Query 导出了 `useMutation` 钩子。 以下是一个向服务器添加新待办事项的变更示例: @@ -102,10 +103,10 @@ useMutation({ useMutation({ mutationFn: addTodo, onSuccess: async () => { - console.log("我先执行!") + console.log('我先执行!') }, onSettled: async () => { - console.log("我后执行!") + console.log('我后执行!') }, }) ``` diff --git a/docs/zh-hans/framework/vue/guides/network-mode.md b/docs/zh-hans/framework/vue/guides/network-mode.md index fddebf372a9..c37046d4145 100644 --- a/docs/zh-hans/framework/vue/guides/network-mode.md +++ b/docs/zh-hans/framework/vue/guides/network-mode.md @@ -5,4 +5,3 @@ id: network-mode title: Network Mode ref: docs/zh-hans/framework/react/guides/network-mode.md --- - diff --git a/docs/zh-hans/framework/vue/guides/optimistic-updates.md b/docs/zh-hans/framework/vue/guides/optimistic-updates.md index f1dc4b087f2..022f5660f44 100644 --- a/docs/zh-hans/framework/vue/guides/optimistic-updates.md +++ b/docs/zh-hans/framework/vue/guides/optimistic-updates.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/guides/optimistic-updates.md replace: React: Vue --- - diff --git a/docs/zh-hans/framework/vue/guides/paginated-queries.md b/docs/zh-hans/framework/vue/guides/paginated-queries.md index a5102e5ff14..fdb1a359d68 100644 --- a/docs/zh-hans/framework/vue/guides/paginated-queries.md +++ b/docs/zh-hans/framework/vue/guides/paginated-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:11:58.853Z' id: paginated-queries title: 分页查询 --- + 分页/滞后查询 渲染分页数据是一种非常常见的 UI 模式,在 TanStack Query 中,只需将页码信息包含在查询键 (query key) 中即可"开箱即用": diff --git a/docs/zh-hans/framework/vue/guides/parallel-queries.md b/docs/zh-hans/framework/vue/guides/parallel-queries.md index b4f327d5ddf..e1ece007c86 100644 --- a/docs/zh-hans/framework/vue/guides/parallel-queries.md +++ b/docs/zh-hans/framework/vue/guides/parallel-queries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:04:00.136Z' id: parallel-queries title: 并行查询 --- + "并行 (Parallel)" 查询是指同时执行多个查询以最大化数据获取的并发性。 ## 手动并行查询 diff --git a/docs/zh-hans/framework/vue/guides/placeholder-query-data.md b/docs/zh-hans/framework/vue/guides/placeholder-query-data.md index 4e5aae4445e..2e04fe0f288 100644 --- a/docs/zh-hans/framework/vue/guides/placeholder-query-data.md +++ b/docs/zh-hans/framework/vue/guides/placeholder-query-data.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:05:23.340Z' id: placeholder-query-data title: 占位查询数据 --- + ## 什么是占位数据? 占位数据允许查询表现得好像它已经有数据一样,类似于 `initialData` 选项,但**这些数据不会持久化到缓存中**。这在以下场景中非常有用:当实际数据在后台获取时,你已经拥有足够的局部(或模拟)数据来成功渲染查询。 diff --git a/docs/zh-hans/framework/vue/guides/prefetching.md b/docs/zh-hans/framework/vue/guides/prefetching.md index 9169feb507e..3059fd24e94 100644 --- a/docs/zh-hans/framework/vue/guides/prefetching.md +++ b/docs/zh-hans/framework/vue/guides/prefetching.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:02:02.404Z' id: prefetching title: 预获取 --- + ## 预取 (Prefetching) 如果足够幸运,您可能对用户即将执行的操作有充分了解,从而能在数据被实际需要之前预先获取!这种情况下,可以使用 `prefetchQuery` 方法预取查询结果并存入缓存: diff --git a/docs/zh-hans/framework/vue/guides/queries.md b/docs/zh-hans/framework/vue/guides/queries.md index c9da8297f6f..c87ff251790 100644 --- a/docs/zh-hans/framework/vue/guides/queries.md +++ b/docs/zh-hans/framework/vue/guides/queries.md @@ -4,11 +4,13 @@ translation-updated-at: '2025-05-06T16:04:51.198Z' id: queries title: 查询 --- + ## 查询基础 查询是与**唯一键**绑定的、对异步数据源的声明式依赖。查询可用于任何基于 Promise 的方法(包括 GET 和 POST 方法)从服务器获取数据。如果你的方法会修改服务器数据,我们建议改用[变更](./mutations.md)。 要在组件或自定义钩子中订阅查询,至少需要调用 `useQuery` 钩子并传入: + - 该查询的**唯一键** - 一个返回 Promise 的函数,该 Promise 会: - 解析出数据,或 @@ -29,11 +31,13 @@ const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) ``` `result` 对象包含若干关键状态,理解这些状态对高效使用至关重要。查询在任意时刻只能处于以下一种状态: + - `isPending` 或 `status === 'pending'` - 查询尚无数据 - `isError` 或 `status === 'error'` - 查询遇到错误 - `isSuccess` 或 `status === 'success'` - 查询成功且数据可用 除了这些主要状态,根据查询状态还可获取更多信息: + - `error` - 若查询处于 `isError` 状态,可通过 `error` 属性获取错误对象 - `data` - 若查询处于 `isSuccess` 状态,可通过 `data` 属性获取数据 - `isFetching` - 在任何状态下,只要查询正在获取数据(包括后台重新获取),`isFetching` 都会为 `true` @@ -87,6 +91,7 @@ const { status, data, error } = useQuery({ ### 获取状态 (FetchStatus) 除了 `status` 字段外,你还会获得一个额外的 `fetchStatus` 属性,其可选值为: + - `fetchStatus === 'fetching'` - 查询正在获取数据 - `fetchStatus === 'paused'` - 查询尝试获取数据但被暂停,详见[网络模式](./network-mode.md)指南 - `fetchStatus === 'idle'` - 查询当前未进行任何操作 @@ -94,9 +99,11 @@ const { status, data, error } = useQuery({ ### 为何需要两种状态? 后台重新获取和"过时但可用"逻辑使得 `status` 和 `fetchStatus` 的所有组合都可能出现。例如: + - 处于 `success` 状态的查询通常对应 `idle` 获取状态,但如果正在进行后台重新获取,则可能为 `fetching` - 刚挂载且无数据的查询通常处于 `pending` 状态和 `fetching` 获取状态,但若无网络连接则可能为 `paused` 因此要记住:查询可能处于 `pending` 状态但并未实际获取数据。简单来说: + - `status` 提供关于 `data` 的信息:我们是否有数据? - `fetchStatus` 提供关于 `queryFn` 的信息:它是否正在执行? diff --git a/docs/zh-hans/framework/vue/guides/query-invalidation.md b/docs/zh-hans/framework/vue/guides/query-invalidation.md index ec6824a59e9..be44d54ee3b 100644 --- a/docs/zh-hans/framework/vue/guides/query-invalidation.md +++ b/docs/zh-hans/framework/vue/guides/query-invalidation.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/guides/query-invalidation.md replace: react-query: vue-query --- - diff --git a/docs/zh-hans/framework/vue/guides/query-options.md b/docs/zh-hans/framework/vue/guides/query-options.md index 4b0a057303b..d9a3cb41bee 100644 --- a/docs/zh-hans/framework/vue/guides/query-options.md +++ b/docs/zh-hans/framework/vue/guides/query-options.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/guides/query-options.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/guides/query-retries.md b/docs/zh-hans/framework/vue/guides/query-retries.md index 12f6cb60c9b..cc72ec2ec55 100644 --- a/docs/zh-hans/framework/vue/guides/query-retries.md +++ b/docs/zh-hans/framework/vue/guides/query-retries.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:28:59.848Z' id: query-retries title: 查询重试 --- + 当 `useQuery` 查询失败(查询函数抛出错误)时,如果该查询的请求未达到最大连续重试次数(默认为 `3`)或提供了判断是否允许重试的函数,TanStack Query 会自动重试该查询。 您可以在全局级别和单个查询级别配置重试行为: diff --git a/docs/zh-hans/framework/vue/guides/scroll-restoration.md b/docs/zh-hans/framework/vue/guides/scroll-restoration.md index a293cfd8c06..f083d6b9c54 100644 --- a/docs/zh-hans/framework/vue/guides/scroll-restoration.md +++ b/docs/zh-hans/framework/vue/guides/scroll-restoration.md @@ -5,4 +5,3 @@ id: scroll-restoration title: Scroll Restoration ref: docs/zh-hans/framework/react/guides/scroll-restoration.md --- - diff --git a/docs/zh-hans/framework/vue/guides/ssr.md b/docs/zh-hans/framework/vue/guides/ssr.md index da4dfadf9ca..42cffac573c 100644 --- a/docs/zh-hans/framework/vue/guides/ssr.md +++ b/docs/zh-hans/framework/vue/guides/ssr.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:30:13.009Z' id: ssr title: SSR 与 Nuxt --- + Vue Query 支持在服务端预取多个查询,并将这些查询 _脱水 (dehydrate)_ 到 queryClient 中。这意味着服务端可以预先渲染页面加载时立即可用的标记,一旦 JS 可用,Vue Query 就能用库的全部功能来升级或 _水合 (hydrate)_ 这些查询。包括在客户端重新获取那些自服务端渲染后已过时的查询。 ## 使用 Nuxt.js diff --git a/docs/zh-hans/framework/vue/guides/suspense.md b/docs/zh-hans/framework/vue/guides/suspense.md index acd0cab04d7..ed517399a49 100644 --- a/docs/zh-hans/framework/vue/guides/suspense.md +++ b/docs/zh-hans/framework/vue/guides/suspense.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:26:47.297Z' id: suspense title: Suspense --- + > 注意:Vue Query 的 Suspense 模式目前属于实验性功能,与 Vue 自身的 Suspense 特性相同。这些 API 将会发生变化,除非您将 Vue 和 Vue Query 的版本锁定为相互兼容的补丁级别版本,否则不应在生产环境中使用。 Vue Query 也可以与 Vue 的新 [Suspense](https://vuejs.org/guide/built-ins/suspense.html) API 结合使用。 diff --git a/docs/zh-hans/framework/vue/guides/testing.md b/docs/zh-hans/framework/vue/guides/testing.md index c1a846b8e21..ac911e2a67a 100644 --- a/docs/zh-hans/framework/vue/guides/testing.md +++ b/docs/zh-hans/framework/vue/guides/testing.md @@ -4,4 +4,3 @@ translation-updated-at: '2025-05-06T05:25:27.162Z' id: testing title: Testing --- - diff --git a/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md b/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md index d73056df6b2..7f62ed59160 100644 --- a/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md +++ b/docs/zh-hans/framework/vue/guides/updates-from-mutation-responses.md @@ -5,4 +5,3 @@ id: updates-from-mutation-responses title: Updates from Mutation Responses ref: docs/zh-hans/framework/react/guides/updates-from-mutation-responses.md --- - diff --git a/docs/zh-hans/framework/vue/installation.md b/docs/zh-hans/framework/vue/installation.md index df21fe9b53e..eb45a4cdfe3 100644 --- a/docs/zh-hans/framework/vue/installation.md +++ b/docs/zh-hans/framework/vue/installation.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:25:47.547Z' id: installation title: 安装 --- + ## 安装 您可以通过 [NPM](https://npmjs.com) 安装 Vue Query。 diff --git a/docs/zh-hans/framework/vue/overview.md b/docs/zh-hans/framework/vue/overview.md index c971dce1999..7d1318fffea 100644 --- a/docs/zh-hans/framework/vue/overview.md +++ b/docs/zh-hans/framework/vue/overview.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:30:39.298Z' id: overview title: 概述 --- + TanStack Query(曾用名 Vue Query)常被称作 Web 应用缺失的数据获取库,用更专业的术语来说,它能让你在 Web 应用中轻松实现**获取、缓存、同步和更新服务端状态 (server state)**。 ## 动机 @@ -32,7 +33,7 @@ TanStack Query(曾用名 Vue Query)常被称作 Web 应用缺失的数据获 TanStack Query 无疑是管理服务端状态的最佳库之一。它具备**开箱即用的零配置体验**,并能随着应用增长按需定制。 -TanStack Query 让你能攻克_服务端状态_的棘手难题,在数据反客为主之前掌控全局。 +TanStack Query 让你能攻克*服务端状态*的棘手难题,在数据反客为主之前掌控全局。 从技术角度而言,TanStack Query 能: diff --git a/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md b/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md index f420483f605..29a33ef999e 100644 --- a/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md +++ b/docs/zh-hans/framework/vue/plugins/broadcastQueryClient.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/plugins/broadcastQueryClient.md replace: react-query: vue-query --- - diff --git a/docs/zh-hans/framework/vue/plugins/createPersister.md b/docs/zh-hans/framework/vue/plugins/createPersister.md index edb7608faca..7937567bd5b 100644 --- a/docs/zh-hans/framework/vue/plugins/createPersister.md +++ b/docs/zh-hans/framework/vue/plugins/createPersister.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:31:54.871Z' id: createPersister title: createPersister (实验性) --- + ## 安装 该工具作为一个独立包提供,可通过 `'@tanstack/query-persist-client-core'` 导入使用。 @@ -32,14 +33,14 @@ bun add @tanstack/query-persist-client-core ## 使用方式 -- 导入 `experimental_createPersister` 函数 +- 导入 `experimental_createPersister` 函数 - 创建一个新的 `experimental_createPersister` - - 可传入任何符合 `AsyncStorage` 或 `Storage` 接口的 `storage` 对象 + - 可传入任何符合 `AsyncStorage` 或 `Storage` 接口的 `storage` 对象 - 将该 `persister` 作为选项传递给 Query。可通过两种方式实现: - - 传递给 `QueryClient` 的 `defaultOptions` - - 或传递给任意 `useQuery` 钩子实例 - - 若作为 `defaultOptions` 传递,所有查询将被持久化到指定的 `storage`。还可通过 `filters` 进一步筛选。与 `persistClient` 插件不同,此方式不会将整个 QueryClient 作为单个条目持久化,而是分别持久化每个查询,并使用查询哈希 (query hash) 作为键名 - - 若提供给单个 `useQuery` 钩子,则仅该查询会被持久化 + - 传递给 `QueryClient` 的 `defaultOptions` + - 或传递给任意 `useQuery` 钩子实例 + - 若作为 `defaultOptions` 传递,所有查询将被持久化到指定的 `storage`。还可通过 `filters` 进一步筛选。与 `persistClient` 插件不同,此方式不会将整个 QueryClient 作为单个条目持久化,而是分别持久化每个查询,并使用查询哈希 (query hash) 作为键名 + - 若提供给单个 `useQuery` 钩子,则仅该查询会被持久化 这种方式无需存储整个 `QueryClient`,而是由您决定应用中哪些数据值得持久化。每个查询会按需恢复(首次使用时)和持久化(每次 `queryFn` 执行后),因此无需节流处理。恢复查询后仍会遵循 `staleTime` 设置——若数据被视为过期 (stale),恢复后将立即重新获取;若数据仍新鲜 (fresh),则不会执行 `queryFn`。 diff --git a/docs/zh-hans/framework/vue/quick-start.md b/docs/zh-hans/framework/vue/quick-start.md index 5c7e1fbf20c..d756223223d 100644 --- a/docs/zh-hans/framework/vue/quick-start.md +++ b/docs/zh-hans/framework/vue/quick-start.md @@ -4,11 +4,12 @@ translation-updated-at: '2025-05-03T22:08:17.182Z' id: quick-start title: 快速开始 --- + 以下代码片段简要展示了 Vue Query 的 3 个核心概念: -- [查询 (Queries)](./guides/queries.md) -- [变更 (Mutations)](./guides/mutations.md) -- [查询失效 (Query Invalidation)](./guides/query-invalidation.md) +- [查询 (Queries)](./guides/queries.md) +- [变更 (Mutations)](./guides/mutations.md) +- [查询失效 (Query Invalidation)](./guides/query-invalidation.md) 如需查看完整可运行的示例,请参考我们的 [基础 codesandbox 示例](../examples/basic) diff --git a/docs/zh-hans/framework/vue/reactivity.md b/docs/zh-hans/framework/vue/reactivity.md index f5d73023afd..d50a8a0bc1f 100644 --- a/docs/zh-hans/framework/vue/reactivity.md +++ b/docs/zh-hans/framework/vue/reactivity.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T16:03:25.519Z' id: reactivity title: 响应式 --- + Vue 采用 [信号范式 (the signals paradigm)](https://vuejs.org/guide/extras/reactivity-in-depth.html#connection-to-signals) 来处理和追踪响应式数据。该系统的核心特性是响应式系统仅会在特定监听的响应式属性上触发更新。因此需要确保当查询所依赖的值更新时,查询也能同步更新。 # 保持查询的响应性 diff --git a/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md b/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md index 41ee67b18db..3ad5283371e 100644 --- a/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md +++ b/docs/zh-hans/framework/vue/reference/infiniteQueryOptions.md @@ -5,4 +5,3 @@ id: infiniteQueryOptions title: infiniteQueryOptions ref: docs/zh-hans/framework/react/reference/infiniteQueryOptions.md --- - diff --git a/docs/zh-hans/framework/vue/reference/queryOptions.md b/docs/zh-hans/framework/vue/reference/queryOptions.md index 872140ce201..bf17a57ee0c 100644 --- a/docs/zh-hans/framework/vue/reference/queryOptions.md +++ b/docs/zh-hans/framework/vue/reference/queryOptions.md @@ -5,4 +5,3 @@ id: queryOptions title: queryOptions ref: docs/zh-hans/framework/react/reference/queryOptions.md --- - diff --git a/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md b/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md index 642dffe1484..6ce3575bebd 100644 --- a/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md +++ b/docs/zh-hans/framework/vue/reference/useInfiniteQuery.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useInfiniteQuery.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useIsFetching.md b/docs/zh-hans/framework/vue/reference/useIsFetching.md index fbab79e3d3d..56a0efb58f1 100644 --- a/docs/zh-hans/framework/vue/reference/useIsFetching.md +++ b/docs/zh-hans/framework/vue/reference/useIsFetching.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useIsFetching.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useIsMutating.md b/docs/zh-hans/framework/vue/reference/useIsMutating.md index bf51bbb9bb8..2dc68c93c63 100644 --- a/docs/zh-hans/framework/vue/reference/useIsMutating.md +++ b/docs/zh-hans/framework/vue/reference/useIsMutating.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useIsMutating.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useMutation.md b/docs/zh-hans/framework/vue/reference/useMutation.md index 1e985cc125f..9f2ac27117f 100644 --- a/docs/zh-hans/framework/vue/reference/useMutation.md +++ b/docs/zh-hans/framework/vue/reference/useMutation.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useMutation.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useMutationState.md b/docs/zh-hans/framework/vue/reference/useMutationState.md index 98ee639efc8..504c05b5837 100644 --- a/docs/zh-hans/framework/vue/reference/useMutationState.md +++ b/docs/zh-hans/framework/vue/reference/useMutationState.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useMutationState.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useQueries.md b/docs/zh-hans/framework/vue/reference/useQueries.md index a7e20c101fc..4ef2e9af831 100644 --- a/docs/zh-hans/framework/vue/reference/useQueries.md +++ b/docs/zh-hans/framework/vue/reference/useQueries.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useQueries.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useQuery.md b/docs/zh-hans/framework/vue/reference/useQuery.md index 30ca4611741..2f9e076448a 100644 --- a/docs/zh-hans/framework/vue/reference/useQuery.md +++ b/docs/zh-hans/framework/vue/reference/useQuery.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useQuery.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/reference/useQueryClient.md b/docs/zh-hans/framework/vue/reference/useQueryClient.md index e5b9c505342..fa939d59fcc 100644 --- a/docs/zh-hans/framework/vue/reference/useQueryClient.md +++ b/docs/zh-hans/framework/vue/reference/useQueryClient.md @@ -7,4 +7,3 @@ ref: docs/zh-hans/framework/react/reference/useQueryClient.md replace: '@tanstack/react-query': '@tanstack/vue-query' --- - diff --git a/docs/zh-hans/framework/vue/typescript.md b/docs/zh-hans/framework/vue/typescript.md index 17387f2bd5c..b30328cdd01 100644 --- a/docs/zh-hans/framework/vue/typescript.md +++ b/docs/zh-hans/framework/vue/typescript.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T05:28:24.242Z' id: typescript title: TypeScript --- + Vue Query 现已采用 **TypeScript** 编写,确保库与您的项目具备类型安全! 注意事项: diff --git a/docs/zh-hans/reference/InfiniteQueryObserver.md b/docs/zh-hans/reference/InfiniteQueryObserver.md index 73195a1f527..e5e80a4d7ee 100644 --- a/docs/zh-hans/reference/InfiniteQueryObserver.md +++ b/docs/zh-hans/reference/InfiniteQueryObserver.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:55:32.417Z' id: InfiniteQueryObserver title: InfiniteQueryObserver --- + ## `InfiniteQueryObserver` `InfiniteQueryObserver` 可用于观察和切换无限查询 (infinite queries)。 diff --git a/docs/zh-hans/reference/MutationCache.md b/docs/zh-hans/reference/MutationCache.md index a001e3f5f1c..d3f87233a5e 100644 --- a/docs/zh-hans/reference/MutationCache.md +++ b/docs/zh-hans/reference/MutationCache.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:51:33.721Z' id: MutationCache title: MutationCache --- + `MutationCache` 是用于存储变更 (mutations) 的容器。 **通常情况下,您不会直接与 MutationCache 交互,而是通过 `QueryClient` 进行操作。** diff --git a/docs/zh-hans/reference/QueriesObserver.md b/docs/zh-hans/reference/QueriesObserver.md index 0fc4441e4f1..1d639a62551 100644 --- a/docs/zh-hans/reference/QueriesObserver.md +++ b/docs/zh-hans/reference/QueriesObserver.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:50:00.104Z' id: QueriesObserver title: QueriesObserver --- + ## `QueriesObserver` `QueriesObserver` 可用于观察多个查询。 diff --git a/docs/zh-hans/reference/QueryCache.md b/docs/zh-hans/reference/QueryCache.md index 0a6fe281859..ef2f04df487 100644 --- a/docs/zh-hans/reference/QueryCache.md +++ b/docs/zh-hans/reference/QueryCache.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:56:14.760Z' id: QueryCache title: QueryCache --- + `QueryCache` 是 TanStack Query 的存储机制,用于存储其包含的所有查询数据、元信息和状态。 **通常情况下,您不会直接与 QueryCache 交互,而是通过 `QueryClient` 来操作特定缓存。** diff --git a/docs/zh-hans/reference/QueryClient.md b/docs/zh-hans/reference/QueryClient.md index 872bb22bdf0..63be2c031aa 100644 --- a/docs/zh-hans/reference/QueryClient.md +++ b/docs/zh-hans/reference/QueryClient.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:55:21.509Z' id: QueryClient title: QueryClient --- + ## `QueryClient` `QueryClient` 可用于与缓存进行交互: @@ -282,11 +283,11 @@ setQueryData(queryKey, newData) setQueryData(queryKey, (oldData) => newData) ``` -如果更新函数返回 `undefined`,则不会更新查询数据。如果更新函数接收到 `undefined` 作为输入,可以返回 `undefined` 以取消更新,从而_不_创建新的缓存条目。 +如果更新函数返回 `undefined`,则不会更新查询数据。如果更新函数接收到 `undefined` 作为输入,可以返回 `undefined` 以取消更新,从而*不*创建新的缓存条目。 **不可变性** -通过 `setQueryData` 进行的更新必须以_不可变_方式执行。**不要**尝试通过直接修改 `oldData` 或通过 `getQueryData` 检索的数据来写入缓存。 +通过 `setQueryData` 进行的更新必须以*不可变*方式执行。**不要**尝试通过直接修改 `oldData` 或通过 `getQueryData` 检索的数据来写入缓存。 ## `queryClient.getQueryState` @@ -457,3 +458,4 @@ queryClient.resetQueries({ queryKey, exact: true }) ```tsx if (queryClient.isFetching()) { console.log('至少有一个查询 +``` diff --git a/docs/zh-hans/reference/QueryObserver.md b/docs/zh-hans/reference/QueryObserver.md index f405e9e28d5..25b211f2c88 100644 --- a/docs/zh-hans/reference/QueryObserver.md +++ b/docs/zh-hans/reference/QueryObserver.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:50:39.683Z' id: QueryObserver title: QueryObserver --- + ## QueryObserver `QueryObserver` 可用于观察并在多个查询之间切换。 diff --git a/docs/zh-hans/reference/focusManager.md b/docs/zh-hans/reference/focusManager.md index fd482fa62ed..88e8e07df56 100644 --- a/docs/zh-hans/reference/focusManager.md +++ b/docs/zh-hans/reference/focusManager.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:52:22.525Z' id: FocusManager title: focusManager --- + `FocusManager` 用于管理 TanStack Query 中的焦点状态。 它可用于更改默认的事件监听器或手动修改焦点状态。 diff --git a/docs/zh-hans/reference/notifyManager.md b/docs/zh-hans/reference/notifyManager.md index dcfc2d2b738..1e777b47911 100644 --- a/docs/zh-hans/reference/notifyManager.md +++ b/docs/zh-hans/reference/notifyManager.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:50:31.682Z' id: NotifyManager title: notifyManager --- + `notifyManager` 负责在 Tanstack Query 中调度和批量处理回调。 它提供以下方法: diff --git a/docs/zh-hans/reference/onlineManager.md b/docs/zh-hans/reference/onlineManager.md index 12c878708fc..add5a345086 100644 --- a/docs/zh-hans/reference/onlineManager.md +++ b/docs/zh-hans/reference/onlineManager.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:52:00.643Z' id: OnlineManager title: onlineManager --- + `OnlineManager` 负责管理 TanStack Query 中的在线状态。它可用于修改默认的事件监听器或手动更改在线状态。 > 默认情况下,`onlineManager` 会假定存在活跃的网络连接,并通过监听 `window` 对象上的 `online` 和 `offline` 事件来检测状态变化。 diff --git a/docs/zh-hans/reference/streamedQuery.md b/docs/zh-hans/reference/streamedQuery.md index 93af0aa4cc0..cc74beca584 100644 --- a/docs/zh-hans/reference/streamedQuery.md +++ b/docs/zh-hans/reference/streamedQuery.md @@ -4,6 +4,7 @@ translation-updated-at: '2025-05-06T03:50:56.230Z' id: streamedQuery title: streamedQuery --- + `streamedQuery` 是一个辅助函数,用于创建一个从 [AsyncIterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) 流式传输数据的查询函数。数据将是接收到的所有数据块的数组。在接收到第一个数据块之前,查询将处于 `pending` 状态,但之后会转为 `success` 状态。查询将保持 `fetchStatus` 为 `fetching` 直到流结束。 ```tsx From c218e3fdab5f08ac8970ae45006f29be54108201 Mon Sep 17 00:00:00 2001 From: "yanqi.zong" Date: Tue, 6 May 2025 15:38:07 -0700 Subject: [PATCH 4/7] feat: update translate action --- .github/workflows/translate.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/translate.yml b/.github/workflows/translate.yml index 971ee72983a..33d69ce162f 100644 --- a/.github/workflows/translate.yml +++ b/.github/workflows/translate.yml @@ -6,10 +6,14 @@ on: '0 20 * * *' # Daily at 20:00 UTC (DeepSeek API off-peak pricing window 16:30-00:30 UTC) # Pacific Time: 1:00 PM PDT / 12:00 PM PST # Off-peak window in PT: ~9:30 AM to 5:30 PM PDT / ~8:30 AM to 4:30 PM PST + push: + # Run when merging from official repo to check if translations are outdated + branches: + - main workflow_dispatch: # Allow manual triggering inputs: custom_arguments: - description: 'Custom arguments to pass to the translation package command' + description: 'Custom arguments to pass to the translation package command. e.g., "-t zh-hans"' required: false type: string @@ -21,6 +25,8 @@ permissions: jobs: translate: runs-on: ubuntu-latest + # Run when manually triggered OR scheduled OR when the commit message contains "Merge pull request" + if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || contains(github.event.head_commit.message, 'Merge pull request') }} steps: # Use the translate-docs-action - name: Translate documentation @@ -28,7 +34,6 @@ jobs: with: # Required inputs api_key: ${{ secrets.OPENAI_API_KEY }} - # Optional inputs with their default values shown github_token: ${{ secrets.GITHUB_TOKEN }} custom_arguments: ${{ github.event.inputs.custom_arguments }} @@ -36,7 +41,11 @@ jobs: # base_branch: 'main' # pr_branch: 'docs/update-translations' # pr_title: 'Update translations' - # pr_body: 'This PR updates the documentation translations automatically.\n\nGenerated by the translate workflow.' + # The following uses YAML pipe syntax for multi-line strings + # pr_body: | + # This PR updates the documentation translations automatically. + # + # Generated by the translate workflow. # commit_message: 'docs: update documentation translations' # add_paths: 'docs/**' # enable_formatting: 'true' From 6a97d1f1fe190e8803a1bbf5947598b2e703034b Mon Sep 17 00:00:00 2001 From: "yanqi.zong" Date: Wed, 7 May 2025 22:38:06 -0700 Subject: [PATCH 5/7] feat: add zh-hant --- translation.config.mjs | 160 ++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/translation.config.mjs b/translation.config.mjs index f7446433a53..89d12cb5490 100644 --- a/translation.config.mjs +++ b/translation.config.mjs @@ -14,86 +14,86 @@ export default { // 格式: 'English term': '中文翻译' terms: {}, }, -// 'zh-Hant': { -// code: 'zh-Hant', -// name: 'Traditional Chinese', -// // 翻譯規則和指南 -// guide: ` -// - For technical terms that should not be fully translated, use the format: "繁體中文翻譯 (English term)" -// Example: "伺服器渲染 (SSR)" instead of just "SSR" or just "伺服器渲染" -// - Add a space between Chinese characters and English words/symbols to improve readability -// - Maintain consistent translations for common terms across the entire document -// `, -// // 常見技術術語翻譯詞典 -// // 格式: 'English term': '繁體中文翻譯' -// terms: {}, -// }, -// ja: { -// code: 'ja', -// name: 'Japanese', -// guide: ` -// - For technical terms that should not be fully translated, use the format: "日本語訳 (English term)" -// Example: "サーバーサイドレンダリング (SSR)" instead of just "SSR" or just "サーバーサイドレンダリング" -// - Maintain consistent translations for common terms across the entire document -// - Use katakana for foreign technical terms where appropriate -// `, -// terms: {}, -// }, -// es: { -// code: 'es', -// name: 'Spanish', -// guide: ` -// - For technical terms that should not be fully translated, use the format: "Traducción en español (English term)" -// Example: "Renderizado del lado del servidor (SSR)" instead of just "SSR" or just "Renderizado del lado del servidor" -// - Maintain consistent translations for common terms across the entire document -// - Use formal "usted" form instead of informal "tú" for instructions -// `, -// terms: {}, -// }, -// de: { -// code: 'de', -// name: 'German', -// guide: ` -// - For technical terms that should not be fully translated, use the format: "Deutsche Übersetzung (English term)" -// Example: "Server-seitiges Rendering (SSR)" instead of just "SSR" or just "Server-seitiges Rendering" -// - Maintain consistent translations for common terms across the entire document -// - Follow German capitalization rules for nouns -// `, -// terms: {}, -// }, -// fr: { -// code: 'fr', -// name: 'French', -// guide: ` -// - For technical terms that should not be fully translated, use the format: "Traduction française (English term)" -// Example: "Rendu côté serveur (SSR)" instead of just "SSR" or just "Rendu côté serveur" -// - Maintain consistent translations for common terms across the entire document -// - Use proper French punctuation with spaces before certain punctuation marks -// `, -// terms: {}, -// }, -// ru: { -// code: 'ru', -// name: 'Russian', -// guide: ` -// - For technical terms that should not be fully translated, use the format: "Русский перевод (English term)" -// Example: "Рендеринг на стороне сервера (SSR)" instead of just "SSR" or just "Рендеринг на стороне сервера" -// - Maintain consistent translations for common terms across the entire document -// - Use proper Russian cases for technical terms where appropriate -// `, -// terms: {}, -// }, -// ar: { -// code: 'ar', -// name: 'Arabic', -// guide: ` -// - For technical terms that should not be fully translated, use the format: "الترجمة العربية (English term)" -// Example: "العرض من جانب الخادم (SSR)" instead of just "SSR" or just "العرض من جانب الخادم" -// - Maintain consistent translations for common terms across the entire document -// - Arabic text should flow right-to-left, but keep code examples and technical terms left-to-right -// `, -// terms: {}, -// }, + 'zh-Hant': { + code: 'zh-Hant', + name: 'Traditional Chinese', + // 翻譯規則和指南 + guide: ` + - For technical terms that should not be fully translated, use the format: "繁體中文翻譯 (English term)" + Example: "伺服器渲染 (SSR)" instead of just "SSR" or just "伺服器渲染" + - Add a space between Chinese characters and English words/symbols to improve readability + - Maintain consistent translations for common terms across the entire document +`, + // 常見技術術語翻譯詞典 + // 格式: 'English term': '繁體中文翻譯' + terms: {}, + }, + // ja: { + // code: 'ja', + // name: 'Japanese', + // guide: ` + // - For technical terms that should not be fully translated, use the format: "日本語訳 (English term)" + // Example: "サーバーサイドレンダリング (SSR)" instead of just "SSR" or just "サーバーサイドレンダリング" + // - Maintain consistent translations for common terms across the entire document + // - Use katakana for foreign technical terms where appropriate + // `, + // terms: {}, + // }, + // es: { + // code: 'es', + // name: 'Spanish', + // guide: ` + // - For technical terms that should not be fully translated, use the format: "Traducción en español (English term)" + // Example: "Renderizado del lado del servidor (SSR)" instead of just "SSR" or just "Renderizado del lado del servidor" + // - Maintain consistent translations for common terms across the entire document + // - Use formal "usted" form instead of informal "tú" for instructions + // `, + // terms: {}, + // }, + // de: { + // code: 'de', + // name: 'German', + // guide: ` + // - For technical terms that should not be fully translated, use the format: "Deutsche Übersetzung (English term)" + // Example: "Server-seitiges Rendering (SSR)" instead of just "SSR" or just "Server-seitiges Rendering" + // - Maintain consistent translations for common terms across the entire document + // - Follow German capitalization rules for nouns + // `, + // terms: {}, + // }, + // fr: { + // code: 'fr', + // name: 'French', + // guide: ` + // - For technical terms that should not be fully translated, use the format: "Traduction française (English term)" + // Example: "Rendu côté serveur (SSR)" instead of just "SSR" or just "Rendu côté serveur" + // - Maintain consistent translations for common terms across the entire document + // - Use proper French punctuation with spaces before certain punctuation marks + // `, + // terms: {}, + // }, + // ru: { + // code: 'ru', + // name: 'Russian', + // guide: ` + // - For technical terms that should not be fully translated, use the format: "Русский перевод (English term)" + // Example: "Рендеринг на стороне сервера (SSR)" instead of just "SSR" or just "Рендеринг на стороне сервера" + // - Maintain consistent translations for common terms across the entire document + // - Use proper Russian cases for technical terms where appropriate + // `, + // terms: {}, + // }, + // ar: { + // code: 'ar', + // name: 'Arabic', + // guide: ` + // - For technical terms that should not be fully translated, use the format: "الترجمة العربية (English term)" + // Example: "العرض من جانب الخادم (SSR)" instead of just "SSR" or just "العرض من جانب الخادم" + // - Maintain consistent translations for common terms across the entire document + // - Arabic text should flow right-to-left, but keep code examples and technical terms left-to-right + // `, + // terms: {}, + // }, }, docsRoot: 'docs', docsContext: `TanStack Query (formerly known as React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your web applications a breeze. From f4c10e15e99e69dcde007df17ac269802feb6f99 Mon Sep 17 00:00:00 2001 From: xiaoyu2er Date: Thu, 8 May 2025 05:40:56 +0000 Subject: [PATCH 6/7] docs: update documentation translations --- docs/zh-hant/config.json | 1372 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1372 insertions(+) create mode 100644 docs/zh-hant/config.json diff --git a/docs/zh-hant/config.json b/docs/zh-hant/config.json new file mode 100644 index 00000000000..58976f0637a --- /dev/null +++ b/docs/zh-hant/config.json @@ -0,0 +1,1372 @@ +{ + "$schema": "https://raw.githubusercontent.com/TanStack/tanstack.com/main/tanstack-docs-config.schema.json", + "docSearch": { + "appId": "20TOVD6LOE", + "apiKey": "35bc6c51aa322700d1383e17c4f669f4", + "indexName": "tanstackquery" + }, + "sections": [ + { + "label": "開始使用", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "概述", + "to": "framework/react/overview" + }, + { + "label": "安裝", + "to": "framework/react/installation" + }, + { + "label": "快速開始", + "to": "framework/react/quick-start" + }, + { + "label": "開發工具", + "to": "framework/react/devtools" + }, + { + "label": "影片與演講", + "to": "framework/react/videos" + }, + { + "label": "比較", + "to": "framework/react/comparison" + }, + { + "label": "TypeScript", + "to": "framework/react/typescript" + }, + { + "label": "GraphQL", + "to": "framework/react/graphql" + }, + { + "label": "React Native", + "to": "framework/react/react-native" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "概述", + "to": "framework/solid/overview" + }, + { + "label": "快速開始", + "to": "framework/solid/quick-start" + }, + { + "label": "安裝", + "to": "framework/solid/installation" + }, + { + "label": "開發工具", + "to": "framework/solid/devtools" + }, + { + "label": "TypeScript", + "to": "framework/solid/typescript" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "概述", + "to": "framework/vue/overview" + }, + { + "label": "安裝", + "to": "framework/vue/installation" + }, + { + "label": "快速開始", + "to": "framework/vue/quick-start" + }, + { + "label": "開發工具", + "to": "framework/vue/devtools" + }, + { + "label": "TypeScript", + "to": "framework/vue/typescript" + }, + { + "label": "響應式", + "to": "framework/vue/reactivity" + }, + { + "label": "GraphQL", + "to": "framework/vue/graphql" + } + ] + }, + { + "label": "svelte", + "children": [ + { + "label": "概述", + "to": "framework/svelte/overview" + }, + { + "label": "安裝", + "to": "framework/svelte/installation" + }, + { + "label": "開發工具", + "to": "framework/svelte/devtools" + }, + { + "label": "SSR 與 SvelteKit", + "to": "framework/svelte/ssr" + }, + { + "label": "響應式", + "to": "framework/svelte/reactivity" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "概述", + "to": "framework/angular/overview" + }, + { + "label": "安裝", + "to": "framework/angular/installation" + }, + { + "label": "快速開始", + "to": "framework/angular/quick-start" + }, + { + "label": "Angular HttpClient 與其他資料獲取客戶端", + "to": "framework/angular/angular-httpclient-and-other-data-fetching-clients" + }, + { + "label": "開發工具", + "to": "framework/angular/devtools" + }, + { + "label": "TypeScript", + "to": "framework/angular/typescript" + }, + { + "label": "無區域 (Zoneless)", + "to": "framework/angular/zoneless" + } + ] + } + ] + }, + { + "label": "指南與概念", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "重要預設值", + "to": "framework/react/guides/important-defaults" + }, + { + "label": "查詢", + "to": "framework/react/guides/queries" + }, + { + "label": "查詢鍵", + "to": "framework/react/guides/query-keys" + }, + { + "label": "查詢函數", + "to": "framework/react/guides/query-functions" + }, + { + "label": "查詢選項", + "to": "framework/react/guides/query-options" + }, + { + "label": "網路模式", + "to": "framework/react/guides/network-mode" + }, + { + "label": "平行查詢", + "to": "framework/react/guides/parallel-queries" + }, + { + "label": "依賴查詢", + "to": "framework/react/guides/dependent-queries" + }, + { + "label": "背景獲取指示器", + "to": "framework/react/guides/background-fetching-indicators" + }, + { + "label": "視窗焦點重新獲取", + "to": "framework/react/guides/window-focus-refetching" + }, + { + "label": "停用/暫停查詢", + "to": "framework/react/guides/disabling-queries" + }, + { + "label": "查詢重試", + "to": "framework/react/guides/query-retries" + }, + { + "label": "分頁查詢", + "to": "framework/react/guides/paginated-queries" + }, + { + "label": "無限查詢", + "to": "framework/react/guides/infinite-queries" + }, + { + "label": "初始查詢資料", + "to": "framework/react/guides/initial-query-data" + }, + { + "label": "佔位查詢資料", + "to": "framework/react/guides/placeholder-query-data" + }, + { + "label": "變更", + "to": "framework/react/guides/mutations" + }, + { + "label": "查詢失效", + "to": "framework/react/guides/query-invalidation" + }, + { + "label": "來自變更的失效", + "to": "framework/react/guides/invalidations-from-mutations" + }, + { + "label": "來自變更回應的更新", + "to": "framework/react/guides/updates-from-mutation-responses" + }, + { + "label": "樂觀更新", + "to": "framework/react/guides/optimistic-updates" + }, + { + "label": "查詢取消", + "to": "framework/react/guides/query-cancellation" + }, + { + "label": "滾動恢復", + "to": "framework/react/guides/scroll-restoration" + }, + { + "label": "篩選器", + "to": "framework/react/guides/filters" + }, + { + "label": "效能與請求瀑布", + "to": "framework/react/guides/request-waterfalls" + }, + { + "label": "預獲取與路由整合", + "to": "framework/react/guides/prefetching" + }, + { + "label": "伺服器渲染與水合", + "to": "framework/react/guides/ssr" + }, + { + "label": "進階伺服器渲染", + "to": "framework/react/guides/advanced-ssr" + }, + { + "label": "快取", + "to": "framework/react/guides/caching" + }, + { + "label": "渲染優化", + "to": "framework/react/guides/render-optimizations" + }, + { + "label": "預設查詢函數", + "to": "framework/react/guides/default-query-function" + }, + { + "label": "Suspense", + "to": "framework/react/guides/suspense" + }, + { + "label": "測試", + "to": "framework/react/guides/testing" + }, + { + "label": "這會取代 [Redux, MobX 等] 嗎?", + "to": "framework/react/guides/does-this-replace-client-state" + }, + { + "label": "遷移至 v3", + "to": "framework/react/guides/migrating-to-react-query-3" + }, + { + "label": "遷移至 v4", + "to": "framework/react/guides/migrating-to-react-query-4" + }, + { + "label": "遷移至 v5", + "to": "framework/react/guides/migrating-to-v5" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "重要預設值", + "to": "framework/solid/guides/important-defaults" + }, + { + "label": "查詢", + "to": "framework/solid/guides/queries" + }, + { + "label": "查詢鍵", + "to": "framework/solid/guides/query-keys" + }, + { + "label": "查詢函數", + "to": "framework/solid/guides/query-functions" + }, + { + "label": "查詢選項", + "to": "framework/solid/guides/query-options" + }, + { + "label": "網路模式", + "to": "framework/solid/guides/network-mode" + }, + { + "label": "平行查詢", + "to": "framework/solid/guides/parallel-queries" + }, + { + "label": "依賴查詢", + "to": "framework/solid/guides/dependent-queries" + }, + { + "label": "背景獲取指示器", + "to": "framework/solid/guides/background-fetching-indicators" + }, + { + "label": "視窗焦點重新獲取", + "to": "framework/solid/guides/window-focus-refetching" + }, + { + "label": "停用/暫停查詢", + "to": "framework/solid/guides/disabling-queries" + }, + { + "label": "查詢重試", + "to": "framework/solid/guides/query-retries" + }, + { + "label": "分頁查詢", + "to": "framework/solid/guides/paginated-queries" + }, + { + "label": "無限查詢", + "to": "framework/solid/guides/infinite-queries" + }, + { + "label": "初始查詢資料", + "to": "framework/solid/guides/initial-query-data" + }, + { + "label": "佔位查詢資料", + "to": "framework/solid/guides/placeholder-query-data" + }, + { + "label": "變更", + "to": "framework/solid/guides/mutations" + }, + { + "label": "查詢失效", + "to": "framework/solid/guides/query-invalidation" + }, + { + "label": "來自變更的失效", + "to": "framework/solid/guides/invalidations-from-mutations" + }, + { + "label": "來自變更回應的更新", + "to": "framework/solid/guides/updates-from-mutation-responses" + }, + { + "label": "樂觀更新", + "to": "framework/solid/guides/optimistic-updates" + }, + { + "label": "查詢取消", + "to": "framework/solid/guides/query-cancellation" + }, + { + "label": "滾動恢復", + "to": "framework/solid/guides/scroll-restoration" + }, + { + "label": "篩選器", + "to": "framework/solid/guides/filters" + }, + { + "label": "請求瀑布", + "to": "framework/solid/guides/request-waterfalls" + }, + { + "label": "預獲取", + "to": "framework/solid/guides/prefetching" + }, + { + "label": "SSR", + "to": "framework/solid/guides/ssr" + }, + { + "label": "進階 SSR", + "to": "framework/solid/guides/advanced-ssr" + }, + { + "label": "快取", + "to": "framework/solid/guides/caching" + }, + { + "label": "預設查詢函數", + "to": "framework/solid/guides/default-query-function" + }, + { + "label": "Suspense", + "to": "framework/solid/guides/suspense" + }, + { + "label": "測試", + "to": "framework/solid/guides/testing" + }, + { + "label": "這會取代狀態管理器嗎?", + "to": "framework/angular/guides/does-this-replace-client-state" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "重要預設值", + "to": "framework/vue/guides/important-defaults" + }, + { + "label": "查詢", + "to": "framework/vue/guides/queries" + }, + { + "label": "查詢鍵", + "to": "framework/vue/guides/query-keys" + }, + { + "label": "查詢函數", + "to": "framework/vue/guides/query-functions" + }, + { + "label": "查詢選項", + "to": "framework/vue/guides/query-options" + }, + { + "label": "網路模式", + "to": "framework/vue/guides/network-mode" + }, + { + "label": "平行查詢", + "to": "framework/vue/guides/parallel-queries" + }, + { + "label": "依賴查詢", + "to": "framework/vue/guides/dependent-queries" + }, + { + "label": "背景獲取指示器", + "to": "framework/vue/guides/background-fetching-indicators" + }, + { + "label": "視窗焦點重新獲取", + "to": "framework/vue/guides/window-focus-refetching" + }, + { + "label": "停用/暫停查詢", + "to": "framework/vue/guides/disabling-queries" + }, + { + "label": "查詢重試", + "to": "framework/vue/guides/query-retries" + }, + { + "label": "分頁查詢", + "to": "framework/vue/guides/paginated-queries" + }, + { + "label": "無限查詢", + "to": "framework/vue/guides/infinite-queries" + }, + { + "label": "初始查詢資料", + "to": "framework/vue/guides/initial-query-data" + }, + { + "label": "佔位查詢資料", + "to": "framework/vue/guides/placeholder-query-data" + }, + { + "label": "變更", + "to": "framework/vue/guides/mutations" + }, + { + "label": "查詢失效", + "to": "framework/vue/guides/query-invalidation" + }, + { + "label": "來自變更的失效", + "to": "framework/vue/guides/invalidations-from-mutations" + }, + { + "label": "來自變更回應的更新", + "to": "framework/vue/guides/updates-from-mutation-responses" + }, + { + "label": "樂觀更新", + "to": "framework/vue/guides/optimistic-updates" + }, + { + "label": "查詢取消", + "to": "framework/vue/guides/query-cancellation" + }, + { + "label": "滾動恢復", + "to": "framework/vue/guides/scroll-restoration" + }, + { + "label": "篩選器", + "to": "framework/vue/guides/filters" + }, + { + "label": "預獲取", + "to": "framework/vue/guides/prefetching" + }, + { + "label": "SSR 與 Nuxt", + "to": "framework/vue/guides/ssr" + }, + { + "label": "快取", + "to": "framework/vue/guides/caching" + }, + { + "label": "預設查詢函數", + "to": "framework/vue/guides/default-query-function" + }, + { + "label": "Suspense", + "to": "framework/vue/guides/suspense" + }, + { + "label": "測試", + "to": "framework/vue/guides/testing" + }, + { + "label": "自訂客戶端", + "to": "framework/vue/guides/custom-client" + }, + { + "label": "這會取代 [Vuex, Pinia] 嗎?", + "to": "framework/vue/guides/does-this-replace-client-state" + }, + { + "label": "遷移至 v5", + "to": "framework/vue/guides/migrating-to-v5" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "重要預設值", + "to": "framework/angular/guides/important-defaults" + }, + { + "label": "查詢", + "to": "framework/angular/guides/queries" + }, + { + "label": "查詢鍵", + "to": "framework/angular/guides/query-keys" + }, + { + "label": "查詢函數", + "to": "framework/angular/guides/query-functions" + }, + { + "label": "查詢選項", + "to": "framework/angular/guides/query-options" + }, + { + "label": "網路模式", + "to": "framework/angular/guides/network-mode" + }, + { + "label": "平行查詢", + "to": "framework/angular/guides/parallel-queries" + }, + { + "label": "依賴查詢", + "to": "framework/angular/guides/dependent-queries" + }, + { + "label": "背景獲取指示器", + "to": "framework/angular/guides/background-fetching-indicators" + }, + { + "label": "視窗焦點重新獲取", + "to": "framework/angular/guides/window-focus-refetching" + }, + { + "label": "停用/暫停查詢", + "to": "framework/angular/guides/disabling-queries" + }, + { + "label": "查詢重試", + "to": "framework/angular/guides/query-retries" + }, + { + "label": "分頁查詢", + "to": "framework/angular/guides/paginated-queries" + }, + { + "label": "無限查詢", + "to": "framework/angular/guides/infinite-queries" + }, + { + "label": "初始查詢資料", + "to": "framework/angular/guides/initial-query-data" + }, + { + "label": "佔位查詢資料", + "to": "framework/angular/guides/placeholder-query-data" + }, + { + "label": "變更", + "to": "framework/angular/guides/mutations" + }, + { + "label": "變更選項", + "to": "framework/angular/guides/mutation-options" + }, + { + "label": "查詢失效", + "to": "framework/angular/guides/query-invalidation" + }, + { + "label": "來自變更的失效", + "to": "framework/angular/guides/invalidations-from-mutations" + }, + { + "label": "樂觀更新", + "to": "framework/angular/guides/optimistic-updates" + }, + { + "label": "查詢取消", + "to": "framework/angular/guides/query-cancellation" + }, + { + "label": "滾動恢復", + "to": "framework/angular/guides/scroll-restoration" + }, + { + "label": "篩選器", + "to": "framework/angular/guides/filters" + }, + { + "label": "快取", + "to": "framework/angular/guides/caching" + }, + { + "label": "預設查詢函數", + "to": "framework/angular/guides/default-query-function" + }, + { + "label": "這會取代狀態管理器嗎?", + "to": "framework/angular/guides/does-this-replace-client-state" + } + ] + } + ] + }, + { + "label": "API 參考", + "children": [ + { + "label": "QueryClient", + "to": "reference/QueryClient" + }, + { + "label": "QueryCache", + "to": "reference/QueryCache" + }, + { + "label": "MutationCache", + "to": "reference/MutationCache" + }, + { + "label": "QueryObserver", + "to": "reference/QueryObserver" + }, + { + "label": "InfiniteQueryObserver", + "to": "reference/InfiniteQueryObserver" + }, + { + "label": "QueriesObserver", + "to": "reference/QueriesObserver" + }, + { + "label": "streamedQuery", + "to": "reference/streamedQuery" + }, + { + "label": "focusManager", + "to": "reference/focusManager" + }, + { + "label": "onlineManager", + "to": "reference/onlineManager" + }, + { + "label": "notifyManager", + "to": "reference/notifyManager" + } + ], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "useQuery", + "to": "framework/react/reference/useQuery" + }, + { + "label": "useQueries", + "to": "framework/react/reference/useQueries" + }, + { + "label": "useInfiniteQuery", + "to": "framework/react/reference/useInfiniteQuery" + }, + { + "label": "useMutation", + "to": "framework/react/reference/useMutation" + }, + { + "label": "useIsFetching", + "to": "framework/react/reference/useIsFetching" + }, + { + "label": "useIsMutating", + "to": "framework/react/reference/useIsMutating" + }, + { + "label": "useMutationState", + "to": "framework/react/reference/useMutationState" + }, + { + "label": "useSuspenseQuery", + "to": "framework/react/reference/useSuspenseQuery" + }, + { + "label": "useSuspenseInfiniteQuery", + "to": "framework/react/reference/useSuspenseInfiniteQuery" + }, + { + "label": "useSuspenseQueries", + "to": "framework/react/reference/useSuspenseQueries" + }, + { + "label": "QueryClientProvider", + "to": "framework/react/reference/QueryClientProvider" + }, + { + "label": "useQueryClient", + "to": "framework/react/reference/useQueryClient" + }, + { + "label": "queryOptions", + "to": "framework/react/reference/queryOptions" + }, + { + "label": "infiniteQueryOptions", + "to": "framework/react/reference/infiniteQueryOptions" + }, + { + "label": "usePrefetchQuery", + "to": "framework/react/reference/usePrefetchQuery" + }, + { + "label": "usePrefetchInfiniteQuery", + "to": "framework/react/reference/usePrefetchInfiniteQuery" + }, + { + "label": "QueryErrorResetBoundary", + "to": "framework/react/reference/QueryErrorResetBoundary" + }, + { + "label": "useQueryErrorResetBoundary", + "to": "framework/react/reference/useQueryErrorResetBoundary" + }, + { + "label": "hydration", + "to": "framework/react/reference/hydration" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "useQuery", + "to": "framework/vue/reference/useQuery" + }, + { + "label": "useQueries", + "to": "framework/vue/reference/useQueries" + }, + { + "label": "useInfiniteQuery", + "to": "framework/vue/reference/useInfiniteQuery" + }, + { + "label": "useMutation", + "to": "framework/vue/reference/useMutation" + }, + { + "label": "useIsFetching", + "to": "framework/vue/reference/useIsFetching" + }, + { + "label": "useIsMutating", + "to": "framework/vue/reference/useIsMutating" + }, + { + "label": "useMutationState", + "to": "framework/vue/reference/useMutationState" + }, + { + "label": "useQueryClient", + "to": "framework/vue/reference/useQueryClient" + }, + { + "label": "queryOptions", + "to": "framework/vue/reference/queryOptions" + }, + { + "label": "infiniteQueryOptions", + "to": "framework/vue/reference/infiniteQueryOptions" + }, + { + "label": "hydration", + "to": "framework/vue/reference/hydration" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "useQuery", + "to": "framework/solid/reference/useQuery" + }, + { + "label": "useQueries", + "to": "framework/solid/reference/useQueries" + }, + { + "label": "useInfiniteQuery", + "to": "framework/solid/reference/useInfiniteQuery" + }, + { + "label": "useMutation", + "to": "framework/solid/reference/useMutation" + }, + { + "label": "useIsFetching", + "to": "framework/solid/reference/useIsFetching" + }, + { + "label": "useIsMutating", + "to": "framework/solid/reference/useIsMutating" + }, + { + "label": "useMutationState", + "to": "framework/solid/reference/useMutationState" + }, + { + "label": "queryOptions", + "to": "framework/solid/reference/queryOptions" + }, + { + "label": "infiniteQueryOptions", + "to": "framework/solid/reference/infiniteQueryOptions" + }, + { + "label": "hydration", + "to": "framework/solid/reference/hydration" + } + ] + }, + { + "label": "svelte", + "children": [ + { + "label": "Svelte 參考", + "to": "framework/svelte/reference/index" + }, + { + "label": "函數 / createQuery", + "to": "framework/svelte/reference/functions/createquery" + }, + { + "label": "函數 / createMutation", + "to": "framework/svelte/reference/functions/createmutation" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "Angular 參考", + "to": "framework/angular/reference/index" + }, + { + "label": "函數 / injectQuery", + "to": "framework/angular/reference/functions/injectquery" + }, + { + "label": "函數 / injectMutation", + "to": "framework/angular/reference/functions/injectmutation" + } + ] + } + ] + }, + { + "label": "ESLint", + "children": [ + { + "label": "ESLint 插件 Query", + "to": "eslint/eslint-plugin-query" + }, + { + "label": "徹底依賴檢查", + "to": "eslint/exhaustive-deps" + }, + { + "label": "穩定查詢客戶端", + "to": "eslint/stable-query-client" + }, + { + "label": "無其餘解構", + "to": "eslint/no-rest-destructuring" + }, + { + "label": "無不穩定依賴", + "to": "eslint/no-unstable-deps" + }, + { + "label": "無限查詢屬性順序", + "to": "eslint/infinite-query-property-order" + } + ] + }, + { + "label": "社群資源", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "TkDodo 的部落格", + "to": "framework/react/community/tkdodos-blog" + }, + { + "label": "社群專案", + "to": "framework/react/community/community-projects" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "TkDodo 的部落格", + "to": "framework/solid/community/tkdodos-blog" + }, + { + "label": "社群專案", + "to": "framework/solid/community/community-projects" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "TkDodo 的部落格", + "to": "framework/vue/community/tkdodos-blog" + }, + { + "label": "社群專案", + "to": "framework/vue/community/community-projects" + } + ] + } + ] + }, + { + "label": "範例", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "簡單", + "to": "framework/react/examples/simple" + }, + { + "label": "基礎", + "to": "framework/react/examples/basic" + }, + { + "label": "基礎 w/ GraphQL-Request", + "to": "framework/react/examples/basic-graphql-request" + }, + { + "label": "自動重新獲取 / 輪詢 / 即時", + "to": "framework/react/examples/auto-refetching" + }, + { + "label": "樂觀更新 (UI)", + "to": "framework/react/examples/optimistic-updates-ui" + }, + { + "label": "樂觀更新 (快取)", + "to": "framework/react/examples/optimistic-updates-cache" + }, + { + "label": "分頁", + "to": "framework/react/examples/pagination" + }, + { + "label": "載入更多與無限滾動", + "to": "framework/react/examples/load-more-infinite-scroll" + }, + { + "label": "帶有最大頁數的無限查詢", + "to": "framework/react/examples/infinite-query-with-max-pages" + }, + { + "label": "Suspense", + "to": "framework/react/examples/suspense" + }, + { + "label": "預設查詢函數", + "to": "framework/react/examples/default-query-function" + }, + { + "label": "遊樂場", + "to": "framework/react/examples/playground" + }, + { + "label": "預獲取", + "to": "framework/react/examples/prefetching" + }, + { + "label": "星際大戰", + "to": "framework/react/examples/star-wars" + }, + { + "label": "瑞克與莫蒂", + "to": "framework/react/examples/rick-morty" + }, + { + "label": "Next.js 頁面", + "to": "framework/react/examples/nextjs" + }, + { + "label": "帶有預獲取的 Next.js 應用", + "to": "framework/react/examples/nextjs-app-prefetching" + }, + { + "label": "帶有串流的 Next.js 應用", + "to": "framework/react/examples/nextjs-suspense-streaming" + }, + { + "label": "React Native", + "to": "framework/react/examples/react-native" + }, + { + "label": "React Router", + "to": "framework/react/examples/react-router" + }, + { + "label": "離線查詢與變更", + "to": "framework/react/examples/offline" + }, + { + "label": "Algolia", + "to": "framework/react/examples/algolia" + }, + { + "label": "Shadow DOM", + "to": "framework/react/examples/shadow-dom" + }, + { + "label": "開發工具嵌入式面板", + "to": "framework/react/examples/devtools-panel" + }, + { + "label": "聊天範例 (串流)", + "to": "framework/react/examples/chat" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "簡單", + "to": "framework/solid/examples/simple" + }, + { + "label": "基礎", + "to": "framework/solid/examples/basic" + }, + { + "label": "基礎 w/ GraphQL-Request", + "to": "framework/solid/examples/basic-graphql-request" + }, + { + "label": "預設查詢函數", + "to": "framework/solid/examples/default-query-function" + }, + { + "label": "Solid Start", + "to": "framework/solid/examples/solid-start-streaming" + }, + { + "label": "Astro", + "to": "framework/solid/examples/astro" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "基礎", + "to": "framework/vue/examples/basic" + }, + { + "label": "Vue 2.6", + "to": "framework/vue/examples/2.6-basic" + }, + { + "label": "Nuxt 3", + "to": "framework/vue/examples/nuxt3" + }, + { + "label": "持久化", + "to": "framework/vue/examples/persister" + } + ] + }, + { + "label": "svelte", + "children": [ + { + "label": "簡單", + "to": "framework/svelte/examples/simple" + }, + { + "label": "基礎", + "to": "framework/svelte/examples/basic" + }, + { + "label": "自動重新獲取 / 輪詢 / 即時", + "to": "framework/svelte/examples/auto-refetching" + }, + { + "label": "SSR", + "to": "framework/svelte/examples/ssr" + }, + { + "label": "樂觀更新", + "to": "framework/svelte/examples/optimistic-updates" + }, + { + "label": "遊樂場", + "to": "framework/svelte/examples/playground" + }, + { + "label": "星際大戰", + "to": "framework/svelte/examples/star-wars" + }, + { + "label": "無限查詢", + "to": "framework/svelte/examples/load-more-infinite-scroll" + } + ] + }, + { + "label": "angular", + "children": [ + { + "label": "簡單", + "to": "framework/angular/examples/simple" + }, + { + "label": "基礎", + "to": "framework/angular/examples/basic" + }, + { + "label": "自動重新獲取 / 輪詢 / 即時", + "to": "framework/angular/examples/auto-refetching" + }, + { + "label": "樂觀更新", + "to": "framework/angular/examples/optimistic-updates" + }, + { + "label": "分頁", + "to": "framework/angular/examples/pagination" + }, + { + "label": "帶有最大頁數的無限查詢", + "to": "framework/angular/examples/infinite-query-with-max-pages" + }, + { + "label": "Angular 路由", + "to": "framework/angular/examples/router" + }, + { + "label": "RxJS 自動完成", + "to": "framework/angular/examples/rxjs" + }, + { + "label": "來自服務的查詢選項", + "to": "framework/angular/examples/query-options-from-a-service" + }, + { + "label": "開發工具嵌入式面板", + "to": "framework/angular/examples/devtools-panel" + } + ] + } + ] + }, + { + "label": "插件", + "children": [], + "frameworks": [ + { + "label": "react", + "children": [ + { + "label": "persistQueryClient", + "to": "framework/react/plugins/persistQueryClient" + }, + { + "label": "createSyncStoragePersister", + "to": "framework/react/plugins/createSyncStoragePersister" + }, + { + "label": "createAsyncStoragePersister", + "to": "framework/react/plugins/createAsyncStoragePersister" + }, + { + "label": "broadcastQueryClient (實驗性)", + "to": "framework/react/plugins/broadcastQueryClient" + }, + { + "label": "createPersister (實驗性)", + "to": "framework/react/plugins/createPersister" + } + ] + }, + { + "label": "solid", + "children": [ + { + "label": "broadcastQueryClient (實驗性)", + "to": "framework/solid/plugins/broadcastQueryClient" + }, + { + "label": "createPersister (實驗性)", + "to": "framework/solid/plugins/createPersister" + } + ] + }, + { + "label": "vue", + "children": [ + { + "label": "broadcastQueryClient (實驗性)", + "to": "framework/vue/plugins/broadcastQueryClient" + }, + { + "label": "createPersister (實驗性)", + "to": "framework/vue/plugins/createPersister" + } + ] + } + ] + } + ], + "users": [ + "Google", + "Walmart", + "Facebook", + "PayPal", + "Amazon", + "American Express", + "Microsoft", + "Target", + "Ebay", + "Autodesk", + "CarFAX", + "Docusign", + "HP", + "MLB", + "Volvo", + "Ocado", + "UPC.ch", + "EFI.com", + "ReactBricks", + "Nozzle.io", + "Uber" + ] +} From 7520feaa18efcf91126e3155c6432e6596b3344a Mon Sep 17 00:00:00 2001 From: xiaoyu2er Date: Thu, 8 May 2025 20:27:58 +0000 Subject: [PATCH 7/7] docs: update documentation translations --- docs/zh-hant/eslint/eslint-plugin-query.md | 103 ++++ docs/zh-hant/eslint/exhaustive-deps.md | 48 ++ .../eslint/infinite-query-property-order.md | 65 +++ docs/zh-hant/eslint/no-rest-destructuring.md | 49 ++ docs/zh-hant/eslint/no-unstable-deps.md | 58 +++ docs/zh-hant/eslint/no-void-query-fn.md | 41 ++ docs/zh-hant/eslint/stable-query-client.md | 63 +++ ...pclient-and-other-data-fetching-clients.md | 50 ++ docs/zh-hant/framework/angular/devtools.md | 117 +++++ .../guides/background-fetching-indicators.md | 54 ++ .../framework/angular/guides/caching.md | 35 ++ .../angular/guides/default-query-function.md | 50 ++ .../angular/guides/dependent-queries.md | 66 +++ .../angular/guides/disabling-queries.md | 120 +++++ .../guides/does-this-replace-client-state.md | 11 + .../framework/angular/guides/filters.md | 7 + .../angular/guides/important-defaults.md | 16 + .../angular/guides/infinite-queries.md | 246 +++++++++ .../angular/guides/initial-query-data.md | 140 +++++ .../guides/invalidations-from-mutations.md | 40 ++ .../angular/guides/mutation-options.md | 25 + .../framework/angular/guides/mutations.md | 301 +++++++++++ .../framework/angular/guides/network-mode.md | 7 + .../angular/guides/optimistic-updates.md | 175 +++++++ .../angular/guides/paginated-queries.md | 107 ++++ .../angular/guides/parallel-queries.md | 46 ++ .../angular/guides/placeholder-query-data.md | 75 +++ .../framework/angular/guides/queries.md | 126 +++++ .../angular/guides/query-cancellation.md | 108 ++++ .../angular/guides/query-functions.md | 101 ++++ .../angular/guides/query-invalidation.md | 118 +++++ .../framework/angular/guides/query-keys.md | 77 +++ .../framework/angular/guides/query-options.md | 58 +++ .../framework/angular/guides/query-retries.md | 65 +++ .../angular/guides/scroll-restoration.md | 7 + .../angular/guides/window-focus-refetching.md | 42 ++ .../zh-hant/framework/angular/installation.md | 36 ++ docs/zh-hant/framework/angular/overview.md | 115 +++++ docs/zh-hant/framework/angular/quick-start.md | 120 +++++ .../functions/infinitequeryoptions.md | 134 +++++ .../functions/injectinfinitequery.md | 190 +++++++ .../reference/functions/injectisfetching.md | 37 ++ .../reference/functions/injectismutating.md | 36 ++ .../reference/functions/injectmutation.md | 49 ++ .../functions/injectmutationstate.md | 41 ++ .../reference/functions/injectqueries.md | 39 ++ .../reference/functions/injectquery.md | 323 ++++++++++++ .../reference/functions/injectqueryclient.md | 90 ++++ .../functions/provideangularquery.md | 66 +++ .../reference/functions/providequeryclient.md | 29 ++ .../reference/functions/queryoptions.md | 146 ++++++ .../framework/angular/reference/index.md | 52 ++ .../interfaces/basemutationnarrowing.md | 98 ++++ .../interfaces/basequerynarrowing.md | 74 +++ .../interfaces/createbasequeryoptions.md | 24 + .../interfaces/createinfinitequeryoptions.md | 26 + .../interfaces/createmutationoptions.md | 22 + .../interfaces/createqueryoptions.md | 22 + .../interfaces/injectmutationstateoptions.md | 20 + .../type-aliases/createbasemutationresult.md | 34 ++ .../type-aliases/createbasequeryresult.md | 24 + .../type-aliases/createinfinitequeryresult.md | 22 + .../type-aliases/createmutateasyncfunction.md | 26 + .../type-aliases/createmutatefunction.md | 34 ++ .../type-aliases/createmutationresult.md | 28 + .../type-aliases/createqueryresult.md | 22 + .../definedcreateinfinitequeryresult.md | 24 + .../type-aliases/definedcreatequeryresult.md | 24 + .../definedinitialdatainfiniteoptions.md | 36 ++ .../type-aliases/definedinitialdataoptions.md | 34 ++ .../type-aliases/nonundefinedguard.md | 20 + .../reference/type-aliases/queriesoptions.md | 26 + .../reference/type-aliases/queriesresults.md | 26 + .../undefinedinitialdatainfiniteoptions.md | 36 ++ .../undefinedinitialdataoptions.md | 34 ++ docs/zh-hant/framework/angular/typescript.md | 293 +++++++++++ docs/zh-hant/framework/angular/zoneless.md | 13 + .../react/community/community-projects.md | 161 ++++++ .../framework/react/community/tkdodos-blog.md | 88 ++++ docs/zh-hant/framework/react/comparison.md | 113 +++++ docs/zh-hant/framework/react/devtools.md | 197 ++++++++ docs/zh-hant/framework/react/graphql.md | 57 +++ .../framework/react/guides/advanced-ssr.md | 396 +++++++++++++++ .../guides/background-fetching-indicators.md | 62 +++ .../zh-hant/framework/react/guides/caching.md | 36 ++ .../react/guides/default-query-function.md | 58 +++ .../react/guides/dependent-queries.md | 97 ++++ .../react/guides/disabling-queries.md | 130 +++++ .../guides/does-this-replace-client-state.md | 58 +++ .../zh-hant/framework/react/guides/filters.md | 92 ++++ .../react/guides/important-defaults.md | 44 ++ .../react/guides/infinite-queries.md | 263 ++++++++++ .../react/guides/initial-query-data.md | 177 +++++++ .../guides/invalidations-from-mutations.md | 41 ++ .../guides/migrating-to-react-query-3.md | 478 ++++++++++++++++++ .../guides/migrating-to-react-query-4.md | 328 ++++++++++++ .../framework/react/guides/migrating-to-v5.md | 327 ++++++++++++ .../framework/react/guides/mutations.md | 415 +++++++++++++++ .../framework/react/guides/network-mode.md | 48 ++ .../react/guides/optimistic-updates.md | 189 +++++++ .../react/guides/paginated-queries.md | 95 ++++ .../react/guides/parallel-queries.md | 58 +++ .../react/guides/placeholder-query-data.md | 103 ++++ .../framework/react/guides/prefetching.md | 440 ++++++++++++++++ .../zh-hant/framework/react/guides/queries.md | 147 ++++++ .../react/guides/query-cancellation.md | 195 +++++++ .../framework/react/guides/query-functions.md | 119 +++++ .../react/guides/query-invalidation.md | 137 +++++ .../framework/react/guides/query-keys.md | 104 ++++ .../framework/react/guides/query-options.md | 51 ++ .../framework/react/guides/query-retries.md | 82 +++ .../react/guides/render-optimizations.md | 77 +++ .../react/guides/request-waterfalls.md | 341 +++++++++++++ .../react/guides/scroll-restoration.md | 12 + docs/zh-hant/framework/react/guides/ssr.md | 470 +++++++++++++++++ .../framework/react/guides/suspense.md | 225 +++++++++ .../zh-hant/framework/react/guides/testing.md | 173 +++++++ .../guides/updates-from-mutation-responses.md | 84 +++ .../react/guides/window-focus-refetching.md | 107 ++++ docs/zh-hant/framework/react/installation.md | 93 ++++ docs/zh-hant/framework/react/overview.md | 103 ++++ .../react/plugins/broadcastQueryClient.md | 63 +++ .../plugins/createAsyncStoragePersister.md | 120 +++++ .../react/plugins/createPersister.md | 137 +++++ .../plugins/createSyncStoragePersister.md | 156 ++++++ .../react/plugins/persistQueryClient.md | 293 +++++++++++ docs/zh-hant/framework/react/quick-start.md | 79 +++ docs/zh-hant/framework/react/react-native.md | 121 +++++ .../react/reference/QueryClientProvider.md | 24 + .../reference/QueryErrorResetBoundary.md | 31 ++ .../framework/react/reference/hydration.md | 130 +++++ .../react/reference/infiniteQueryOptions.md | 21 + .../framework/react/reference/queryOptions.md | 26 + .../react/reference/useInfiniteQuery.md | 94 ++++ .../react/reference/useIsFetching.md | 27 + .../react/reference/useIsMutating.md | 27 + .../framework/react/reference/useMutation.md | 162 ++++++ .../react/reference/useMutationState.md | 83 +++ .../reference/usePrefetchInfiniteQuery.md | 39 ++ .../react/reference/usePrefetchQuery.md | 26 + .../framework/react/reference/useQueries.md | 69 +++ .../framework/react/reference/useQuery.md | 256 ++++++++++ .../react/reference/useQueryClient.md | 19 + .../reference/useQueryErrorResetBoundary.md | 30 ++ .../reference/useSuspenseInfiniteQuery.md | 32 ++ .../react/reference/useSuspenseQueries.md | 34 ++ .../react/reference/useSuspenseQuery.md | 31 ++ docs/zh-hant/framework/react/typescript.md | 252 +++++++++ docs/zh-hant/framework/react/videos.md | 73 +++ .../solid/community/community-projects.md | 9 + .../framework/solid/community/tkdodos-blog.md | 7 + docs/zh-hant/framework/solid/devtools.md | 83 +++ .../framework/solid/guides/advanced-ssr.md | 8 + .../guides/background-fetching-indicators.md | 9 + .../zh-hant/framework/solid/guides/caching.md | 7 + .../solid/guides/default-query-function.md | 14 + .../solid/guides/dependent-queries.md | 14 + .../solid/guides/disabling-queries.md | 14 + .../guides/does-this-replace-client-state.md | 9 + .../zh-hant/framework/solid/guides/filters.md | 7 + .../solid/guides/important-defaults.md | 7 + .../solid/guides/infinite-queries.md | 14 + .../solid/guides/initial-query-data.md | 14 + .../guides/invalidations-from-mutations.md | 15 + .../framework/solid/guides/mutations.md | 15 + .../framework/solid/guides/network-mode.md | 7 + .../solid/guides/optimistic-updates.md | 15 + .../solid/guides/paginated-queries.md | 14 + .../solid/guides/parallel-queries.md | 14 + .../solid/guides/placeholder-query-data.md | 16 + .../framework/solid/guides/prefetching.md | 16 + .../zh-hant/framework/solid/guides/queries.md | 13 + .../solid/guides/query-cancellation.md | 34 ++ .../framework/solid/guides/query-functions.md | 14 + .../solid/guides/query-invalidation.md | 14 + .../framework/solid/guides/query-keys.md | 14 + .../framework/solid/guides/query-options.md | 14 + .../framework/solid/guides/query-retries.md | 14 + .../solid/guides/request-waterfalls.md | 15 + .../solid/guides/scroll-restoration.md | 7 + docs/zh-hant/framework/solid/guides/ssr.md | 8 + .../framework/solid/guides/suspense.md | 44 ++ .../zh-hant/framework/solid/guides/testing.md | 6 + .../guides/updates-from-mutation-responses.md | 15 + .../solid/guides/window-focus-refetching.md | 14 + docs/zh-hant/framework/solid/installation.md | 61 +++ docs/zh-hant/framework/solid/overview.md | 140 +++++ .../solid/plugins/broadcastQueryClient.md | 9 + .../solid/plugins/createPersister.md | 9 + docs/zh-hant/framework/solid/quick-start.md | 231 +++++++++ .../framework/solid/reference/hydration.md | 12 + .../solid/reference/infiniteQueryOptions.md | 7 + .../framework/solid/reference/queryOptions.md | 7 + .../solid/reference/useInfiniteQuery.md | 11 + .../solid/reference/useIsFetching.md | 9 + .../solid/reference/useIsMutating.md | 9 + .../framework/solid/reference/useMutation.md | 11 + .../solid/reference/useMutationState.md | 11 + .../framework/solid/reference/useQueries.md | 10 + .../framework/solid/reference/useQuery.md | 376 ++++++++++++++ docs/zh-hant/framework/solid/typescript.md | 219 ++++++++ docs/zh-hant/framework/svelte/devtools.md | 77 +++ docs/zh-hant/framework/svelte/installation.md | 36 ++ docs/zh-hant/framework/svelte/overview.md | 76 +++ docs/zh-hant/framework/svelte/reactivity.md | 51 ++ .../reference/classes/hydrationboundary.md | 276 ++++++++++ .../functions/createinfinitequery.md | 44 ++ .../reference/functions/createmutation.md | 39 ++ .../reference/functions/createqueries.md | 39 ++ .../svelte/reference/functions/createquery.md | 107 ++++ .../functions/getisrestoringcontext.md | 22 + .../functions/getqueryclientcontext.md | 22 + .../functions/infinitequeryoptions.md | 51 ++ .../reference/functions/queryoptions.md | 68 +++ .../functions/setisrestoringcontext.md | 26 + .../functions/setqueryclientcontext.md | 26 + .../svelte/reference/functions/usehydrate.md | 28 + .../reference/functions/useisfetching.md | 26 + .../reference/functions/useismutating.md | 26 + .../reference/functions/useisrestoring.md | 20 + .../reference/functions/usemutationstate.md | 30 ++ .../reference/functions/usequeryclient.md | 24 + .../framework/svelte/reference/index.md | 59 +++ .../type-aliases/createbasemutationresult.md | 34 ++ .../type-aliases/createbasequeryoptions.md | 30 ++ .../type-aliases/createbasequeryresult.md | 24 + .../createinfinitequeryoptions.md | 32 ++ .../type-aliases/createinfinitequeryresult.md | 24 + .../type-aliases/createmutateasyncfunction.md | 26 + .../type-aliases/createmutatefunction.md | 34 ++ .../type-aliases/createmutationoptions.md | 28 + .../type-aliases/createmutationresult.md | 28 + .../type-aliases/createqueryoptions.md | 28 + .../type-aliases/createqueryresult.md | 24 + .../definedcreatebasequeryresult.md | 24 + .../type-aliases/definedcreatequeryresult.md | 24 + .../type-aliases/definedinitialdataoptions.md | 34 ++ .../type-aliases/mutationstateoptions.md | 44 ++ .../reference/type-aliases/queriesoptions.md | 26 + .../reference/type-aliases/queriesresults.md | 26 + .../reference/type-aliases/storeorval.md | 22 + .../undefinedinitialdataoptions.md | 34 ++ docs/zh-hant/framework/svelte/ssr.md | 156 ++++++ .../vue/community/community-projects.md | 31 ++ .../framework/vue/community/tkdodos-blog.md | 7 + docs/zh-hant/framework/vue/devtools.md | 91 ++++ docs/zh-hant/framework/vue/graphql.md | 10 + .../guides/background-fetching-indicators.md | 47 ++ docs/zh-hant/framework/vue/guides/caching.md | 7 + .../framework/vue/guides/custom-client.md | 74 +++ .../vue/guides/default-query-function.md | 33 ++ .../framework/vue/guides/dependent-queries.md | 89 ++++ .../framework/vue/guides/disabling-queries.md | 105 ++++ .../guides/does-this-replace-client-state.md | 58 +++ docs/zh-hant/framework/vue/guides/filters.md | 7 + .../vue/guides/important-defaults.md | 10 + .../framework/vue/guides/infinite-queries.md | 58 +++ .../vue/guides/initial-query-data.md | 10 + .../guides/invalidations-from-mutations.md | 35 ++ .../framework/vue/guides/migrating-to-v5.md | 325 ++++++++++++ .../zh-hant/framework/vue/guides/mutations.md | 306 +++++++++++ .../framework/vue/guides/network-mode.md | 7 + .../vue/guides/optimistic-updates.md | 9 + .../framework/vue/guides/paginated-queries.md | 77 +++ .../framework/vue/guides/parallel-queries.md | 42 ++ .../vue/guides/placeholder-query-data.md | 61 +++ .../framework/vue/guides/prefetching.md | 49 ++ docs/zh-hant/framework/vue/guides/queries.md | 109 ++++ .../vue/guides/query-cancellation.md | 27 + .../framework/vue/guides/query-functions.md | 24 + .../vue/guides/query-invalidation.md | 9 + .../framework/vue/guides/query-keys.md | 21 + .../framework/vue/guides/query-options.md | 9 + .../framework/vue/guides/query-retries.md | 61 +++ .../vue/guides/scroll-restoration.md | 7 + docs/zh-hant/framework/vue/guides/ssr.md | 260 ++++++++++ docs/zh-hant/framework/vue/guides/suspense.md | 56 ++ docs/zh-hant/framework/vue/guides/testing.md | 6 + .../guides/updates-from-mutation-responses.md | 7 + .../vue/guides/window-focus-refetching.md | 28 + docs/zh-hant/framework/vue/installation.md | 71 +++ docs/zh-hant/framework/vue/overview.md | 47 ++ .../vue/plugins/broadcastQueryClient.md | 9 + .../framework/vue/plugins/createPersister.md | 133 +++++ docs/zh-hant/framework/vue/quick-start.md | 57 +++ docs/zh-hant/framework/vue/reactivity.md | 171 +++++++ .../framework/vue/reference/hydration.md | 12 + .../vue/reference/infiniteQueryOptions.md | 7 + .../framework/vue/reference/queryOptions.md | 7 + .../vue/reference/useInfiniteQuery.md | 9 + .../framework/vue/reference/useIsFetching.md | 9 + .../framework/vue/reference/useIsMutating.md | 9 + .../framework/vue/reference/useMutation.md | 9 + .../vue/reference/useMutationState.md | 9 + .../framework/vue/reference/useQueries.md | 9 + .../framework/vue/reference/useQuery.md | 9 + .../framework/vue/reference/useQueryClient.md | 9 + docs/zh-hant/framework/vue/typescript.md | 119 +++++ .../reference/InfiniteQueryObserver.md | 28 + docs/zh-hant/reference/MutationCache.md | 100 ++++ docs/zh-hant/reference/QueriesObserver.md | 26 + docs/zh-hant/reference/QueryCache.md | 124 +++++ docs/zh-hant/reference/QueryClient.md | 413 +++++++++++++++ docs/zh-hant/reference/QueryObserver.md | 21 + docs/zh-hant/reference/focusManager.md | 78 +++ docs/zh-hant/reference/notifyManager.md | 90 ++++ docs/zh-hant/reference/onlineManager.md | 76 +++ docs/zh-hant/reference/streamedQuery.md | 37 ++ 308 files changed, 22319 insertions(+) create mode 100644 docs/zh-hant/eslint/eslint-plugin-query.md create mode 100644 docs/zh-hant/eslint/exhaustive-deps.md create mode 100644 docs/zh-hant/eslint/infinite-query-property-order.md create mode 100644 docs/zh-hant/eslint/no-rest-destructuring.md create mode 100644 docs/zh-hant/eslint/no-unstable-deps.md create mode 100644 docs/zh-hant/eslint/no-void-query-fn.md create mode 100644 docs/zh-hant/eslint/stable-query-client.md create mode 100644 docs/zh-hant/framework/angular/angular-httpclient-and-other-data-fetching-clients.md create mode 100644 docs/zh-hant/framework/angular/devtools.md create mode 100644 docs/zh-hant/framework/angular/guides/background-fetching-indicators.md create mode 100644 docs/zh-hant/framework/angular/guides/caching.md create mode 100644 docs/zh-hant/framework/angular/guides/default-query-function.md create mode 100644 docs/zh-hant/framework/angular/guides/dependent-queries.md create mode 100644 docs/zh-hant/framework/angular/guides/disabling-queries.md create mode 100644 docs/zh-hant/framework/angular/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hant/framework/angular/guides/filters.md create mode 100644 docs/zh-hant/framework/angular/guides/important-defaults.md create mode 100644 docs/zh-hant/framework/angular/guides/infinite-queries.md create mode 100644 docs/zh-hant/framework/angular/guides/initial-query-data.md create mode 100644 docs/zh-hant/framework/angular/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hant/framework/angular/guides/mutation-options.md create mode 100644 docs/zh-hant/framework/angular/guides/mutations.md create mode 100644 docs/zh-hant/framework/angular/guides/network-mode.md create mode 100644 docs/zh-hant/framework/angular/guides/optimistic-updates.md create mode 100644 docs/zh-hant/framework/angular/guides/paginated-queries.md create mode 100644 docs/zh-hant/framework/angular/guides/parallel-queries.md create mode 100644 docs/zh-hant/framework/angular/guides/placeholder-query-data.md create mode 100644 docs/zh-hant/framework/angular/guides/queries.md create mode 100644 docs/zh-hant/framework/angular/guides/query-cancellation.md create mode 100644 docs/zh-hant/framework/angular/guides/query-functions.md create mode 100644 docs/zh-hant/framework/angular/guides/query-invalidation.md create mode 100644 docs/zh-hant/framework/angular/guides/query-keys.md create mode 100644 docs/zh-hant/framework/angular/guides/query-options.md create mode 100644 docs/zh-hant/framework/angular/guides/query-retries.md create mode 100644 docs/zh-hant/framework/angular/guides/scroll-restoration.md create mode 100644 docs/zh-hant/framework/angular/guides/window-focus-refetching.md create mode 100644 docs/zh-hant/framework/angular/installation.md create mode 100644 docs/zh-hant/framework/angular/overview.md create mode 100644 docs/zh-hant/framework/angular/quick-start.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/infinitequeryoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectinfinitequery.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectisfetching.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectismutating.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectmutation.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectmutationstate.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectqueries.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectquery.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/injectqueryclient.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/provideangularquery.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/providequeryclient.md create mode 100644 docs/zh-hant/framework/angular/reference/functions/queryoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/index.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/basemutationnarrowing.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/basequerynarrowing.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/createbasequeryoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/createinfinitequeryoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/createmutationoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/createqueryoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/interfaces/injectmutationstateoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createbasemutationresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createbasequeryresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createinfinitequeryresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createmutateasyncfunction.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createmutatefunction.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createmutationresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/createqueryresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/definedcreatequeryresult.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdataoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/nonundefinedguard.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/queriesoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/queriesresults.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md create mode 100644 docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md create mode 100644 docs/zh-hant/framework/angular/typescript.md create mode 100644 docs/zh-hant/framework/angular/zoneless.md create mode 100644 docs/zh-hant/framework/react/community/community-projects.md create mode 100644 docs/zh-hant/framework/react/community/tkdodos-blog.md create mode 100644 docs/zh-hant/framework/react/comparison.md create mode 100644 docs/zh-hant/framework/react/devtools.md create mode 100644 docs/zh-hant/framework/react/graphql.md create mode 100644 docs/zh-hant/framework/react/guides/advanced-ssr.md create mode 100644 docs/zh-hant/framework/react/guides/background-fetching-indicators.md create mode 100644 docs/zh-hant/framework/react/guides/caching.md create mode 100644 docs/zh-hant/framework/react/guides/default-query-function.md create mode 100644 docs/zh-hant/framework/react/guides/dependent-queries.md create mode 100644 docs/zh-hant/framework/react/guides/disabling-queries.md create mode 100644 docs/zh-hant/framework/react/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hant/framework/react/guides/filters.md create mode 100644 docs/zh-hant/framework/react/guides/important-defaults.md create mode 100644 docs/zh-hant/framework/react/guides/infinite-queries.md create mode 100644 docs/zh-hant/framework/react/guides/initial-query-data.md create mode 100644 docs/zh-hant/framework/react/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hant/framework/react/guides/migrating-to-react-query-3.md create mode 100644 docs/zh-hant/framework/react/guides/migrating-to-react-query-4.md create mode 100644 docs/zh-hant/framework/react/guides/migrating-to-v5.md create mode 100644 docs/zh-hant/framework/react/guides/mutations.md create mode 100644 docs/zh-hant/framework/react/guides/network-mode.md create mode 100644 docs/zh-hant/framework/react/guides/optimistic-updates.md create mode 100644 docs/zh-hant/framework/react/guides/paginated-queries.md create mode 100644 docs/zh-hant/framework/react/guides/parallel-queries.md create mode 100644 docs/zh-hant/framework/react/guides/placeholder-query-data.md create mode 100644 docs/zh-hant/framework/react/guides/prefetching.md create mode 100644 docs/zh-hant/framework/react/guides/queries.md create mode 100644 docs/zh-hant/framework/react/guides/query-cancellation.md create mode 100644 docs/zh-hant/framework/react/guides/query-functions.md create mode 100644 docs/zh-hant/framework/react/guides/query-invalidation.md create mode 100644 docs/zh-hant/framework/react/guides/query-keys.md create mode 100644 docs/zh-hant/framework/react/guides/query-options.md create mode 100644 docs/zh-hant/framework/react/guides/query-retries.md create mode 100644 docs/zh-hant/framework/react/guides/render-optimizations.md create mode 100644 docs/zh-hant/framework/react/guides/request-waterfalls.md create mode 100644 docs/zh-hant/framework/react/guides/scroll-restoration.md create mode 100644 docs/zh-hant/framework/react/guides/ssr.md create mode 100644 docs/zh-hant/framework/react/guides/suspense.md create mode 100644 docs/zh-hant/framework/react/guides/testing.md create mode 100644 docs/zh-hant/framework/react/guides/updates-from-mutation-responses.md create mode 100644 docs/zh-hant/framework/react/guides/window-focus-refetching.md create mode 100644 docs/zh-hant/framework/react/installation.md create mode 100644 docs/zh-hant/framework/react/overview.md create mode 100644 docs/zh-hant/framework/react/plugins/broadcastQueryClient.md create mode 100644 docs/zh-hant/framework/react/plugins/createAsyncStoragePersister.md create mode 100644 docs/zh-hant/framework/react/plugins/createPersister.md create mode 100644 docs/zh-hant/framework/react/plugins/createSyncStoragePersister.md create mode 100644 docs/zh-hant/framework/react/plugins/persistQueryClient.md create mode 100644 docs/zh-hant/framework/react/quick-start.md create mode 100644 docs/zh-hant/framework/react/react-native.md create mode 100644 docs/zh-hant/framework/react/reference/QueryClientProvider.md create mode 100644 docs/zh-hant/framework/react/reference/QueryErrorResetBoundary.md create mode 100644 docs/zh-hant/framework/react/reference/hydration.md create mode 100644 docs/zh-hant/framework/react/reference/infiniteQueryOptions.md create mode 100644 docs/zh-hant/framework/react/reference/queryOptions.md create mode 100644 docs/zh-hant/framework/react/reference/useInfiniteQuery.md create mode 100644 docs/zh-hant/framework/react/reference/useIsFetching.md create mode 100644 docs/zh-hant/framework/react/reference/useIsMutating.md create mode 100644 docs/zh-hant/framework/react/reference/useMutation.md create mode 100644 docs/zh-hant/framework/react/reference/useMutationState.md create mode 100644 docs/zh-hant/framework/react/reference/usePrefetchInfiniteQuery.md create mode 100644 docs/zh-hant/framework/react/reference/usePrefetchQuery.md create mode 100644 docs/zh-hant/framework/react/reference/useQueries.md create mode 100644 docs/zh-hant/framework/react/reference/useQuery.md create mode 100644 docs/zh-hant/framework/react/reference/useQueryClient.md create mode 100644 docs/zh-hant/framework/react/reference/useQueryErrorResetBoundary.md create mode 100644 docs/zh-hant/framework/react/reference/useSuspenseInfiniteQuery.md create mode 100644 docs/zh-hant/framework/react/reference/useSuspenseQueries.md create mode 100644 docs/zh-hant/framework/react/reference/useSuspenseQuery.md create mode 100644 docs/zh-hant/framework/react/typescript.md create mode 100644 docs/zh-hant/framework/react/videos.md create mode 100644 docs/zh-hant/framework/solid/community/community-projects.md create mode 100644 docs/zh-hant/framework/solid/community/tkdodos-blog.md create mode 100644 docs/zh-hant/framework/solid/devtools.md create mode 100644 docs/zh-hant/framework/solid/guides/advanced-ssr.md create mode 100644 docs/zh-hant/framework/solid/guides/background-fetching-indicators.md create mode 100644 docs/zh-hant/framework/solid/guides/caching.md create mode 100644 docs/zh-hant/framework/solid/guides/default-query-function.md create mode 100644 docs/zh-hant/framework/solid/guides/dependent-queries.md create mode 100644 docs/zh-hant/framework/solid/guides/disabling-queries.md create mode 100644 docs/zh-hant/framework/solid/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hant/framework/solid/guides/filters.md create mode 100644 docs/zh-hant/framework/solid/guides/important-defaults.md create mode 100644 docs/zh-hant/framework/solid/guides/infinite-queries.md create mode 100644 docs/zh-hant/framework/solid/guides/initial-query-data.md create mode 100644 docs/zh-hant/framework/solid/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hant/framework/solid/guides/mutations.md create mode 100644 docs/zh-hant/framework/solid/guides/network-mode.md create mode 100644 docs/zh-hant/framework/solid/guides/optimistic-updates.md create mode 100644 docs/zh-hant/framework/solid/guides/paginated-queries.md create mode 100644 docs/zh-hant/framework/solid/guides/parallel-queries.md create mode 100644 docs/zh-hant/framework/solid/guides/placeholder-query-data.md create mode 100644 docs/zh-hant/framework/solid/guides/prefetching.md create mode 100644 docs/zh-hant/framework/solid/guides/queries.md create mode 100644 docs/zh-hant/framework/solid/guides/query-cancellation.md create mode 100644 docs/zh-hant/framework/solid/guides/query-functions.md create mode 100644 docs/zh-hant/framework/solid/guides/query-invalidation.md create mode 100644 docs/zh-hant/framework/solid/guides/query-keys.md create mode 100644 docs/zh-hant/framework/solid/guides/query-options.md create mode 100644 docs/zh-hant/framework/solid/guides/query-retries.md create mode 100644 docs/zh-hant/framework/solid/guides/request-waterfalls.md create mode 100644 docs/zh-hant/framework/solid/guides/scroll-restoration.md create mode 100644 docs/zh-hant/framework/solid/guides/ssr.md create mode 100644 docs/zh-hant/framework/solid/guides/suspense.md create mode 100644 docs/zh-hant/framework/solid/guides/testing.md create mode 100644 docs/zh-hant/framework/solid/guides/updates-from-mutation-responses.md create mode 100644 docs/zh-hant/framework/solid/guides/window-focus-refetching.md create mode 100644 docs/zh-hant/framework/solid/installation.md create mode 100644 docs/zh-hant/framework/solid/overview.md create mode 100644 docs/zh-hant/framework/solid/plugins/broadcastQueryClient.md create mode 100644 docs/zh-hant/framework/solid/plugins/createPersister.md create mode 100644 docs/zh-hant/framework/solid/quick-start.md create mode 100644 docs/zh-hant/framework/solid/reference/hydration.md create mode 100644 docs/zh-hant/framework/solid/reference/infiniteQueryOptions.md create mode 100644 docs/zh-hant/framework/solid/reference/queryOptions.md create mode 100644 docs/zh-hant/framework/solid/reference/useInfiniteQuery.md create mode 100644 docs/zh-hant/framework/solid/reference/useIsFetching.md create mode 100644 docs/zh-hant/framework/solid/reference/useIsMutating.md create mode 100644 docs/zh-hant/framework/solid/reference/useMutation.md create mode 100644 docs/zh-hant/framework/solid/reference/useMutationState.md create mode 100644 docs/zh-hant/framework/solid/reference/useQueries.md create mode 100644 docs/zh-hant/framework/solid/reference/useQuery.md create mode 100644 docs/zh-hant/framework/solid/typescript.md create mode 100644 docs/zh-hant/framework/svelte/devtools.md create mode 100644 docs/zh-hant/framework/svelte/installation.md create mode 100644 docs/zh-hant/framework/svelte/overview.md create mode 100644 docs/zh-hant/framework/svelte/reactivity.md create mode 100644 docs/zh-hant/framework/svelte/reference/classes/hydrationboundary.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/createinfinitequery.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/createmutation.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/createqueries.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/createquery.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/getisrestoringcontext.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/getqueryclientcontext.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/infinitequeryoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/queryoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/setisrestoringcontext.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/setqueryclientcontext.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/usehydrate.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/useisfetching.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/useismutating.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/useisrestoring.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/usemutationstate.md create mode 100644 docs/zh-hant/framework/svelte/reference/functions/usequeryclient.md create mode 100644 docs/zh-hant/framework/svelte/reference/index.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createbasemutationresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createmutateasyncfunction.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createmutatefunction.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createmutationoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createmutationresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createqueryoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/createqueryresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatequeryresult.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/definedinitialdataoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/mutationstateoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/queriesoptions.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/queriesresults.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/storeorval.md create mode 100644 docs/zh-hant/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md create mode 100644 docs/zh-hant/framework/svelte/ssr.md create mode 100644 docs/zh-hant/framework/vue/community/community-projects.md create mode 100644 docs/zh-hant/framework/vue/community/tkdodos-blog.md create mode 100644 docs/zh-hant/framework/vue/devtools.md create mode 100644 docs/zh-hant/framework/vue/graphql.md create mode 100644 docs/zh-hant/framework/vue/guides/background-fetching-indicators.md create mode 100644 docs/zh-hant/framework/vue/guides/caching.md create mode 100644 docs/zh-hant/framework/vue/guides/custom-client.md create mode 100644 docs/zh-hant/framework/vue/guides/default-query-function.md create mode 100644 docs/zh-hant/framework/vue/guides/dependent-queries.md create mode 100644 docs/zh-hant/framework/vue/guides/disabling-queries.md create mode 100644 docs/zh-hant/framework/vue/guides/does-this-replace-client-state.md create mode 100644 docs/zh-hant/framework/vue/guides/filters.md create mode 100644 docs/zh-hant/framework/vue/guides/important-defaults.md create mode 100644 docs/zh-hant/framework/vue/guides/infinite-queries.md create mode 100644 docs/zh-hant/framework/vue/guides/initial-query-data.md create mode 100644 docs/zh-hant/framework/vue/guides/invalidations-from-mutations.md create mode 100644 docs/zh-hant/framework/vue/guides/migrating-to-v5.md create mode 100644 docs/zh-hant/framework/vue/guides/mutations.md create mode 100644 docs/zh-hant/framework/vue/guides/network-mode.md create mode 100644 docs/zh-hant/framework/vue/guides/optimistic-updates.md create mode 100644 docs/zh-hant/framework/vue/guides/paginated-queries.md create mode 100644 docs/zh-hant/framework/vue/guides/parallel-queries.md create mode 100644 docs/zh-hant/framework/vue/guides/placeholder-query-data.md create mode 100644 docs/zh-hant/framework/vue/guides/prefetching.md create mode 100644 docs/zh-hant/framework/vue/guides/queries.md create mode 100644 docs/zh-hant/framework/vue/guides/query-cancellation.md create mode 100644 docs/zh-hant/framework/vue/guides/query-functions.md create mode 100644 docs/zh-hant/framework/vue/guides/query-invalidation.md create mode 100644 docs/zh-hant/framework/vue/guides/query-keys.md create mode 100644 docs/zh-hant/framework/vue/guides/query-options.md create mode 100644 docs/zh-hant/framework/vue/guides/query-retries.md create mode 100644 docs/zh-hant/framework/vue/guides/scroll-restoration.md create mode 100644 docs/zh-hant/framework/vue/guides/ssr.md create mode 100644 docs/zh-hant/framework/vue/guides/suspense.md create mode 100644 docs/zh-hant/framework/vue/guides/testing.md create mode 100644 docs/zh-hant/framework/vue/guides/updates-from-mutation-responses.md create mode 100644 docs/zh-hant/framework/vue/guides/window-focus-refetching.md create mode 100644 docs/zh-hant/framework/vue/installation.md create mode 100644 docs/zh-hant/framework/vue/overview.md create mode 100644 docs/zh-hant/framework/vue/plugins/broadcastQueryClient.md create mode 100644 docs/zh-hant/framework/vue/plugins/createPersister.md create mode 100644 docs/zh-hant/framework/vue/quick-start.md create mode 100644 docs/zh-hant/framework/vue/reactivity.md create mode 100644 docs/zh-hant/framework/vue/reference/hydration.md create mode 100644 docs/zh-hant/framework/vue/reference/infiniteQueryOptions.md create mode 100644 docs/zh-hant/framework/vue/reference/queryOptions.md create mode 100644 docs/zh-hant/framework/vue/reference/useInfiniteQuery.md create mode 100644 docs/zh-hant/framework/vue/reference/useIsFetching.md create mode 100644 docs/zh-hant/framework/vue/reference/useIsMutating.md create mode 100644 docs/zh-hant/framework/vue/reference/useMutation.md create mode 100644 docs/zh-hant/framework/vue/reference/useMutationState.md create mode 100644 docs/zh-hant/framework/vue/reference/useQueries.md create mode 100644 docs/zh-hant/framework/vue/reference/useQuery.md create mode 100644 docs/zh-hant/framework/vue/reference/useQueryClient.md create mode 100644 docs/zh-hant/framework/vue/typescript.md create mode 100644 docs/zh-hant/reference/InfiniteQueryObserver.md create mode 100644 docs/zh-hant/reference/MutationCache.md create mode 100644 docs/zh-hant/reference/QueriesObserver.md create mode 100644 docs/zh-hant/reference/QueryCache.md create mode 100644 docs/zh-hant/reference/QueryClient.md create mode 100644 docs/zh-hant/reference/QueryObserver.md create mode 100644 docs/zh-hant/reference/focusManager.md create mode 100644 docs/zh-hant/reference/notifyManager.md create mode 100644 docs/zh-hant/reference/onlineManager.md create mode 100644 docs/zh-hant/reference/streamedQuery.md diff --git a/docs/zh-hant/eslint/eslint-plugin-query.md b/docs/zh-hant/eslint/eslint-plugin-query.md new file mode 100644 index 00000000000..e04128010b1 --- /dev/null +++ b/docs/zh-hant/eslint/eslint-plugin-query.md @@ -0,0 +1,103 @@ +--- +source-updated-at: '2025-04-07T09:17:45.000Z' +translation-updated-at: '2025-05-08T20:15:17.659Z' +id: eslint-plugin-query +title: ESLint 插件 Query +--- + +TanStack Query 提供了專屬的 ESLint 插件。此插件用於強制執行最佳實踐,並幫助您避免常見錯誤。 + +## 安裝 + +該插件是一個獨立套件,需另行安裝: + +```bash +npm i -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +pnpm add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +yarn add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +bun add -D @tanstack/eslint-plugin-query +``` + +## 扁平化設定 (`eslint.config.js`) + +### 推薦設定 + +若要啟用所有推薦規則,請加入以下設定: + +```js +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', + }, + }, + // 其他設定... +] +``` + +## 傳統設定 (`.eslintrc`) + +### 推薦設定 + +若要啟用所有推薦規則,請在 `extends` 中加入 `plugin:@tanstack/query/recommended`: + +```json +{ + "extends": ["plugin:@tanstack/query/recommended"] +} +``` + +### 自訂設定 + +或者,在 `plugins` 區塊加入 `@tanstack/query`,並配置想使用的規則: + +```json +{ + "plugins": ["@tanstack/query"], + "rules": { + "@tanstack/query/exhaustive-deps": "error" + } +} +``` + +## 規則 + +- [@tanstack/query/exhaustive-deps](./exhaustive-deps.md) +- [@tanstack/query/no-rest-destructuring](./no-rest-destructuring.md) +- [@tanstack/query/stable-query-client](./stable-query-client.md) +- [@tanstack/query/no-unstable-deps](./no-unstable-deps.md) +- [@tanstack/query/infinite-query-property-order](./infinite-query-property-order.md) +- [@tanstack/query/no-void-query-fn](./no-void-query-fn.md) diff --git a/docs/zh-hant/eslint/exhaustive-deps.md b/docs/zh-hant/eslint/exhaustive-deps.md new file mode 100644 index 00000000000..0d0e1b66246 --- /dev/null +++ b/docs/zh-hant/eslint/exhaustive-deps.md @@ -0,0 +1,48 @@ +--- +source-updated-at: '2024-01-26T20:53:47.000Z' +translation-updated-at: '2025-05-08T20:15:06.785Z' +id: exhaustive-deps +title: 徹底依賴檢查 +--- + +查詢鍵 (query keys) 應視為查詢函式的依賴陣列 (dependency array):任何在 queryFn 中使用的變數都應加入查詢鍵中。 +這能確保查詢會被獨立快取,且當變數變更時查詢會自動重新取得。 + +## 規則詳情 + +**錯誤**程式碼範例: + +```tsx +/* 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) }), +} +``` + +## 不適用情境 + +若您不在意查詢鍵的規則,則不需要此規則。 + +## 屬性 + +- [x] ✅ 推薦 +- [x] 🔧 可自動修復 diff --git a/docs/zh-hant/eslint/infinite-query-property-order.md b/docs/zh-hant/eslint/infinite-query-property-order.md new file mode 100644 index 00000000000..104e97c2071 --- /dev/null +++ b/docs/zh-hant/eslint/infinite-query-property-order.md @@ -0,0 +1,65 @@ +--- +source-updated-at: '2024-09-20T19:45:40.000Z' +translation-updated-at: '2025-05-08T20:15:12.474Z' +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, +}) +``` + +以下為 **正確** 的程式碼範例: + +```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, +}) +``` + +## 屬性 + +- [x] ✅ 推薦 +- [x] 🔧 可自動修正 diff --git a/docs/zh-hant/eslint/no-rest-destructuring.md b/docs/zh-hant/eslint/no-rest-destructuring.md new file mode 100644 index 00000000000..2518e0a1f40 --- /dev/null +++ b/docs/zh-hant/eslint/no-rest-destructuring.md @@ -0,0 +1,49 @@ +--- +source-updated-at: '2024-01-26T20:53:47.000Z' +translation-updated-at: '2025-05-08T20:15:05.763Z' +id: no-rest-destructuring +title: 無其餘解構 +--- + +禁止對查詢結果使用物件剩餘解構 (Disallow object rest destructuring on query results) + +## 動機 + +在查詢結果上使用物件剩餘解構會自動訂閱查詢結果的每個欄位,這可能導致不必要的重新渲染。此規則確保您僅訂閱實際需要的欄位。 + +## 規則詳情 + +**錯誤**程式碼範例: + +```tsx +/* 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 +``` + +## 例外情況 + +若您手動設定 `notifyOnChangeProps` 選項,可以停用此規則。由於您未使用追蹤查詢,需自行指定哪些屬性應觸發重新渲染。 + +## 屬性 + +- [x] ✅ 推薦使用 +- [ ] 🔧 可自動修復 diff --git a/docs/zh-hant/eslint/no-unstable-deps.md b/docs/zh-hant/eslint/no-unstable-deps.md new file mode 100644 index 00000000000..ba1c01a56a8 --- /dev/null +++ b/docs/zh-hant/eslint/no-unstable-deps.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-08-20T18:51:35.000Z' +translation-updated-at: '2025-05-08T20:15:00.234Z' +id: no-unstable-deps +title: 無不穩定依賴 +--- + +以下查詢鉤子 (query hooks) 回傳的物件**不具**參考穩定性 (referentially stable): + +- `useQuery` +- `useSuspenseQuery` +- `useQueries` +- `useSuspenseQueries` +- `useInfiniteQuery` +- `useSuspenseInfiniteQuery` +- `useMutation` + +這些鉤子回傳的物件**不應**直接被放入 React 鉤子 (如 `useEffect`、`useMemo`、`useCallback`) 的依賴陣列 (dependency array) 中。 +正確做法是解構 (destructure) 查詢鉤子的回傳值,並將解構後的值傳入 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 +} +``` + +**正確**範例程式碼: + +```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 +} +``` + +## 屬性 + +- [x] ✅ 推薦 +- [ ] 🔧 可自動修復 diff --git a/docs/zh-hant/eslint/no-void-query-fn.md b/docs/zh-hant/eslint/no-void-query-fn.md new file mode 100644 index 00000000000..e652926cf8f --- /dev/null +++ b/docs/zh-hant/eslint/no-void-query-fn.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2025-04-07T09:17:45.000Z' +translation-updated-at: '2025-05-08T20:14:52.848Z' +id: no-void-query-fn +title: Disallow returning void from query functions +--- + +查詢函式必須回傳一個會被 TanStack Query 快取的值。沒有回傳值的函式(void 函式)可能導致非預期的行為,並可能表示實作中存在錯誤。 + +## 規則詳情 + +此規則的**錯誤**程式碼範例: + +```tsx +/* 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 + }, +}) +``` + +## 屬性 + +- [x] ✅ 推薦 +- [ ] 🔧 可自動修復 diff --git a/docs/zh-hant/eslint/stable-query-client.md b/docs/zh-hant/eslint/stable-query-client.md new file mode 100644 index 00000000000..b46f4c72d19 --- /dev/null +++ b/docs/zh-hant/eslint/stable-query-client.md @@ -0,0 +1,63 @@ +--- +source-updated-at: '2024-09-26T17:04:33.000Z' +translation-updated-at: '2025-05-08T20:14:53.379Z' +id: stable-query-client +title: 穩定查詢客戶端 +--- + +QueryClient 包含 QueryCache,因此你應該只在應用程式的生命週期中建立一個 QueryClient 實例,而不是在每次渲染時都建立新實例。 + +> 例外:允許在非同步伺服器元件 (async Server Component) 中建立新的 QueryClient,因為該非同步函式僅會在伺服器端呼叫一次。 + +## 規則詳情 + +以下為**不正確**的程式碼範例: + +```tsx +/* eslint "@tanstack/query/stable-query-client": "error" */ + +function App() { + const queryClient = new QueryClient() + return ( + + + + ) +} +``` + +以下為**正確**的程式碼範例: + +```tsx +function App() { + const [queryClient] = useState(() => new QueryClient()) + return ( + + + + ) +} +``` + +```tsx +const queryClient = new QueryClient() +function App() { + return ( + + + + ) +} +``` + +```tsx +async function App() { + const queryClient = new QueryClient() + await queryClient.prefetchQuery(options) +} +``` + +## 屬性 + +- [x] ✅ 推薦 +- [x] 🔧 可自動修復 diff --git a/docs/zh-hant/framework/angular/angular-httpclient-and-other-data-fetching-clients.md b/docs/zh-hant/framework/angular/angular-httpclient-and-other-data-fetching-clients.md new file mode 100644 index 00000000000..7c38256e80d --- /dev/null +++ b/docs/zh-hant/framework/angular/angular-httpclient-and-other-data-fetching-clients.md @@ -0,0 +1,50 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-08T20:17:43.461Z' +id: Angular-HttpClient-and-other-data-fetching-clients +title: Angular HttpClient 與其他資料獲取客戶端 +--- + +由於 TanStack Query 的資料獲取機制是基於 Promise 不可知論 (Promise-agnostic) 設計的,您實際上可以使用任何非同步資料獲取客戶端,包括瀏覽器原生的 `fetch` API、`graphql-request` 等等。 + +## 使用 Angular 的 `HttpClient` 進行資料獲取 + +`HttpClient` 是 Angular 強大且整合完善的一部分,具有以下優勢: + +- 在單元測試中使用 [provideHttpClientTesting](https://angular.dev/guide/http/testing) 模擬回應。 +- [攔截器 (Interceptors)](https://angular.dev/guide/http/interceptors) 可用於多種功能,例如添加驗證標頭、執行日誌記錄等。雖然某些資料獲取函式庫有自己的攔截器系統,但 `HttpClient` 的攔截器與 Angular 的依賴注入系統深度整合。 +- `HttpClient` 會自動通知 [`PendingTasks`](https://angular.dev/api/core/PendingTasks#),讓 Angular 能感知待處理的請求。單元測試和 SSR 可利用應用程式的 _穩定性_ 資訊來等待待處理請求完成,這使得 [無區域 (Zoneless)](https://angular.dev/guide/experimental/zoneless) 應用的單元測試更加容易。 +- 使用 SSR 時,`HttpClient` 會[快取伺服器端的請求](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient),避免客戶端發出不必要的請求。`HttpClient` 的 SSR 快取功能開箱即用。TanStack Query 雖有更強大的 hydration 功能,但需要額外設定。選擇哪種方案取決於您的具體需求。 + +### 在 `queryFn` 中使用可觀察物件 (Observables) + +由於 TanStack Query 是基於 Promise 的函式庫,來自 `HttpClient` 的可觀察物件需轉換為 Promise。這可透過 `rxjs` 的 `lastValueFrom` 或 `firstValueFrom` 函式實現。 + +```ts +@Component({ + // ... +}) +class ExampleComponent { + private readonly http = inject(HttpClient) + + readonly query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + lastValueFrom( + this.http.get('https://api.github.com/repos/tanstack/query'), + ), + })) +} +``` + +> 由於 Angular 正逐步將 RxJS 改為可選依賴,預計 `HttpClient` 未來也將支援 Promise。 +> +> TanStack Query for Angular 計劃加入對可觀察物件的支援。 + +## 比較表格 + +| 資料獲取客戶端 | 優點 | 缺點 | +| ---------------------------------- | -------------------------------- | --------------------------------------------- | +| **Angular HttpClient** | 功能豐富且與 Angular 深度整合。 | 需將可觀察物件轉換為 Promise。 | +| **Fetch** | 瀏覽器原生 API,不增加套件體積。 | 功能陽春的 API,缺乏許多進階特性。 | +| **專用函式庫如 `graphql-request`** | 針對特定使用場景提供專屬功能。 | 若非 Angular 專用函式庫,與框架的整合度較差。 | diff --git a/docs/zh-hant/framework/angular/devtools.md b/docs/zh-hant/framework/angular/devtools.md new file mode 100644 index 00000000000..3f661c15b8a --- /dev/null +++ b/docs/zh-hant/framework/angular/devtools.md @@ -0,0 +1,117 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-08T20:17:59.962Z' +id: devtools +title: 開發工具 +--- + +## 啟用開發者工具 (Devtools) + +開發者工具能協助你除錯及檢查查詢 (queries) 與變異 (mutations)。你可以透過在 `provideTanStackQuery` 中加入 `withDevtools` 來啟用開發者工具。 + +預設情況下,當 Angular 的 [`isDevMode`](https://angular.dev/api/core/isDevMode) 回傳 true 時,開發者工具會自動啟用。因此你無需擔心在正式環境建置時會包含這些工具。核心工具會延遲載入 (lazily loaded) 並從打包程式碼中排除。在多數情況下,你只需在 `provideTanStackQuery` 中加入 `withDevtools()` 而無需額外設定。 + +```ts +import { + QueryClient, + provideTanStackQuery, + withDevtools, +} from '@tanstack/angular-query-experimental' + +export const appConfig: ApplicationConfig = { + providers: [provideTanStackQuery(new QueryClient(), withDevtools())], +} +``` + +## 設定開發者工具的載入時機 + +若你需要更精確控制開發者工具的載入時機,可以使用 `loadDevtools` 選項。這在需要根據環境設定載入工具時特別有用,例如測試環境可能運行在正式模式但仍需使用開發者工具。 + +當未設定此選項或設為 'auto' 時,開發者工具會在 Angular 處於開發模式時載入。 + +```ts +provideTanStackQuery(new QueryClient(), withDevtools()) + +// 等同於 +provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ loadDevtools: 'auto' })), +) +``` + +當設為 true 時,開發者工具會在開發與正式模式中都載入。 + +```ts +provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ loadDevtools: true })), +) +``` + +當設為 false 時,開發者工具將不會載入。 + +```ts +provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ loadDevtools: false })), +) +``` + +`withDevtools` 的選項透過回呼函式 (callback function) 回傳以支援透過訊號 (signals) 實現的反應式 (reactivity) 功能。以下範例中,我們從監聽鍵盤快捷鍵的 RxJS 可觀察物件 (observable) 建立訊號。當事件觸發時,開發者工具會延遲載入。此技術讓你能支援在正式模式中按需載入開發者工具,而無需將完整工具包含在打包程式碼中。 + +```ts +@Injectable({ providedIn: 'root' }) +class DevtoolsOptionsManager { + loadDevtools = toSignal( + fromEvent(document, 'keydown').pipe( + map( + (event): boolean => + event.metaKey && event.ctrlKey && event.shiftKey && event.key === 'D', + ), + scan((acc, curr) => acc || curr, false), + ), + { + initialValue: false, + }, + ) +} + +export const appConfig: ApplicationConfig = { + providers: [ + provideHttpClient(), + provideTanStackQuery( + new QueryClient(), + withDevtools(() => ({ + initialIsOpen: true, + loadDevtools: inject(DevtoolsOptionsManager).loadDevtools(), + })), + ), + ], +} +``` + +### 選項 + +以下選項 `client`、`position`、`errorTypes`、`buttonPosition` 和 `initialIsOpen` 支援透過訊號實現的反應式功能。 + +- `loadDevtools?: 'auto' | boolean` + - 預設為 `auto`: 在開發模式時延遲載入開發者工具,正式模式則跳過載入。 + - 用於控制是否載入開發者工具。 +- `initialIsOpen?: Boolean` + - 設為 `true` 可讓工具面板預設為開啟狀態 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` + - 預設為 `bottom-right` + - TanStack 商標按鈕的位置,用於開啟/關閉開發者工具面板 + - 設為 `relative` 時,按鈕會置於你渲染開發者工具的位置。 +- `position?: "top" | "bottom" | "left" | "right"` + - 預設為 `bottom` + - Angular Query 開發者工具面板的位置 +- `client?: QueryClient`, + - 用於指定自訂的 QueryClient。若未提供,則會注入透過 `provideTanStackQuery` 提供的 QueryClient。 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}[]` + - 用於預先定義可在查詢中觸發的錯誤類型。當從 UI 觸發該錯誤時,初始化器 (initializer) 會以特定查詢為參數被呼叫,並必須回傳一個錯誤物件。 +- `styleNonce?: string` + - 用於傳遞 nonce 給加入文件 head 的 style 標籤。這在使用內容安全政策 (CSP) nonce 允許內嵌樣式時特別有用。 +- `shadowDOMTarget?: ShadowRoot` + - 預設行為會將開發者工具的樣式套用到 DOM 中的 head 標籤。 + - 用於指定 shadow DOM 目標,讓樣式套用在 shadow DOM 內而非 light DOM 的 head 標籤中。 diff --git a/docs/zh-hant/framework/angular/guides/background-fetching-indicators.md b/docs/zh-hant/framework/angular/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..6bd7d457b17 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/background-fetching-indicators.md @@ -0,0 +1,54 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-08T20:26:11.071Z' +id: background-fetching-indicators +title: 背景獲取指示器 +--- + +查詢的 `status === 'pending'` 狀態足以顯示查詢的初始硬載入狀態,但有時您可能希望額外顯示一個指示器,表示查詢正在背景重新獲取資料。為此,查詢還提供了一個 `isFetching` 布林值,您可以用來顯示它正處於獲取狀態,無論 `status` 變數的狀態為何: + +```angular-ts +@Component({ + selector: 'todos', + template: ` + @if (todosQuery.isPending()) { + Loading... + } @else if (todosQuery.isError()) { + An error has occurred: {{ todosQuery.error().message }} + } @else if (todosQuery.isSuccess()) { + @if (todosQuery.isFetching()) { + Refreshing... + } + @for (todos of todosQuery.data(); track todo.id) { + + } + } + `, +}) +class TodosComponent { + todosQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) +} +``` + +## 顯示全域背景獲取載入狀態 + +除了個別查詢的載入狀態外,如果您希望在**任何**查詢正在獲取資料時(包括在背景)顯示全域載入指示器,可以使用 `useIsFetching` 鉤子: + +```angular-ts +import { injectIsFetching } from '@tanstack/angular-query-experimental' + +@Component({ + selector: 'global-loading-indicator', + template: ` + @if (isFetching()) { +
    Queries are fetching in the background...
    + } + `, +}) +export class GlobalLoadingIndicatorComponent { + isFetching = injectIsFetching() +} +``` diff --git a/docs/zh-hant/framework/angular/guides/caching.md b/docs/zh-hant/framework/angular/guides/caching.md new file mode 100644 index 00000000000..06efb48ba9a --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/caching.md @@ -0,0 +1,35 @@ +--- +source-updated-at: '2025-04-13T18:21:31.000Z' +translation-updated-at: '2025-05-08T20:26:12.104Z' +id: caching +title: 快取 +--- + +> 請在閱讀本指南前,先詳細閱讀[重要預設值](./important-defaults.md) + +## 基礎範例 + +這個快取範例說明了以下情境的生命週期: + +- 有無快取資料的查詢實例 (Query Instances) +- 背景重新獲取 (Background Refetching) +- 非活躍查詢 (Inactive Queries) +- 垃圾回收機制 (Garbage Collection) + +假設我們使用預設的 `gcTime` 值 **5 分鐘** 和預設的 `staleTime` 值 `0`。 + +- 當一個新的 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos }))` 實例初始化時: + - 由於尚未有其他使用 `['todos']` 查詢鍵 (query key) 的查詢,此查詢會顯示硬載入狀態 (hard loading state) 並發起網路請求以獲取資料。 + - 當網路請求完成時,返回的資料會被快取在 `['todos']` 鍵值下。 + - 資料會根據設定的 `staleTime`(預設為 `0`,即立即)標記為過時 (stale)。 +- 當第二個 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 實例在其他地方初始化時: + - 由於快取中已有來自第一個查詢的 `['todos']` 鍵值資料,該資料會立即從快取中返回。 + - 新實例會使用其查詢函數觸發新的網路請求。 + - 請注意,無論兩個 `fetchTodos` 查詢函數是否相同,兩個查詢的 [`status`](../../reference/injectQuery.md)(包括 `isFetching`、`isPending` 和其他相關值)都會更新,因為它們具有相同的查詢鍵。 + - 當請求成功完成時,快取中 `['todos']` 鍵值的資料會更新為新資料,兩個實例也會同步更新為新資料。 +- 當兩個 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 查詢實例都被銷毀且不再使用時: + - 由於該查詢已無活躍實例,系統會根據 `gcTime` 設定一個垃圾回收超時(預設為 **5 分鐘**)來刪除並回收該查詢。 +- 在快取超時完成前,另一個 `injectQuery(() => ({ queryKey: ['todos'], queyFn: fetchTodos })` 實例掛載。該查詢會立即返回可用的快取資料,同時在背景執行 `fetchTodos` 函數。當背景請求成功完成時,會用新資料更新快取。 +- 最後一個 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 實例被銷毀。 +- 在 **5 分鐘** 內沒有再出現任何 `injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })` 實例: + - `['todos']` 鍵值下的快取資料會被刪除並進行垃圾回收。 diff --git a/docs/zh-hant/framework/angular/guides/default-query-function.md b/docs/zh-hant/framework/angular/guides/default-query-function.md new file mode 100644 index 00000000000..cde14f0a9a9 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/default-query-function.md @@ -0,0 +1,50 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-08T20:25:52.045Z' +id: default-query-function +title: 預設查詢函數 +--- + +如果你基於任何原因,希望能在整個應用程式中共享同一個查詢函式,並僅透過查詢鍵 (query key) 來識別應該獲取的資料,你可以透過為 TanStack Query 提供一個 **預設查詢函式 (default query function)** 來實現: + +```ts +// 定義一個預設查詢函式,它會接收查詢鍵 +const defaultQueryFn: QueryFunction = async ({ queryKey }) => { + const { data } = await axios.get( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + ) + return data +} + +// 透過 defaultOptions 將預設查詢函式提供給你的應用程式 +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, + }, +}) + +bootstrapApplication(MyAppComponent, { + providers: [provideTanStackQuery(queryClient)], +}) + +export class PostsComponent { + // 現在你只需要傳入一個鍵! + postsQuery = injectQuery>(() => ({ + queryKey: ['/posts'], + })) + // ... +} + +export class PostComponent { + // 你甚至可以省略 queryFn,直接傳入選項 + postQuery = injectQuery(() => ({ + enabled: this.postIdSignal() > 0, + queryKey: [`/posts/${this.postIdSignal()}`], + })) + // ... +} +``` + +如果你想要覆寫預設的 queryFn,只需像平常一樣提供你自己的函式即可。 diff --git a/docs/zh-hant/framework/angular/guides/dependent-queries.md b/docs/zh-hant/framework/angular/guides/dependent-queries.md new file mode 100644 index 00000000000..d888aba6515 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/dependent-queries.md @@ -0,0 +1,66 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:56.372Z' +id: dependent-queries +title: 依賴查詢 +--- + +## injectQuery 依賴查詢 (Dependent Query) + +依賴查詢 (或稱串行查詢) 需要等待前一個查詢完成才能執行。實現方式很簡單,只需使用 `enabled` 選項來告知查詢何時可以執行: + +```ts +// 先獲取使用者資料 +userQuery = injectQuery(() => ({ + queryKey: ['user', email], + queryFn: getUserByEmail, +})) + +// 然後獲取該使用者的專案資料 +projectsQuery = injectQuery(() => ({ + queryKey: ['projects', this.userQuery.data()?.id], + queryFn: getProjectsByUser, + // 此查詢會等到使用者 ID 存在後才執行 + enabled: !!this.userQuery.data()?.id, +})) +``` + +`projects` 查詢會以以下狀態開始: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'idle' +``` + +當 `user` 資料可用時,`projects` 查詢會被 `enabled` 並轉變為: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'fetching' +``` + +當專案資料獲取完成後,狀態會變為: + +```tsx +status: 'success' +isPending: false +fetchStatus: 'idle' +``` + +## injectQueries 依賴查詢 (Dependent Query) + +動態平行查詢 - `injectQueries` 也可以依賴前一個查詢,實現方式如下: + +```ts +// injectQueries 目前在 Angular Query 中處於開發階段 +``` + +**注意** `injectQueries` 會返回一個**查詢結果陣列** + +## 關於效能的說明 + +依賴查詢本質上會形成一種[請求瀑布 (request waterfall)](./request-waterfalls.md),這會影響效能。假設兩個查詢花費相同時間,串行執行總會比平行執行多花一倍時間,在客戶端高延遲環境下尤其不利。如果可能,最好重構後端 API 讓兩個查詢能平行獲取,雖然這並非總是可行。 + +在上例中,與其先執行 `getUserByEmail` 才能執行 `getProjectsByUser`,引入新的 `getProjectsByUserEmail` 查詢可以消除請求瀑布。 diff --git a/docs/zh-hant/framework/angular/guides/disabling-queries.md b/docs/zh-hant/framework/angular/guides/disabling-queries.md new file mode 100644 index 00000000000..fb8982dcd9b --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/disabling-queries.md @@ -0,0 +1,120 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-08T20:26:18.461Z' +id: disabling-queries +title: 停用/暫停查詢 +--- + +若想阻止查詢 (query) 自動執行,可使用 `enabled = false` 選項。`enabled` 選項也接受回傳布林值的回調函式。 + +當 `enabled` 為 `false` 時: + +- 若查詢有快取資料,則會以 `status === 'success'` 或 `isSuccess` 狀態初始化。 +- 若查詢無快取資料,則會以 `status === 'pending'` 和 `fetchStatus === 'idle'` 狀態開始。 +- 查詢不會在掛載時自動抓取資料。 +- 查詢不會在背景自動重新抓取。 +- 查詢會忽略查詢客戶端 (query client) 的 `invalidateQueries` 和 `refetchQueries` 呼叫(這些呼叫通常會觸發重新抓取)。 +- 從 `injectQuery` 回傳的 `refetch` 可用於手動觸發查詢抓取,但與 `skipToken` 併用時無效。 + +> TypeScript 使用者可改用 [skipToken](#typesafe-disabling-of-queries-using-skiptoken) 作為 `enabled = false` 的替代方案。 + +```angular-ts +@Component({ + selector: 'todos', + template: `
    + + + @if (query.data()) { +
      + @for (todo of query.data(); track todo.id) { +
    • {{ todo.title }}
    • + } +
    + } @else { + @if (query.isError()) { + Error: {{ query.error().message }} + } @else if (query.isLoading()) { + Loading... + } @else if (!query.isLoading() && !query.isError()) { + Not ready ... + } + } + +
    {{ query.isLoading() ? 'Fetching...' : '' }}
    +
    `, +}) +export class TodosComponent { + query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + enabled: false, + })) +} +``` + +永久停用查詢會使您無法使用 TanStack Query 提供的許多強大功能(例如背景重新抓取),這也非慣用做法。此做法會讓您從宣告式模式(定義查詢執行時機的依賴條件)轉為命令式模式(點擊時才抓取資料),且無法傳遞參數給 `refetch`。多數情況下,您真正需要的是延遲初始抓取的「懶查詢 (lazy query)」: + +## 懶查詢 (Lazy Queries) + +`enabled` 選項不僅能永久停用查詢,還能在後續動態啟用/停用。典型範例是篩選表單——您可能希望使用者輸入篩選值後才發送首次請求: + +```angular-ts +@Component({ + selector: 'todos', + template: ` +
    + // 🚀 套用篩選條件將啟用並執行查詢 + + +
    + `, +}) +export class TodosComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + enabled: !!this.filter(), + })) +} +``` + +### isLoading (原為 `isInitialLoading`) + +懶查詢會從開始就處於 `status: 'pending'` 狀態,因為 `pending` 表示尚未取得資料。雖然技術上正確,但由於當前並未抓取資料(查詢未啟用),您可能無法用此標誌顯示載入旋轉圖示。 + +若使用停用或懶查詢,可改用 `isLoading` 標誌。此為衍生標誌,由以下條件計算: + +`isPending && isFetching` + +因此僅在查詢首次抓取資料時會回傳 `true`。 + +## 使用 `skipToken` 實現類型安全的查詢停用 + +若使用 TypeScript,可用 `skipToken` 停用查詢。這適用於需基於條件停用查詢,但仍需保持類型安全的場景。 + +> 重要:`injectQuery` 的 `refetch` 與 `skipToken` 併用時無效,除此之外 `skipToken` 行為與 `enabled: false` 相同。 + +```angular-ts +import { skipToken, injectQuery } from '@tanstack/query-angular' + +@Component({ + selector: 'todos', + template: ` +
    + // 🚀 套用篩選條件將啟用並執行查詢 + + +
    + `, +}) +export class TodosComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: this.filter() ? () => fetchTodos(this.filter()) : skipToken, + })) +} +``` diff --git a/docs/zh-hant/framework/angular/guides/does-this-replace-client-state.md b/docs/zh-hant/framework/angular/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..9607ebd4301 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/does-this-replace-client-state.md @@ -0,0 +1,11 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:28.571Z' +id: does-this-replace-client-state +title: Does TanStack Query replace global state managers? +ref: docs/zh-hant/framework/react/guides/does-this-replace-client-state.md +replace: + useQuery: injectQuery + useMutation: injectMutation + hook: function +--- diff --git a/docs/zh-hant/framework/angular/guides/filters.md b/docs/zh-hant/framework/angular/guides/filters.md new file mode 100644 index 00000000000..c13f32f5b4c --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/filters.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:28.552Z' +id: filters +title: Filters +ref: docs/zh-hant/framework/react/guides/filters.md +--- diff --git a/docs/zh-hant/framework/angular/guides/important-defaults.md b/docs/zh-hant/framework/angular/guides/important-defaults.md new file mode 100644 index 00000000000..8ba24cf0afb --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/important-defaults.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:28.533Z' +id: important-defaults +title: Important Defaults +ref: docs/zh-hant/framework/react/guides/important-defaults.md +replace: + React: Angular + react-query: angular-query + useQuery: injectQuery + useInfiniteQuery: injectInfiniteQuery + useMemo and useCallback: setting signal values +--- + +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/angular/guides/infinite-queries.md b/docs/zh-hant/framework/angular/guides/infinite-queries.md new file mode 100644 index 00000000000..11d38c3bf4d --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/infinite-queries.md @@ -0,0 +1,246 @@ +--- +source-updated-at: '2024-07-19T09:36:35.000Z' +translation-updated-at: '2025-05-08T20:27:00.713Z' +id: infinite-queries +title: 無限查詢 +--- + +# 無限查詢 (Infinite Queries) + +能夠以增量方式「載入更多」資料到現有資料集或實現「無限滾動」的列表渲染,是非常常見的 UI 模式。TanStack Query 支援一個名為 `injectInfiniteQuery` 的 `injectQuery` 變體,專門用於查詢這類型的列表。 + +使用 `injectInfiniteQuery` 時,你會注意到一些不同之處: + +- `data` 現在是一個包含無限查詢資料的物件: + - `data.pages` 陣列包含已獲取的頁面 + - `data.pageParams` 陣列包含用於獲取頁面的頁面參數 +- 現在可使用 `fetchNextPage` 和 `fetchPreviousPage` 函式(`fetchNextPage` 是必需的) +- 現在可使用(且必須提供)`initialPageParam` 選項來指定初始頁面參數 +- 可使用 `getNextPageParam` 和 `getPreviousPageParam` 選項來判斷是否有更多資料需要載入,以及獲取這些資料所需的資訊。這些資訊會作為查詢函式的附加參數提供 +- 現在提供 `hasNextPage` 布林值,當 `getNextPageParam` 返回值不是 `null` 或 `undefined` 時為 `true` +- 現在提供 `hasPreviousPage` 布林值,當 `getPreviousPageParam` 返回值不是 `null` 或 `undefined` 時為 `true` +- 現在提供 `isFetchingNextPage` 和 `isFetchingPreviousPage` 布林值,用於區分背景刷新狀態和載入更多狀態 + +> 注意:選項 `initialData` 或 `placeholderData` 需要符合具有 `data.pages` 和 `data.pageParams` 屬性的物件結構。 + +## 範例 + +假設我們有一個 API,基於 `cursor` 索引每次返回 3 個 `projects` 頁面,以及可用於獲取下一組專案的游標: + +```tsx +fetch('/api/projects?cursor=0') +// { data: [...], nextCursor: 3} +fetch('/api/projects?cursor=3') +// { data: [...], nextCursor: 6} +fetch('/api/projects?cursor=6') +// { data: [...], nextCursor: 9} +fetch('/api/projects?cursor=9') +// { data: [...] } +``` + +根據這些資訊,我們可以通過以下方式建立「載入更多」UI: + +- 預設等待 `injectInfiniteQuery` 請求第一組資料 +- 在 `getNextPageParam` 中返回下一個查詢的資訊 +- 呼叫 `fetchNextPage` 函式 + +```angular-ts +import { Component, computed, inject } from '@angular/core' +import { injectInfiniteQuery } from '@tanstack/angular-query-experimental' +import { lastValueFrom } from 'rxjs' +import { ProjectsService } from './projects-service' + +@Component({ + selector: 'example', + templateUrl: './example.component.html', +}) +export class Example { + projectsService = inject(ProjectsService) + + query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: async ({ pageParam }) => { + return lastValueFrom(this.projectsService.getProjects(pageParam)) + }, + initialPageParam: 0, + getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined, + getNextPageParam: (lastPage) => lastPage.nextId ?? undefined, + maxPages: 3, + })) + + nextButtonDisabled = computed( + () => !this.#hasNextPage() || this.#isFetchingNextPage(), + ) + nextButtonText = computed(() => + this.#isFetchingNextPage() + ? '載入更多中...' + : this.#hasNextPage() + ? '載入新資料' + : '已無更多資料', + ) + + #hasNextPage = this.query.hasNextPage + #isFetchingNextPage = this.query.isFetchingNextPage +} +``` + +```angular-html +
    + @if (query.isPending()) { +

    載入中...

    + } @else if (query.isError()) { + 錯誤: {{ query?.error().message }} + } @else { @for (page of query?.data().pages; track $index) { @for (project of + page.data; track project.id) { +

    {{ project.name }} {{ project.id }}

    + } } +
    + +
    + } +
    +``` + +必須理解的是,在進行中的 fetch 期間呼叫 `fetchNextPage` 有可能會覆蓋正在背景進行的資料刷新。當同時渲染列表和觸發 `fetchNextPage` 時,這種情況變得特別關鍵。 + +請記住,對於 InfiniteQuery 只能有一個進行中的 fetch。所有頁面共享單一快取條目,嘗試同時進行兩次 fetch 可能會導致資料覆寫。 + +如果你想啟用同時 fetch,可以在 `fetchNextPage` 中使用 `{ cancelRefetch: false }` 選項(預設為 true)。 + +為了確保查詢過程無衝突,強烈建議檢查查詢是否處於 `isFetching` 狀態,特別是當使用者不會直接控制該呼叫時。 + +```angular-ts +@Component({ + template: ` `, +}) +export class Example { + query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: async ({ pageParam }) => { + return lastValueFrom(this.projectsService.getProjects(pageParam)) + }, + })) + + fetchNextPage() { + // 如果已在獲取中,則不執行任何操作 + if (this.query.isFetching()) return + this.query.fetchNextPage() + } +} +``` + +## 當無限查詢需要重新獲取時會發生什麼? + +當無限查詢變為 `stale` 並需要重新獲取時,每組資料會從第一組開始「依序」獲取。這確保即使基礎資料發生變更,我們也不會使用過時的游標,從而可能導致重複或跳過記錄。如果無限查詢的結果從 queryCache 中移除,分頁將從初始狀態重新開始,僅請求初始組。 + +## 如果想實現雙向無限列表怎麼辦? + +雙向列表可以通過使用 `getPreviousPageParam`、`fetchPreviousPage`、`hasPreviousPage` 和 `isFetchingPreviousPage` 屬性和函式來實現。 + +```ts +query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, +})) +``` + +## 如果想以相反順序顯示頁面怎麼辦? + +有時你可能想以相反順序顯示頁面。如果是這種情況,可以使用 `select` 選項: + +```ts +query = injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), +})) +``` + +## 如果想手動更新無限查詢怎麼辦? + +### 手動移除第一頁: + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1), +})) +``` + +### 手動從單個頁面中移除單個值: + +```tsx +const newPagesArray = + oldPagesArray?.pages.map((page) => + page.filter((val) => val.id !== updatedId), + ) ?? [] + +queryClient.setQueryData(['projects'], (data) => ({ + pages: newPagesArray, + pageParams: data.pageParams, +})) +``` + +### 僅保留第一頁: + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(0, 1), + pageParams: data.pageParams.slice(0, 1), +})) +``` + +確保始終保持 pages 和 pageParams 的相同資料結構! + +## 如果想限制頁面數量怎麼辦? + +在某些使用情境下,你可能想限制查詢資料中儲存的頁面數量以提高效能和使用者體驗: + +- 當使用者可以載入大量頁面時(記憶體使用) +- 當需要重新獲取包含數十頁的無限查詢時(網路使用:所有頁面會依序獲取) + +解決方案是使用「有限無限查詢」。這可以通過將 `maxPages` 選項與 `getNextPageParam` 和 `getPreviousPageParam` 結合使用來實現,以便在需要時雙向獲取頁面。 + +在以下範例中,查詢資料 pages 陣列中僅保留 3 頁。如果需要重新獲取,將僅依序重新獲取 3 頁。 + +```ts +injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, + maxPages: 3, +})) +``` + +## 如果我的 API 不返回游標怎麼辦? + +如果你的 API 不返回游標,可以使用 `pageParam` 作為游標。因為 `getNextPageParam` 和 `getPreviousPageParam` 也會獲取當前頁面的 `pageParam`,所以可以用它來計算下一個/上一個頁面參數。 + +```ts +injectInfiniteQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, allPages, lastPageParam) => { + if (lastPage.length === 0) { + return undefined + } + return lastPageParam + 1 + }, + getPreviousPageParam: (firstPage, allPages, firstPageParam) => { + if (firstPageParam <= 1) { + return undefined + } + return firstPageParam - 1 + }, +})) +``` diff --git a/docs/zh-hant/framework/angular/guides/initial-query-data.md b/docs/zh-hant/framework/angular/guides/initial-query-data.md new file mode 100644 index 00000000000..7da19415441 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/initial-query-data.md @@ -0,0 +1,140 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:26:27.212Z' +id: initial-query-data +title: 初始查詢資料 +--- + +在需要之前,有許多方式可以為快取中的查詢提供初始資料: + +- 宣告式: + - 提供 `initialData` 給查詢,以便在快取為空時預先填入資料 +- 命令式: + - [使用 `queryClient.prefetchQuery` 預先取得資料](./prefetching.md) + - [使用 `queryClient.setQueryData` 手動將資料放入快取](./prefetching.md) + +## 使用 `initialData` 預先填入查詢 + +有時您可能已經在應用程式中擁有查詢的初始資料,可以直接提供給查詢。在這種情況下,您可以使用 `config.initialData` 選項來設定查詢的初始資料,並跳過初始載入狀態! + +> 重要提示:`initialData` 會被持久化到快取中,因此不建議在此選項中提供佔位、部分或不完整的資料,而應使用 `placeholderData` + +```ts +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, +})) +``` + +### `staleTime` 與 `initialDataUpdatedAt` + +預設情況下,`initialData` 會被視為完全新鮮的資料,就像剛取得一樣。這也會影響 `staleTime` 選項的解讀方式。 + +- 如果您使用 `initialData` 設定查詢觀察者,且沒有設定 `staleTime`(預設 `staleTime: 0`),查詢會立即重新取得資料: + +```ts +// 會立即顯示 initialTodos,但也會在元件或服務實例建立時立即重新取得 todos +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, +})) +``` + +- 如果您使用 `initialData` 和 `staleTime` 設為 `1000` 毫秒來設定查詢觀察者,資料將在相同時間內被視為新鮮的,就像剛從查詢函數取得一樣。 + +```ts +// 立即顯示 initialTodos,但在 1000 毫秒後遇到另一個互動事件之前不會重新取得 +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 1000, +})) +``` + +- 如果您的 `initialData` 不是完全新鮮的怎麼辦?這就引出了最後一種也是最準確的配置,使用名為 `initialDataUpdatedAt` 的選項。此選項允許您傳遞一個以毫秒為單位的 JS 時間戳記,表示 `initialData` 本身最後更新的時間,例如 `Date.now()` 提供的值。請注意,如果您有 unix 時間戳記,則需要將其乘以 `1000` 轉換為 JS 時間戳記。 + +```ts +// 立即顯示 initialTodos,但在 1 分鐘後遇到另一個互動事件之前不會重新取得 +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 60 * 1000, // 1 分鐘 + // 這可能是 10 秒前或 10 分鐘前 + initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052 +})) +``` + +此選項允許 `staleTime` 用於其原始目的,確定資料需要多新鮮,同時也允許在初始化時重新取得資料,如果 `initialData` 比 `staleTime` 舊的話。在上面的例子中,我們的資料需要在 1 分鐘內保持新鮮,並且我們可以提示查詢 `initialData` 最後更新的時間,以便查詢自行決定是否需要重新取得資料。 + +> 如果您更希望將資料視為**預先取得的資料**,建議您使用 `prefetchQuery` 或 `fetchQuery` API 預先填入快取,從而讓您可以獨立於 `initialData` 配置 `staleTime` + +### 初始資料函數 + +如果取得查詢初始資料的過程很耗資源,或者您不想在每個服務或元件實例上執行,您可以將一個函數作為 `initialData` 的值傳遞。此函數只會在查詢初始化時執行一次,節省寶貴的記憶體和/或 CPU: + +```ts +result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: () => getExpensiveTodos(), +})) +``` + +### 從快取取得初始資料 + +在某些情況下,您可以從另一個查詢的快取結果中提供查詢的初始資料。一個很好的例子是從 todos 列表查詢的快取資料中搜尋單個 todo 項目,然後將其用作單個 todo 查詢的初始資料: + +```ts +result = injectQuery(() => ({ + queryKey: ['todo', this.todoId()], + queryFn: () => fetch('/todos'), + initialData: () => { + // 使用 'todos' 查詢中的一個 todo 作為此 todo 查詢的初始資料 + return this.queryClient + .getQueryData(['todos']) + ?.find((d) => d.id === this.todoId()) + }, +})) +``` + +### 從快取取得初始資料並使用 `initialDataUpdatedAt` + +從快取取得初始資料意味著您用來查找初始資料的來源查詢可能已經過時。與其使用人工的 `staleTime` 來防止查詢立即重新取得資料,建議您將來源查詢的 `dataUpdatedAt` 傳遞給 `initialDataUpdatedAt`。這為查詢實例提供了所有需要的資訊,以確定是否需要以及何時需要重新取得資料,無論是否提供了初始資料。 + +```ts +result = injectQuery(() => ({ + queryKey: ['todos', this.todoId()], + queryFn: () => fetch(`/todos/${this.todoId()}`), + initialData: () => + queryClient.getQueryData(['todos'])?.find((d) => d.id === this.todoId()), + initialDataUpdatedAt: () => + queryClient.getQueryState(['todos'])?.dataUpdatedAt, +})) +``` + +### 從快取條件式取得初始資料 + +如果您用來查找初始資料的來源查詢已經過時,您可能根本不想使用快取資料,而是直接從伺服器取得。為了更容易做出這個決定,您可以使用 `queryClient.getQueryState` 方法來獲取有關來源查詢的更多資訊,包括 `state.dataUpdatedAt` 時間戳記,您可以用來判斷查詢是否足夠「新鮮」以滿足您的需求: + +```ts +result = injectQuery(() => ({ + queryKey: ['todo', this.todoId()], + queryFn: () => fetch(`/todos/${this.todoId()}`), + initialData: () => { + // 取得查詢狀態 + const state = queryClient.getQueryState(['todos']) + + // 如果查詢存在且資料不超過 10 秒... + if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) { + // 回傳單個 todo + return state.data.find((d) => d.id === this.todoId()) + } + + // 否則,回傳 undefined 並讓它從硬載入狀態取得資料! + }, +})) +``` diff --git a/docs/zh-hant/framework/angular/guides/invalidations-from-mutations.md b/docs/zh-hant/framework/angular/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..d85ed9653f8 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/invalidations-from-mutations.md @@ -0,0 +1,40 @@ +--- +source-updated-at: '2025-04-13T18:21:31.000Z' +translation-updated-at: '2025-05-08T20:25:35.319Z' +id: invalidations-from-mutations +title: 來自變更的失效 +--- + +使查詢失效只是成功的一半,知道**何時**讓它們失效才是另一半。通常當應用程式中的一個變異 (mutation) 成功時,很可能會有相關的查詢需要失效,甚至可能需要重新獲取資料以反映變異帶來的新變更。 + +舉例來說,假設我們有一個新增待辦事項的變異: + +```ts +mutation = injectMutation(() => ({ + mutationFn: postTodo, +})) +``` + +當 `postTodo` 變異成功時,我們很可能會希望所有 `todos` 查詢都失效,並可能重新獲取資料以顯示新的待辦事項。要做到這一點,你可以使用 `injectMutation` 的 `onSuccess` 選項和 `client` 的 `invalidateQueries` 函式: + +```ts +import { + injectMutation, + QueryClient, +} from '@tanstack/angular-query-experimental' + +export class TodosComponent { + queryClient = inject(QueryClient) + + // 當此變異成功時,使任何帶有 `todos` 或 `reminders` 查詢鍵 (query key) 的查詢失效 + mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: () => { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + this.queryClient.invalidateQueries({ queryKey: ['reminders'] }) + }, + })) +} +``` + +你可以利用 [`injectMutation` 函式](./mutations.md) 中提供的任何回調 (callback) 來設定失效的時機。 diff --git a/docs/zh-hant/framework/angular/guides/mutation-options.md b/docs/zh-hant/framework/angular/guides/mutation-options.md new file mode 100644 index 00000000000..116e548616c --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/mutation-options.md @@ -0,0 +1,25 @@ +--- +source-updated-at: '2025-04-13T18:21:31.000Z' +translation-updated-at: '2025-05-08T20:25:28.228Z' +id: query-options +title: 變更選項 +--- + +在多重使用情境間共享變更選項 (mutation options) 的最佳方式之一,就是使用 `mutationOptions` 輔助函式。在運行時,這個輔助函式僅會回傳你傳入的內容,但當[與 TypeScript 搭配使用](../../typescript#typing-query-options.md)時,它能帶來許多優勢。你可以在單一位置定義變更操作的所有可能選項,同時還能獲得完整的型別推論與型別安全檢查。 + +```ts +export class QueriesService { + private http = inject(HttpClient) + + updatePost(id: number) { + return mutationOptions({ + mutationFn: (post: Post) => Promise.resolve(post), + mutationKey: ['updatePost', id], + onSuccess: (newPost) => { + // ^? newPost: Post + this.queryClient.setQueryData(['posts', id], newPost) + }, + }) + } +} +``` diff --git a/docs/zh-hant/framework/angular/guides/mutations.md b/docs/zh-hant/framework/angular/guides/mutations.md new file mode 100644 index 00000000000..4d306f76e91 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/mutations.md @@ -0,0 +1,301 @@ +--- +source-updated-at: '2024-07-19T09:36:35.000Z' +translation-updated-at: '2025-05-08T20:26:56.640Z' +id: mutations +title: 變更 +--- + +與查詢 (queries) 不同,突變 (mutations) 通常用於建立/更新/刪除資料或執行伺服器副作用 (side-effects)。為此,TanStack Query 導出了 `injectMutation` 函式。 + +以下是一個將新待辦事項 (todo) 新增至伺服器的突變範例: + +```angular-ts +@Component({ + template: ` +
    + @if (mutation.isPending()) { + 新增待辦事項中... + } @else if (mutation.isError()) { +
    發生錯誤:{{ mutation.error()?.message }}
    + } @else if (mutation.isSuccess()) { +
    待辦事項已新增!
    + } + +
    + `, +}) +export class TodosComponent { + todoService = inject(TodoService) + mutation = injectMutation(() => ({ + mutationFn: (todoId: number) => + lastValueFrom(this.todoService.create(todoId)), + })) +} +``` + +在任何時刻,突變只能處於以下其中一種狀態: + +- `isIdle` 或 `status === 'idle'` - 突變目前處於閒置或全新/重置狀態 +- `isPending` 或 `status === 'pending'` - 突變正在執行中 +- `isError` 或 `status === 'error'` - 突變發生錯誤 +- `isSuccess` 或 `status === 'success'` - 突變成功且突變資料可用 + +除了這些主要狀態外,根據突變的狀態還可取得更多資訊: + +- `error` - 若突變處於 `error` 狀態,可透過 `error` 屬性取得錯誤資訊。 +- `data` - 若突變處於 `success` 狀態,可透過 `data` 屬性取得資料。 + +在上面的範例中,您也看到可以透過呼叫 `mutate` 函式並傳入**單一變數或物件**來將變數傳遞給突變函式。 + +即使只有變數,突變並不特別,但當與 `onSuccess` 選項、[Query Client 的 `invalidateQueries` 方法](../../../reference/QueryClient.md#queryclientinvalidatequeries) 和 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 一起使用時,突變就成為非常強大的工具。 + +## 重置突變狀態 + +有時您需要清除突變請求的 `error` 或 `data`。為此,您可以使用 `reset` 函式來處理: + +```angular-ts +@Component({ + standalone: true, + selector: 'todo-item', + imports: [ReactiveFormsModule], + template: ` +
    + @if (mutation.error()) { +
    {{ mutation.error() }}
    + } + +
    + +
    + `, +}) +export class TodosComponent { + mutation = injectMutation(() => ({ + mutationFn: createTodo, + })) + + fb = inject(NonNullableFormBuilder) + + todoForm = this.fb.group({ + title: this.fb.control('', { + validators: [Validators.required], + }), + }) + + title = toSignal(this.todoForm.controls.title.valueChanges, { + initialValue: '', + }) + + onCreateTodo = () => { + this.mutation.mutate(this.title()) + } +} +``` + +## 突變副作用 + +`injectMutation` 提供了一些輔助選項,允許在突變生命週期的任何階段快速簡便地執行副作用。這些選項對於[突變後使查詢失效並重新獲取](./invalidations-from-mutations.md)甚至[樂觀更新 (optimistic updates)](./optimistic-updates.md)非常有用。 + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + onMutate: (variables) => { + // 突變即將發生! + + // 可選擇返回包含資料的上下文,用於例如回滾 + return { id: 1 } + }, + onError: (error, variables, context) => { + // 發生錯誤! + console.log(`回滾樂觀更新,id ${context.id}`) + }, + onSuccess: (data, variables, context) => { + // 成功! + }, + onSettled: (data, error, variables, context) => { + // 無論錯誤或成功! + }, +})) +``` + +當在任何回調函式中返回 Promise 時,它將首先被等待,然後才會呼叫下一個回調: + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: async () => { + console.log('我先執行!') + }, + onSettled: async () => { + console.log('我第二個執行!') + }, +})) +``` + +您可能會發現,在呼叫 `mutate` 時,除了在 `injectMutation` 上定義的回調外,還想**觸發其他回調**。這可以用來觸發特定於元件的副作用。為此,您可以在突變變數之後向 `mutate` 函式提供任何相同的回調選項。支援的選項包括:`onSuccess`、`onError` 和 `onSettled`。請注意,如果您的元件在突變完成*之前*被銷毀,這些額外的回調將不會執行。 + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 我會先執行 + }, + onError: (error, variables, context) => { + // 我會先執行 + }, + onSettled: (data, error, variables, context) => { + // 我會先執行 + }, +})) + +mutation.mutate(todo, { + onSuccess: (data, variables, context) => { + // 我會第二個執行! + }, + onError: (error, variables, context) => { + // 我會第二個執行! + }, + onSettled: (data, error, variables, context) => { + // 我會第二個執行! + }, +}) +``` + +### 連續突變 + +在處理連續突變時,`onSuccess`、`onError` 和 `onSettled` 回調的處理方式略有不同。當傳遞給 `mutate` 函式時,它們只會觸發*一次*,並且只有在元件仍處於活動狀態時才會觸發。這是因為每次呼叫 `mutate` 函式時,突變觀察器 (observer) 都會被移除並重新訂閱。相反,`injectMutation` 處理程序會針對每個 `mutate` 呼叫執行。 + +> 請注意,傳遞給 `injectMutation` 的 `mutationFn` 很可能是非同步的。在這種情況下,突變完成的順序可能與 `mutate` 函式呼叫的順序不同。 + +```ts +export class Example { + mutation = injectMutation(() => ({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 會被呼叫 3 次 + }, + })) + + doMutations() { + ;['Todo 1', 'Todo 2', 'Todo 3'].forEach((todo) => { + this.mutation.mutate(todo, { + onSuccess: (data, variables, context) => { + // 只會執行一次,針對最後一個突變 (Todo 3), + // 無論哪個突變先完成 + }, + }) + }) + } +} +``` + +## Promise + +使用 `mutateAsync` 而不是 `mutate` 來獲取一個 Promise,該 Promise 將在成功時解析或在錯誤時拋出。例如,這可以用於組合副作用。 + +```ts +mutation = injectMutation(() => ({ mutationFn: addTodo })) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('完成') +} +``` + +## 重試 + +預設情況下,TanStack Query 不會在錯誤時重試突變,但可以透過 `retry` 選項實現: + +```ts +mutation = injectMutation(() => ({ + mutationFn: addTodo, + retry: 3, +})) +``` + +如果突變因設備離線而失敗,它們將在設備重新連接時以相同的順序重試。 + +## 持久化突變 + +如果需要,可以將突變持久化到儲存中,並在稍後恢復。這可以透過水合 (hydration) 函式來完成: + +```ts +const queryClient = new QueryClient() + +// 定義 "addTodo" 突變 +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables) => { + // 取消目前的待辦事項列表查詢 + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 建立樂觀待辦事項 + const optimisticTodo = { id: uuid(), title: variables.title } + + // 將樂觀待辦事項新增到待辦事項列表 + queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // 返回包含樂觀待辦事項的上下文 + return { optimisticTodo } + }, + onSuccess: (result, variables, context) => { + // 用結果替換待辦事項列表中的樂觀待辦事項 + queryClient.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === context.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, context) => { + // 從待辦事項列表中移除樂觀待辦事項 + queryClient.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== context.optimisticTodo.id), + ) + }, + retry: 3, +}) + +class someComponent { + // 在某個元件中啟動突變: + mutation = injectMutation(() => ({ mutationKey: ['addTodo'] })) + + someMethod() { + mutation.mutate({ title: 'title' }) + } +} + +// 如果突變因設備離線等原因被暫停, +// 則可以在應用程式退出時將暫停的突變脫水 (dehydrate): +const state = dehydrate(queryClient) + +// 然後可以在應用程式啟動時再次水合 (hydrate) 突變: +hydrate(queryClient, state) + +// 恢復暫停的突變: +queryClient.resumePausedMutations() +``` + +### 持久化離線突變 + +如果您使用 [persistQueryClient 插件](../plugins/persistQueryClient.md) 持久化離線突變,除非您提供預設的突變函式,否則在重新載入頁面時無法恢復突變。 + +這是一個技術限制。當持久化到外部儲存時,只有突變的狀態會被持久化,因為函式無法被序列化。水合後,觸發突變的元件可能尚未初始化,因此呼叫 `resumePausedMutations` 可能會產生錯誤:`找不到 mutationFn`。 + +我們還有一個全面的[離線範例](../examples/offline),涵蓋了查詢和突變。 + +## 突變範圍 + +預設情況下,所有突變都是並行運行的 - 即使您多次呼叫同一突變的 `.mutate()`。可以透過為突變指定帶有 `id` 的 `scope` 來避免這種情況。所有具有相同 `scope.id` 的突變將按順序運行,這意味著當它們被觸發時,如果該範圍已經有一個突變正在進行中,它們將以 `isPaused: true` 狀態開始。它們將被放入佇列中,並在輪到它們時自動恢復。 + +```tsx +const mutation = injectMutation({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +}) +``` diff --git a/docs/zh-hant/framework/angular/guides/network-mode.md b/docs/zh-hant/framework/angular/guides/network-mode.md new file mode 100644 index 00000000000..b4ff7934efe --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/network-mode.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:09.530Z' +id: network-mode +title: Network Mode +ref: docs/zh-hant/framework/react/guides/network-mode.md +--- diff --git a/docs/zh-hant/framework/angular/guides/optimistic-updates.md b/docs/zh-hant/framework/angular/guides/optimistic-updates.md new file mode 100644 index 00000000000..449efb1da17 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/optimistic-updates.md @@ -0,0 +1,175 @@ +--- +source-updated-at: '2024-11-19T18:32:49.000Z' +translation-updated-at: '2025-05-08T20:26:13.741Z' +id: optimistic-updates +title: 樂觀更新 +--- + +Angular Query 提供了兩種方式,讓你在突變 (mutation) 完成前樂觀地更新 UI。你可以使用 `onMutate` 選項直接更新快取 (cache),或是利用 `injectMutation` 結果返回的 `variables` 來更新 UI。 + +## 透過 UI 更新 + +這是較簡單的方式,因為它不直接與快取互動。 + +```ts +addTodo = injectMutation(() => ({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + // 確保 _返回_ 查詢失效的 Promise + // 這樣突變會保持在 `pending` 狀態,直到重新擷取完成 + onSettled: async () => { + return await queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, +})) +``` + +接著你可以存取 `addTodo.variables`,其中包含新增的待辦事項。在渲染查詢的 UI 清單中,你可以在突變 `isPending` 時將另一個項目附加到清單: + +```angular-ts +@Component({ + template: ` + @for (todo of todos.data(); track todo.id) { +
  • {{ todo.title }}
  • + } + @if (addTodo.isPending()) { +
  • {{ addTodo.variables() }}
  • + } + `, +}) +class TodosComponent {} +``` + +我們會在突變處於 pending 狀態時渲染一個帶有不同 `opacity` 的臨時項目。一旦完成,該項目將自動不再渲染。假設重新擷取成功,我們應該會在清單中看到該項目顯示為「正常項目」。 + +如果突變發生錯誤,該項目也會消失。但如果需要,我們可以透過檢查突變的 `isError` 狀態繼續顯示它。`variables` 在突變錯誤時 _不會_ 被清除,因此我們仍可以存取它們,甚至顯示重試按鈕: + +```angular-ts +@Component({ + template: ` + @if (addTodo.isError()) { +
  • + {{ addTodo.variables() }} + +
  • + } + `, +}) +class TodosComponent {} +``` + +### 如果突變與查詢不在同一個元件中 + +這種方式在突變與查詢位於同一元件時效果很好。不過,你也可以透過專用的 `injectMutationState` 函式在其他元件中存取所有突變。最好與 `mutationKey` 搭配使用: + +```ts +// 在應用程式的某處 +addTodo = injectMutation(() => ({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + mutationKey: ['addTodo'], +})) + +// 在其他地方存取 variables + +mutationState = injectMutationState(() => ({ + filters: { mutationKey: ['addTodo'], status: 'pending' }, + select: (mutation) => mutation.state.variables, +})) +``` + +`variables` 會是一個 `Array`,因為可能同時有多個突變正在執行。如果我們需要項目的唯一鍵,也可以選擇 `mutation.state.submittedAt`。這會讓顯示並發的樂觀更新變得更加容易。 + +## 透過快取更新 + +當你在執行突變前樂觀地更新狀態時,突變有可能會失敗。在大多數失敗情況下,你可以直接觸發樂觀查詢的重新擷取,將其恢復為伺服器的真實狀態。但在某些情況下,重新擷取可能無法正確運作,且突變錯誤可能代表某種伺服器問題,導致無法重新擷取。此時,你可以選擇回滾更新。 + +為此,`injectMutation` 的 `onMutate` 處理器選項允許你返回一個值,該值稍後會作為最後一個參數傳遞給 `onError` 和 `onSettled` 處理器。在多數情況下,傳遞一個回滾函式最為實用。 + +### 在新增待辦事項時更新待辦事項清單 + +```ts +queryClient = inject(QueryClient) + +updateTodo = injectMutation(() => ({ + mutationFn: updateTodo, + // 當 mutate 被呼叫時: + onMutate: async (newTodo) => { + // 取消任何正在進行的重新擷取 + // (避免覆蓋我們的樂觀更新) + await this.queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 快照先前的值 + const previousTodos = client.getQueryData(['todos']) + + // 樂觀地更新為新值 + this.queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) + + // 返回一個包含快照值的上下文物件 + return { previousTodos } + }, + // 如果突變失敗, + // 使用 onMutate 返回的上下文進行回滾 + onError: (err, newTodo, context) => { + client.setQueryData(['todos'], context.previousTodos) + }, + // 無論錯誤或成功後都重新擷取: + onSettled: () => { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, +})) +``` + +### 更新單個待辦事項 + +```ts +queryClient = inject(QueryClient) + +updateTodo = injectMutation(() => ({ + mutationFn: updateTodo, + // 當 mutate 被呼叫時: + onMutate: async (newTodo) => { + // 取消任何正在進行的重新擷取 + // (避免覆蓋我們的樂觀更新) + await this.queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] }) + + // 快照先前的值 + const previousTodo = this.queryClient.getQueryData(['todos', newTodo.id]) + + // 樂觀地更新為新值 + this.queryClient.setQueryData(['todos', newTodo.id], newTodo) + + // 返回包含先前與新待辦事項的上下文 + return { previousTodo, newTodo } + }, + // 如果突變失敗,使用上面返回的上下文 + onError: (err, newTodo, context) => { + this.queryClient.setQueryData( + ['todos', context.newTodo.id], + context.previousTodo, + ) + }, + // 無論錯誤或成功後都重新擷取: + onSettled: (newTodo) => { + this.queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] }) + }, +})) +``` + +你也可以使用 `onSettled` 函式來取代獨立的 `onError` 和 `onSuccess` 處理器: + +```ts +injectMutation({ + mutationFn: updateTodo, + // ... + onSettled: (newTodo, error, variables, context) => { + if (error) { + // 執行某些操作 + } + }, +}) +``` + +## 何時使用哪種方式 + +如果只有一個地方需要顯示樂觀結果,使用 `variables` 並直接更新 UI 是程式碼較少且通常更容易理解的方式。例如,你完全不需要處理回滾。 + +然而,如果畫面上有多個地方需要知道更新,直接操作快取會自動為你處理這一點。 diff --git a/docs/zh-hant/framework/angular/guides/paginated-queries.md b/docs/zh-hant/framework/angular/guides/paginated-queries.md new file mode 100644 index 00000000000..6c4c4553430 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/paginated-queries.md @@ -0,0 +1,107 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-08T20:25:51.906Z' +id: paginated-queries +title: 分頁查詢 +--- + +在 UI 中渲染分頁資料是非常常見的模式,而在 TanStack Query 中,只需將頁面資訊包含在查詢鍵 (query key) 中即可「直接運作」: + +```ts +const result = injectQuery(() => ({ + queryKey: ['projects', page()], + queryFn: fetchProjects, +})) +``` + +然而,當你運行這個簡單範例時,可能會注意到一個奇怪的現象: + +**UI 會在 `success` 和 `pending` 狀態之間跳動,因為每個新頁面都被視為全新的查詢。** + +這種體驗並不理想,但不幸的是,這也是目前許多工具堅持的工作方式。但 TanStack Query 不同!如你所料,TanStack Query 提供了一個名為 `placeholderData` 的強大功能來解決這個問題。 + +## 使用 `placeholderData` 實現更好的分頁查詢 + +考慮以下範例,我們理想情況下希望增加查詢的頁面索引 (pageIndex) 或游標 (cursor)。如果使用 `injectQuery`,**技術上仍能正常運作**,但當為每個頁面或游標創建和銷毀不同查詢時,UI 仍會在 `success` 和 `pending` 狀態之間跳動。通過將 `placeholderData` 設為 `(previousData) => previousData` 或使用 TanStack Query 導出的 `keepPreviousData` 函數,我們可以獲得以下新特性: + +- **即使查詢鍵 (query key) 已更改,上次成功獲取的資料仍可在請求新資料時使用**。 +- 當新資料到達時,舊的 `data` 會無縫切換以顯示新資料。 +- 可透過 `isPlaceholderData` 判斷查詢當前提供的資料類型 + +```angular-ts +@Component({ + selector: 'pagination-example', + template: ` +
    +

    + 在此範例中,當獲取下一頁資料時,當前頁面的資料仍保持可見。按鈕和前往下一頁的功能也會在下一頁游標未知時被禁用。每一頁都會像普通查詢一樣被緩存,因此當返回上一頁時,你會立即看到它們,同時它們也會在背景無形中被重新獲取。 +

    + @if (query.status() === 'pending') { +
    載入中...
    + } @else if (query.status() === 'error') { +
    錯誤: {{ query.error().message }}
    + } @else { + + +
    + @for (project of query.data().projects; track project.id) { +

    {{ project.name }}

    + } +
    + } + +
    當前頁面: {{ page() + 1 }}
    + + + + + + @if (query.isFetching()) { + 載入中... + } +
    + `, +}) +export class PaginationExampleComponent { + page = signal(0) + queryClient = inject(QueryClient) + + query = injectQuery(() => ({ + queryKey: ['projects', this.page()], + queryFn: () => lastValueFrom(fetchProjects(this.page())), + placeholderData: keepPreviousData, + staleTime: 5000, + })) + + constructor() { + effect(() => { + // 預先獲取下一頁! + if (!this.query.isPlaceholderData() && this.query.data()?.hasMore) { + this.#queryClient.prefetchQuery({ + queryKey: ['projects', this.page() + 1], + queryFn: () => lastValueFrom(fetchProjects(this.page() + 1)), + }) + } + }) + } + + previousPage() { + this.page.update((old) => Math.max(old - 1, 0)) + } + + nextPage() { + this.page.update((old) => (this.query.data()?.hasMore ? old + 1 : old)) + } +} +``` + +## 使用 `placeholderData` 實現延遲無限查詢結果 + +雖然不那麼常見,但 `placeholderData` 選項也能與 `injectInfiniteQuery` 函數完美配合,讓你能在無限查詢鍵隨時間變化的同時,無縫地讓使用者繼續查看緩存的資料。 diff --git a/docs/zh-hant/framework/angular/guides/parallel-queries.md b/docs/zh-hant/framework/angular/guides/parallel-queries.md new file mode 100644 index 00000000000..587f7cb8ec6 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/parallel-queries.md @@ -0,0 +1,46 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:18.361Z' +id: parallel-queries +title: 平行查詢 +--- + +「平行 (Parallel)」查詢是指同時執行多個查詢,以最大化資料獲取的並發性。 + +## 手動平行查詢 + +當平行查詢的數量固定時,使用平行查詢**無需額外處理**。只需並列使用多個 TanStack Query 的 `injectQuery` 和 `injectInfiniteQuery` 函式即可! + +```ts +export class AppComponent { + // 以下查詢將會平行執行 + usersQuery = injectQuery(() => ({ queryKey: ['users'], queryFn: fetchUsers })) + teamsQuery = injectQuery(() => ({ queryKey: ['teams'], queryFn: fetchTeams })) + projectsQuery = injectQuery(() => ({ + queryKey: ['projects'], + queryFn: fetchProjects, + })) +} +``` + +## 使用 `injectQueries` 動態平行查詢 + +TanStack Query 提供 `injectQueries` 函式,可讓你動態執行任意數量的平行查詢。 + +`injectQueries` 接受一個**選項物件**,其中包含一個 **queries 鍵**,其值為**查詢物件的陣列**。它會回傳一個**查詢結果的陣列**: + +```ts +export class AppComponent { + users = signal>([]) + + // 請注意 injectQueries 仍在開發中,此程式碼目前無法運作 + userQueries = injectQueries(() => ({ + queries: users().map((user) => { + return { + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + } + }), + })) +} +``` diff --git a/docs/zh-hant/framework/angular/guides/placeholder-query-data.md b/docs/zh-hant/framework/angular/guides/placeholder-query-data.md new file mode 100644 index 00000000000..2d0f6ca8652 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/placeholder-query-data.md @@ -0,0 +1,75 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-08T20:25:30.490Z' +id: placeholder-query-data +title: 佔位查詢資料 +--- + +## 什麼是預留位置資料 (placeholder data)? + +預留位置資料允許查詢 (query) 表現得像已經擁有資料一樣,類似於 `initialData` 選項,但**這些資料不會被持久化到快取 (cache)**。這在以下情況特別有用:當你擁有足夠的部分(或模擬)資料可以成功渲染查詢結果,同時在背景中獲取實際資料。 + +> 範例:單篇部落格文章的查詢可以從父層的部落格文章列表中提取「預覽」資料,這些預覽資料僅包含標題和文章內容的一小段摘要。雖然你不會想將這些部分資料持久化到單一查詢的結果中,但它對於盡快顯示內容佈局非常有用,同時實際查詢會繼續獲取完整的物件。 + +有幾種方法可以在需要之前為查詢提供預留位置資料到快取中: + +- 宣告式 (Declaratively): + - 提供 `placeholderData` 給查詢,以便在快取為空時預先填充 +- 命令式 (Imperatively): + - [使用 `queryClient` 和 `placeholderData` 選項預取 (prefetch) 或獲取資料](./prefetching.md) + +當我們使用 `placeholderData` 時,查詢不會處於 `pending` 狀態——它會從 `success` 狀態開始,因為我們有 `data` 可以顯示,即使這些資料只是「預留位置」資料。為了區分它與「真實」資料,我們還會在查詢結果中將 `isPlaceholderData` 標記設為 `true`。 + +## 預留位置資料作為值 + +```ts +class TodosComponent { + result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData: placeholderTodos, + })) +} +``` + +## 預留位置資料作為函式 + +`placeholderData` 也可以是一個函式,讓你可以存取「先前」成功查詢的資料和查詢元資訊 (meta information)。這在以下情況非常有用:當你想使用一個查詢的資料作為另一個查詢的預留位置資料時。當查詢鍵 (QueryKey) 變更時(例如從 `['todos', 1]` 變為 `['todos', 2]`),我們可以繼續顯示「舊」資料,而不必在資料從一個查詢過渡到下一個查詢時顯示載入指示器 (loading spinner)。更多資訊請參見[分頁查詢 (Paginated Queries)](./paginated-queries.md)。 + +```ts +class TodosComponent { + result = injectQuery(() => ({ + queryKey: ['todos', id()], + queryFn: () => fetch(`/todos/${id}`), + placeholderData: (previousData, previousQuery) => previousData, + })) +} +``` + +### 從快取中獲取預留位置資料 + +在某些情況下,你可以從另一個查詢的快取結果中為當前查詢提供預留位置資料。一個很好的例子是:從部落格文章列表查詢的快取資料中搜尋文章的預覽版本,然後將其用作單篇文章查詢的預留位置資料: + +```ts +export class BlogPostComponent { + // Until Angular supports signal-based inputs, we have to set a signal + @Input({ required: true, alias: 'postId' }) + set _postId(value: number) { + this.postId.set(value) + } + postId = signal(0) + queryClient = inject(QueryClient) + + result = injectQuery(() => ({ + queryKey: ['blogPost', this.postId()], + queryFn: () => fetch(`/blogPosts/${this.postId()}`), + placeholderData: () => { + // Use the smaller/preview version of the blogPost from the 'blogPosts' + // query as the placeholder data for this blogPost query + return queryClient + .getQueryData(['blogPosts']) + ?.find((d) => d.id === this.postId()) + }, + })) +} +``` diff --git a/docs/zh-hant/framework/angular/guides/queries.md b/docs/zh-hant/framework/angular/guides/queries.md new file mode 100644 index 00000000000..cbb9144f64e --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/queries.md @@ -0,0 +1,126 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-08T20:25:38.940Z' +id: queries +title: 查詢 +--- + +## 查詢基礎 + +查詢是基於**唯一鍵**與非同步資料來源綁定的宣告式依賴關係。查詢可與任何基於 Promise 的方法(包括 GET 和 POST 方法)一起使用,從伺服器獲取資料。若您的方法會修改伺服器上的資料,建議改用[變更 (Mutations)](./mutations.md)。 + +要在元件或服務中訂閱查詢,請調用 `injectQuery` 並至少提供以下參數: + +- **查詢的唯一鍵** +- 一個回傳 Promise 或 Observable 的函式,該函式應: + - 解析資料,或 + - 拋出錯誤 + +```ts +import { injectQuery } from '@tanstack/angular-query-experimental' + +export class TodosComponent { + info = injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodoList })) +} +``` + +您提供的**唯一鍵**會在內部用於重新獲取、快取及在應用程式中共享查詢。 + +`injectQuery` 回傳的查詢結果包含所有與查詢相關的資訊,可供模板渲染或其他資料使用: + +```ts +result = injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodoList })) +``` + +`result` 物件包含幾個非常重要的狀態,您需要了解這些狀態才能有效使用。查詢在任何時刻只能處於以下其中一種狀態: + +- `isPending` 或 `status === 'pending'` - 查詢尚未取得資料 +- `isError` 或 `status === 'error'` - 查詢遇到錯誤 +- `isSuccess` 或 `status === 'success'` - 查詢成功且資料可用 + +除了這些主要狀態外,根據查詢的狀態還可獲取更多資訊: + +- `error` - 若查詢處於 `isError` 狀態,可透過 `error` 屬性取得錯誤資訊。 +- `data` - 若查詢處於 `isSuccess` 狀態,可透過 `data` 屬性取得資料。 +- `isFetching` - 在任何狀態下,若查詢正在獲取資料(包括背景重新獲取),`isFetching` 會為 `true`。 + +對於**大多數**查詢,通常只需先檢查 `isPending` 狀態,再檢查 `isError` 狀態,最後即可假設資料已可用並渲染成功狀態: + +```angular-ts +@Component({ + selector: 'todos', + standalone: true, + template: ` + @if (todos.isPending()) { + Loading... + } @else if (todos.isError()) { + Error: {{ todos.error()?.message }} + } @else { + + @for (todo of todos.data(); track todo.id) { +
  • {{ todo.title }}
  • + } @empty { +
  • No todos found
  • + } + } + `, +}) +export class PostsComponent { + todos = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + })) +} +``` + +若不喜歡使用布林值,也可以直接使用 `status` 狀態: + +```angular-ts +@Component({ + selector: 'todos', + standalone: true, + template: ` + @switch (todos.status()) { + @case ('pending') { + Loading... + } + @case ('error') { + Error: {{ todos.error()?.message }} + } + + @default { +
      + @for (todo of todos.data(); track todo.id) { +
    • {{ todo.title }}
    • + } @empty { +
    • No todos found
    • + } +
    + } + } + `, +}) +class TodosComponent {} +``` + +若您在存取 `data` 前已檢查過 `pending` 和 `error`,TypeScript 也會正確縮小 `data` 的型別範圍。 + +### 獲取狀態 (FetchStatus) + +除了 `status` 欄位外,您還會獲得一個額外的 `fetchStatus` 屬性,其選項如下: + +- `fetchStatus === 'fetching'` - 查詢正在獲取資料。 +- `fetchStatus === 'paused'` - 查詢想要獲取資料,但被暫停。詳情請參閱[網路模式 (Network Mode)](./network-mode.md)指南。 +- `fetchStatus === 'idle'` - 查詢目前未進行任何操作。 + +### 為何需要兩種不同狀態? + +背景重新獲取與過期資料重新驗證邏輯會導致 `status` 和 `fetchStatus` 的所有組合都可能出現。例如: + +- 處於 `success` 狀態的查詢通常會處於 `idle` 獲取狀態,但若正在進行背景重新獲取,也可能處於 `fetching` 狀態。 +- 剛掛載且無資料的查詢通常會處於 `pending` 狀態和 `fetching` 獲取狀態,但若無網路連接,也可能處於 `paused` 狀態。 + +因此請記住,查詢可能處於 `pending` 狀態但實際上並未獲取資料。簡單來說: + +- `status` 提供關於 `data` 的資訊:我們是否有資料? +- `fetchStatus` 提供關於 `queryFn` 的資訊:它是否正在執行? diff --git a/docs/zh-hant/framework/angular/guides/query-cancellation.md b/docs/zh-hant/framework/angular/guides/query-cancellation.md new file mode 100644 index 00000000000..996e9aba3d7 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/query-cancellation.md @@ -0,0 +1,108 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-08T20:25:16.314Z' +id: query-cancellation +title: 查詢取消 +--- + +TanStack Query 為每個查詢函式提供了一個 [`AbortSignal` 實例](https://developer.mozilla.org/docs/Web/API/AbortSignal)。當查詢過時或變為非活動狀態時,此 `signal` 將被中止。這意味著所有查詢都是可取消的,並且您可以根據需要在查詢函式中響應取消操作。最棒的是,這讓您可以繼續使用普通的 async/await 語法,同時獲得自動取消的所有好處。 + +## 預設行為 + +預設情況下,在 Promise 解析之前卸載或變為未使用的查詢 _不會_ 被取消。這意味著在 Promise 解析後,結果資料將保留在快取中。如果您已經開始接收查詢,但在查詢完成前卸載了元件,這會很有幫助。如果您再次掛載元件且查詢尚未被垃圾回收,資料仍然可用。 + +然而,如果您使用了 `AbortSignal`,Promise 將被取消(例如中止 fetch 請求),因此查詢也必須被取消。取消查詢將導致其狀態 _恢復_ 到先前的狀態。 + +## 使用 `HttpClient` + +```ts +import { HttpClient } from '@angular/common/http' +import { injectQuery } from '@tanstack/angular-query-experimental' + +postQuery = injectQuery(() => ({ + enabled: this.postId() > 0, + queryKey: ['post', this.postId()], + queryFn: async (context): Promise => { + const abort$ = fromEvent(context.signal, 'abort') + return lastValueFrom(this.getPost$(this.postId()).pipe(takeUntil(abort$))) + }, +})) +``` + +## 使用 `fetch` + +[//]: # 'Example2' + +```ts +query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const todosResponse = await fetch('/todos', { + // 將 signal 傳遞給 fetch + signal, + }) + const todos = await todosResponse.json() + + const todoDetails = todos.map(async ({ details }) => { + const response = await fetch(details, { + // 或傳遞給多個請求 + signal, + }) + return response.json() + }) + + return Promise.all(todoDetails) + }, +})) +``` + +[//]: # 'Example2' + +## 使用 `axios` + +[//]: # 'Example3' + +```tsx +import axios from 'axios' + +const query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => + axios.get('/todos', { + // 將 signal 傳遞給 `axios` + signal, + }), +})) +``` + +[//]: # 'Example3' + +## 手動取消 + +您可能需要手動取消查詢。例如,如果請求需要很長時間才能完成,您可以允許使用者點擊取消按鈕來停止請求。為此,您只需呼叫 `queryClient.cancelQueries({ queryKey })`,這將取消查詢並將其恢復到先前的狀態。如果您使用了傳遞給查詢函式的 `signal`,TanStack Query 還會額外取消 Promise。 + +[//]: # 'Example7' + +```angular-ts +@Component({ + standalone: true, + template: ``, +}) +export class TodosComponent { + query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, + })) + + queryClient = inject(QueryClient) + + onCancel() { + this.queryClient.cancelQueries(['todos']) + } +} +``` + +[//]: # 'Example7' diff --git a/docs/zh-hant/framework/angular/guides/query-functions.md b/docs/zh-hant/framework/angular/guides/query-functions.md new file mode 100644 index 00000000000..b6ee61b7ffa --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/query-functions.md @@ -0,0 +1,101 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:19.553Z' +id: query-functions +title: 查詢函數 +--- + +查詢函式 (query function) 可以是**任何會回傳 promise 的函式**。這個 promise 應該要能夠**解析資料 (resolve the data)** 或**拋出錯誤 (throw an error)**。 + +以下都是有效的查詢函式設定方式: + +```ts +injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchAllTodos })) +injectQuery(() => ({ queryKey: ['todos', todoId], queryFn: () => fetchTodoById(todoId) }) +injectQuery(() => ({ + queryKey: ['todos', todoId], + queryFn: async () => { + const data = await fetchTodoById(todoId) + return data + }, +})) +injectQuery(() => ({ + queryKey: ['todos', todoId], + queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]), +})) +``` + +## 處理與拋出錯誤 + +為了讓 TanStack Query 判斷查詢是否發生錯誤,查詢函式**必須拋出錯誤**或回傳一個**被拒絕的 Promise (rejected Promise)**。任何在查詢函式中拋出的錯誤都會被保存在查詢的 `error` 狀態中。 + +```ts +todos = injectQuery(() => ({ + queryKey: ['todos', todoId()], + queryFn: async () => { + if (somethingGoesWrong) { + throw new Error('Oh no!') + } + if (somethingElseGoesWrong) { + return Promise.reject(new Error('Oh no!')) + } + + return data + }, +})) +``` + +## 與預設不會拋出錯誤的 `fetch` 或其他客戶端一起使用 + +雖然大多數工具如 `axios` 或 `graphql-request` 會自動為不成功的 HTTP 呼叫拋出錯誤,但像 `fetch` 這樣的工具預設不會拋出錯誤。如果是這種情況,你需要自行拋出錯誤。以下是使用常見的 `fetch` API 來實現的簡單方法: + +```ts +todos = injectQuery(() => ({ + queryKey: ['todos', todoId()], + queryFn: async () => { + const response = await fetch('/todos/' + todoId) + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() + }, +})) +``` + +## 查詢函式變數 + +查詢鍵 (query key) 不僅用於唯一標識你正在獲取的資料,還會作為 QueryFunctionContext 的一部分方便地傳入你的查詢函式中。雖然這並非總是必要,但這使得在需要時提取查詢函式成為可能: + +```ts +result = injectQuery(() => ({ + queryKey: ['todos', { status: status(), page: page() }], + queryFn: fetchTodoList, +})) + +// 在查詢函式中存取 key、status 和 page 變數! +function fetchTodoList({ queryKey }) { + const [_key, { status, page }] = queryKey + return new Promise() +} +``` + +### QueryFunctionContext + +`QueryFunctionContext` 是傳遞給每個查詢函式的物件,它包含以下內容: + +- `queryKey: QueryKey`: [查詢鍵 (Query Keys)](./query-keys.md) +- `client: QueryClient`: [QueryClient](../../../reference/QueryClient.md) +- `signal?: AbortSignal` + - 由 TanStack Query 提供的 [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) 實例 + - 可用於 [查詢取消 (Query Cancellation)](./query-cancellation.md) +- `meta: Record | undefined` + - 一個可選欄位,你可以填入與查詢相關的額外資訊 + +此外,[無限查詢 (Infinite Queries)](./infinite-queries.md) 還會獲得以下傳遞的選項: + +- `pageParam: TPageParam` + - 用於獲取當前頁面的頁面參數 +- `direction: 'forward' | 'backward'` + - **已棄用** + - 當前頁面獲取的方向 + - 要獲取當前頁面獲取的方向,請從 `getNextPageParam` 和 `getPreviousPageParam` 中將方向添加到 `pageParam`。 diff --git a/docs/zh-hant/framework/angular/guides/query-invalidation.md b/docs/zh-hant/framework/angular/guides/query-invalidation.md new file mode 100644 index 00000000000..a3d7257f770 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/query-invalidation.md @@ -0,0 +1,118 @@ +--- +source-updated-at: '2024-11-14T21:48:46.000Z' +translation-updated-at: '2025-05-08T20:25:28.513Z' +id: query-invalidation +title: 查詢失效 +--- + +等待查詢變為過時 (stale) 後再重新獲取資料,這種方式並不總是有效,特別是當您確知由於使用者操作導致某個查詢的資料已經過期時。為此,`QueryClient` 提供了一個 `invalidateQueries` 方法,讓您能智能地標記查詢為過時狀態,並可能觸發重新獲取! + +```tsx +// 使快取中的所有查詢失效 +queryClient.invalidateQueries() +// 使所有以 `todos` 開頭的查詢鍵的查詢失效 +queryClient.invalidateQueries({ queryKey: ['todos'] }) +``` + +> 注意:其他使用正規化快取 (normalized caches) 的函式庫可能會嘗試透過命令式或模式推斷來更新本地查詢的新資料,而 TanStack Query 則提供工具讓您避免維護正規化快取的手動操作,轉而採用**精確的失效標記、背景重新獲取及最終的原子更新**。 + +當使用 `invalidateQueries` 使查詢失效時,會發生兩件事: + +- 該查詢被標記為過時 (stale)。此過時狀態會覆蓋 `injectQuery` 或相關函式中使用的任何 `staleTime` 設定 +- 如果該查詢目前正透過 `injectQuery` 或相關函式渲染,它也會在背景重新獲取資料 + +## 使用 `invalidateQueries` 進行查詢匹配 + +在使用如 `invalidateQueries` 和 `removeQueries` 等 API(以及其他支援部分查詢匹配的功能)時,您可以透過查詢鍵的前綴來匹配多個查詢,或者非常精確地匹配一個特定查詢。有關可使用的篩選器類型,請參閱[查詢篩選器](./filters.md#query-filters)。 + +在此範例中,我們可以使用 `todos` 前綴來使任何查詢鍵以 `todos` 開頭的查詢失效: + +```ts +import { injectQuery, QueryClient } from '@tanstack/angular-query-experimental' + +class QueryInvalidationExample { + queryClient = inject(QueryClient) + + invalidateQueries() { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + } + + // 以下兩個查詢都將失效 + todoListQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + })) + todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { page: 1 }], + queryFn: fetchTodoList, + })) +} +``` + +您甚至可以透過傳遞更明確的查詢鍵給 `invalidateQueries` 方法,來使帶有特定變數的查詢失效: + +```ts +queryClient.invalidateQueries({ + queryKey: ['todos', { type: 'done' }], +}) + +// 以下查詢將失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +})) + +// 但以下查詢不會失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, +})) +``` + +`invalidateQueries` API 非常靈活,因此即使您只想使**不帶任何其他變數或子鍵**的 `todos` 查詢失效,也可以傳遞 `exact: true` 選項給 `invalidateQueries` 方法: + +```ts +queryClient.invalidateQueries({ + queryKey: ['todos'], + exact: true, +}) + +// 以下查詢將失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, +})) + +// 但以下查詢不會失效 +const todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +})) +``` + +如果您希望獲取**更細緻**的控制,可以傳遞一個斷言函式 (predicate function) 給 `invalidateQueries` 方法。此函式會接收查詢快取中的每個 `Query` 實例,並讓您返回 `true` 或 `false` 來決定是否要使該查詢失效: + +```ts +queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10, +}) + +// 以下查詢將失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { version: 20 }], + queryFn: fetchTodoList, +})) + +// 以下查詢將失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { version: 10 }], + queryFn: fetchTodoList, +})) + +// 但以下查詢不會失效 +todoListQuery = injectQuery(() => ({ + queryKey: ['todos', { version: 5 }], + queryFn: fetchTodoList, +})) +``` diff --git a/docs/zh-hant/framework/angular/guides/query-keys.md b/docs/zh-hant/framework/angular/guides/query-keys.md new file mode 100644 index 00000000000..819ad49ebde --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/query-keys.md @@ -0,0 +1,77 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:25:06.592Z' +id: query-keys +title: 查詢鍵 +--- + +從核心來看,TanStack Query 會根據查詢鍵 (query keys) 為你管理查詢快取 (query caching)。查詢鍵在頂層必須是一個陣列 (Array),可以簡單到只包含單一字串的陣列,也可以複雜到包含多個字串和巢狀物件。只要查詢鍵是可序列化 (serializable) 的,並且**對查詢資料具有唯一性**,你就可以使用它! + +## 簡單的查詢鍵 + +最簡單的鍵形式是由常數值組成的陣列。這種格式適用於: + +- 通用列表/索引資源 (Generic List/Index resources) +- 非階層式資源 (Non-hierarchical resources) + +```ts +// 待辦事項列表 +injectQuery(() => ({ queryKey: ['todos'], ... })) + +// 其他任何東西! +injectQuery(() => ({ queryKey: ['something', 'special'], ... })) +``` + +## 帶有變數的陣列鍵 + +當查詢需要更多資訊來唯一描述其資料時,你可以使用包含字串和任意數量可序列化物件的陣列來描述。這適用於: + +- 階層式或巢狀資源 (Hierarchical or nested resources) + - 通常會傳遞 ID、索引或其他基本類型來唯一識別項目 +- 帶有附加參數的查詢 + - 通常會傳遞包含附加選項的物件 + +```ts +// 單個待辦事項 +injectQuery(() => ({queryKey: ['todo', 5], ...})) + +// 以「預覽」格式顯示的單個待辦事項 +injectQuery(() => ({queryKey: ['todo', 5, {preview: true}], ...})) + +// 標記為「完成」的待辦事項列表 +injectQuery(() => ({queryKey: ['todos', {type: 'done'}], ...})) +``` + +## 查詢鍵會以確定性方式進行雜湊處理! + +這意味著無論物件中鍵的順序如何,以下所有查詢都被視為相等: + +```ts +injectQuery(() => ({ queryKey: ['todos', { status, page }], ... })) +injectQuery(() => ({ queryKey: ['todos', { page, status }], ...})) +injectQuery(() => ({ queryKey: ['todos', { page, status, other: undefined }], ... })) +``` + +然而,以下查詢鍵並不相等的。陣列項目的順序很重要! + +```ts +injectQuery(() => ({ queryKey: ['todos', status, page], ... })) +injectQuery(() => ({ queryKey: ['todos', page, status], ...})) +injectQuery(() => ({ queryKey: ['todos', undefined, page, status], ...})) +``` + +## 如果你的查詢函式依賴於變數,請將其包含在查詢鍵中 + +由於查詢鍵唯一描述了它們正在獲取的資料,因此應該包含你在查詢函式中使用的任何**會變化**的變數。例如: + +```ts +todoId = signal(-1) + +injectQuery(() => ({ + enabled: todoId() > 0, + queryKey: ['todos', todoId()], + queryFn: () => fetchTodoById(todoId()), +})) +``` + +請注意,查詢鍵會作為查詢函式的依賴項。將依賴變數添加到查詢鍵中,可以確保查詢被獨立快取,並且每當變數變化時,_查詢會自動重新獲取_(取決於你的 `staleTime` 設定)。更多資訊和範例請參閱 [exhaustive-deps](../../../eslint/exhaustive-deps.md) 章節。 diff --git a/docs/zh-hant/framework/angular/guides/query-options.md b/docs/zh-hant/framework/angular/guides/query-options.md new file mode 100644 index 00000000000..8c2693a89da --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/query-options.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2025-02-02T18:26:57.000Z' +translation-updated-at: '2025-05-08T20:24:52.117Z' +id: query-options +title: 查詢選項 +--- + +在多重使用場景中共享 `queryKey` 和 `queryFn` 卻又能保持它們彼此關聯的最佳方式之一,就是使用 `queryOptions` 輔助工具。在運行時,這個輔助工具僅會回傳你傳入的內容,但當[與 TypeScript 搭配使用](../typescript.md#typing-query-options)時,它能帶來許多優勢。你可以在單一位置定義查詢的所有可能選項,同時還能獲得完整的型別推論與型別安全檢查。 + +```ts +import { queryOptions } from '@tanstack/angular-query-experimental' + +@Injectable({ + providedIn: 'root', +}) +export class QueriesService { + private http = inject(HttpClient) + + post(postId: number) { + return queryOptions({ + queryKey: ['post', postId], + queryFn: () => { + return lastValueFrom( + this.http.get( + `https://jsonplaceholder.typicode.com/posts/${postId}`, + ), + ) + }, + }) + } +} + +// 使用方式: + +postId = input.required({ + transform: numberAttribute, +}) +queries = inject(QueriesService) + +postQuery = injectQuery(() => this.queries.post(this.postId())) + +queryClient.prefetchQuery(this.queries.post(23)) +queryClient.setQueryData(this.queries.post(42).queryKey, newPost) +``` + +針對無限查詢 (Infinite Queries),另有獨立的 [`infiniteQueryOptions`](../reference/infiniteQueryOptions.md) 輔助工具可供使用。 + +你仍可在元件層級覆寫部分選項。一個極其常見且實用的模式是建立每個元件專屬的 [`select`](./render-optimizations.md#select) 函式: + +```ts +// 型別推論依然有效,因此 query.data 會是 select 的回傳型別而非 queryFn 的 +queries = inject(QueriesService) + +query = injectQuery(() => ({ + ...groupOptions(1), + select: (data) => data.title, +})) +``` diff --git a/docs/zh-hant/framework/angular/guides/query-retries.md b/docs/zh-hant/framework/angular/guides/query-retries.md new file mode 100644 index 00000000000..da49b9e0458 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/query-retries.md @@ -0,0 +1,65 @@ +--- +source-updated-at: '2024-11-07T15:18:52.000Z' +translation-updated-at: '2025-05-08T20:24:49.121Z' +id: query-retries +title: 查詢重試 +--- + +當 `injectQuery` 查詢失敗(查詢函數拋出錯誤)時,TanStack Query 會自動重試該查詢,前提是該查詢的請求尚未達到最大連續重試次數(預設為 `3`)或提供了決定是否允許重試的函數。 + +您可以在全域層級和個別查詢層級設定重試行為: + +- 設定 `retry = false` 將停用重試功能。 +- 設定 `retry = 6` 會在顯示函數拋出的最終錯誤前重試失敗的請求 6 次。 +- 設定 `retry = true` 會無限次重試失敗的請求。 +- 設定 `retry = (failureCount, error) => ...` 可根據請求失敗原因自訂重試邏輯。 + +```ts +import { injectQuery } from '@tanstack/angular-query-experimental' + +// 讓特定查詢重試特定次數 +const result = injectQuery(() => ({ + queryKey: ['todos', 1], + queryFn: fetchTodoListPage, + retry: 10, // 會在顯示錯誤前重試失敗的請求 10 次 +})) +``` + +> 資訊:在最後一次重試嘗試前,`error` 屬性的內容會是 `injectQuery` 回應屬性 `failureReason` 的一部分。因此在上例中,任何錯誤內容在前 9 次重試嘗試(總共 10 次嘗試)期間都會是 `failureReason` 屬性的一部分,若所有重試後錯誤仍存在,最終才會成為 `error` 的一部分。 + +## 重試延遲 + +預設情況下,TanStack Query 的重試不會在請求失敗後立即執行。按照標準做法,每次重試嘗試會逐步套用退避延遲。 + +預設的 `retryDelay` 設定為每次嘗試加倍(從 `1000` 毫秒開始),但不超過 30 秒: + +```ts +// 為所有查詢進行設定 +import { + QueryCache, + QueryClient, + QueryClientProvider, +} from '@tanstack/angular-query-experimental' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, + }, +}) + +bootstrapApplication(AppComponent, { + providers: [provideTanStackQuery(queryClient)], +}) +``` + +雖然不建議,但您顯然可以在外掛程式和個別查詢選項中覆寫 `retryDelay` 函數/整數。若設為整數而非函數,延遲時間將始終固定: + +```ts +const result = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, + retryDelay: 1000, // 無論重試多少次,始終等待 1000 毫秒後重試 +})) +``` diff --git a/docs/zh-hant/framework/angular/guides/scroll-restoration.md b/docs/zh-hant/framework/angular/guides/scroll-restoration.md new file mode 100644 index 00000000000..844ceb6e009 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/scroll-restoration.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:24:19.863Z' +id: scroll-restoration +title: Scroll Restoration +ref: docs/zh-hant/framework/react/guides/scroll-restoration.md +--- diff --git a/docs/zh-hant/framework/angular/guides/window-focus-refetching.md b/docs/zh-hant/framework/angular/guides/window-focus-refetching.md new file mode 100644 index 00000000000..934fd3a9960 --- /dev/null +++ b/docs/zh-hant/framework/angular/guides/window-focus-refetching.md @@ -0,0 +1,42 @@ +--- +source-updated-at: '2025-03-01T21:43:55.000Z' +translation-updated-at: '2025-05-08T20:24:19.843Z' +id: window-focus-refetching +title: Window Focus Refetching +ref: docs/zh-hant/framework/react/guides/window-focus-refetching.md +replace: + '@tanstack/react-query': '@tanstack/angular-query-experimental' +--- + +[//]: # 'Example' + +```ts +export const appConfig: ApplicationConfig = { + providers: [ + provideTanStackQuery( + new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, // default: true + }, + }, + }), + ), + ], +} +``` + +[//]: # 'Example' +[//]: # 'Example2' + +```ts +injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + refetchOnWindowFocus: false, +})) +``` + +[//]: # 'Example2' +[//]: # 'ReactNative' +[//]: # 'ReactNative' diff --git a/docs/zh-hant/framework/angular/installation.md b/docs/zh-hant/framework/angular/installation.md new file mode 100644 index 00000000000..c353e971947 --- /dev/null +++ b/docs/zh-hant/framework/angular/installation.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-08T20:17:08.378Z' +id: installation +title: 安裝 +--- + +> 重要:此函式庫目前處於實驗階段。這意味著次要版本和修補版本都可能包含破壞性變更。升級時請謹慎操作。若您在實驗階段將此用於生產環境,請將版本鎖定在特定修補版本,以避免意外的破壞性變更。 + +### NPM + +_Angular Query 相容於 Angular v16 及以上版本_ + +```bash +npm i @tanstack/angular-query-experimental +``` + +或 + +```bash +pnpm add @tanstack/angular-query-experimental +``` + +或 + +```bash +yarn add @tanstack/angular-query-experimental +``` + +或 + +```bash +bun add @tanstack/angular-query-experimental +``` + +> 想在下載前試用看看嗎?試試 [簡單範例](../examples/simple) 或 [基礎範例](../examples/basic)! diff --git a/docs/zh-hant/framework/angular/overview.md b/docs/zh-hant/framework/angular/overview.md new file mode 100644 index 00000000000..95356c1f8f7 --- /dev/null +++ b/docs/zh-hant/framework/angular/overview.md @@ -0,0 +1,115 @@ +--- +source-updated-at: '2025-04-13T18:21:31.000Z' +translation-updated-at: '2025-05-08T20:17:51.709Z' +id: overview +title: 概述 +--- + +> 重要提示:此函式庫目前處於實驗階段。這意味著次要版本和修補版本都可能包含破壞性變更。升級時請謹慎操作。若您要在生產環境中使用此實驗階段的套件,請將版本鎖定在特定修補版本,以避免意外的破壞性變更。 + +`@tanstack/angular-query-experimental` 套件提供了在 Angular 中使用 TanStack Query 的一流 API。 + +## 歡迎提供意見回饋! + +我們正在努力為 TanStack Query 在 Angular 上建立穩定的 API。如果您有任何意見,請透過 [TanStack Discord](https://tlinz.com/discord) 伺服器聯繫我們,或到 [GitHub 上的這個討論串](https://github.com/TanStack/query/discussions/6293)留言。 + +## 支援的 Angular 版本 + +TanStack Query 相容於 Angular v16 及更高版本。 + +TanStack Query (前身為 React Query) 常被稱為網頁應用程式中缺失的資料獲取函式庫,但更技術性地來說,它能讓您在網頁應用程式中輕鬆實現**獲取、快取、同步和更新伺服器狀態 (server state)**。 + +## 動機 + +大多數核心網頁框架**並未**提供一套全面的資料獲取或更新方法。因此,開發者最終要麼建立封裝了嚴格資料獲取觀點的元框架 (meta-frameworks),要麼發明自己的資料獲取方式。這通常意味著拼湊基於元件的狀態和副作用,或者使用更通用的狀態管理函式庫來儲存並在應用程式中提供非同步資料。 + +雖然大多數傳統的狀態管理函式庫非常適合處理客戶端狀態 (client state),但它們**在處理非同步或伺服器狀態 (server state) 時表現不佳**。這是因為**伺服器狀態完全不同**。首先,伺服器狀態: + +- 遠端儲存在您可能無法控制或擁有的位置 +- 需要非同步 API 來獲取和更新 +- 意味著共享所有權,其他人可能在您不知情的情況下更改它 +- 如果不小心處理,可能會在您的應用程式中變得「過時」 + +一旦您理解了應用程式中伺服器狀態的本質,**隨著開發進展,更多挑戰會接踵而至**,例如: + +- 快取...(可能是程式設計中最難實現的部分) +- 將多個相同資料的請求去重 (deduping) 為單一請求 +- 在後台更新「過時」的資料 +- 知道資料何時「過時」 +- 盡快反映資料的更新 +- 效能優化,如分頁 (pagination) 和懶加載 (lazy loading) 資料 +- 管理伺服器狀態的記憶體和垃圾回收 +- 透過結構共享 (structural sharing) 記憶化 (memoizing) 查詢結果 + +如果這份清單沒有讓您感到不知所措,那可能意味著您已經解決了所有伺服器狀態的問題,值得獲得獎勵。然而,如果您像大多數人一樣,要麼尚未解決全部或大部分挑戰,而我們才剛剛觸及表面! + +TanStack Query 無疑是管理伺服器狀態的最佳函式庫之一。它**開箱即用、零配置**,並且可以隨著應用程式的增長按您的喜好進行**自訂**。 + +TanStack Query 讓您能夠戰勝並克服伺服器狀態的棘手挑戰和障礙,在它開始控制您之前掌控您的應用程式資料。 + +從更技術的角度來看,TanStack Query 可能會: + +- 幫助您從應用程式中移除**許多**複雜且難以理解的程式碼,並用寥寥幾行 TanStack Query 邏輯取代 +- 使您的應用程式更易於維護,並在無需擔心連接新的伺服器狀態資料來源的情況下更容易建立新功能 +- 透過讓您的應用程式感覺比以往更快、更靈敏,直接影響您的終端使用者 +- 可能幫助您節省頻寬並提高記憶體效能 + +[//]: # '範例' + +## 說夠了,快給我看看程式碼! + +在下面的範例中,您可以看到 TanStack Query 以最基本和簡單的形式用於獲取 TanStack Query GitHub 專案本身的 GitHub 統計資料: + +[在 StackBlitz 中開啟](https://stackblitz.com/github/TanStack/query/tree/main/examples/angular/simple) + +```angular-ts +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { HttpClient } from '@angular/common/http' +import { CommonModule } from '@angular/common' +import { injectQuery } from '@tanstack/angular-query-experimental' +import { lastValueFrom } from 'rxjs' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'simple-example', + standalone: true, + template: ` + @if (query.isPending()) { + Loading... + } + @if (query.error()) { + An error has occurred: {{ query.error().message }} + } + @if (query.data(); as data) { +

    {{ data.name }}

    +

    {{ data.description }}

    + 👀 {{ data.subscribers_count }} + ✨ {{ data.stargazers_count }} + 🍴 {{ data.forks_count }} + } + ` +}) +export class SimpleExampleComponent { + http = inject(HttpClient) + + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + lastValueFrom( + this.http.get('https://api.github.com/repos/tanstack/query'), + ), + })) +} + +interface Response { + name: string + description: string + subscribers_count: number + stargazers_count: number + forks_count: number +} +``` + +## 您說服我了,接下來該怎麼做? + +- 按照自己的步調學習 TanStack Query,我們提供了詳盡的[逐步指南](../installation.md)和[API 參考文件](../reference/functions/injectquery.md) diff --git a/docs/zh-hant/framework/angular/quick-start.md b/docs/zh-hant/framework/angular/quick-start.md new file mode 100644 index 00000000000..7a4c1c17b7b --- /dev/null +++ b/docs/zh-hant/framework/angular/quick-start.md @@ -0,0 +1,120 @@ +--- +source-updated-at: '2024-11-19T18:32:49.000Z' +translation-updated-at: '2025-05-08T20:17:24.453Z' +id: quick-start +title: 快速開始 +--- + +> 重要:此函式庫目前處於實驗階段。這意味著在次要版本和修補版本中可能會出現破壞性變更。升級時請謹慎操作。若您在實驗階段將此用於生產環境,請將版本鎖定在修補版本,以避免意外的破壞性變更。 + +[//]: # '範例' + +如果您正在尋找一個完整功能的範例,請查看我們的 [基本 codesandbox 範例](../examples/basic) + +### 將 client 提供給您的 App + +```ts +import { provideHttpClient } from '@angular/common/http' +import { + provideTanStackQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +bootstrapApplication(AppComponent, { + providers: [provideHttpClient(), provideTanStackQuery(new QueryClient())], +}) +``` + +或在基於 NgModule 的應用中 + +```ts +import { provideHttpClient } from '@angular/common/http' +import { + provideTanStackQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule], + providers: [provideTanStackQuery(new QueryClient())], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +### 包含查詢 (query) 和變異 (mutation) 的元件 + +```angular-ts +import { Component, Injectable, inject } from '@angular/core' +import { HttpClient } from '@angular/common/http' +import { lastValueFrom } from 'rxjs' + +import { + injectMutation, + injectQuery, + QueryClient +} from '@tanstack/angular-query-experimental' + +@Component({ + standalone: true, + template: ` +
    + + +
      + @for (todo of query.data(); track todo.title) { +
    • {{ todo.title }}
    • + } +
    +
    + `, +}) +export class TodosComponent { + todoService = inject(TodoService) + queryClient = inject(QueryClient) + + query = injectQuery(() => ({ + queryKey: ['todos'], + queryFn: () => this.todoService.getTodos(), + })) + + mutation = injectMutation(() => ({ + mutationFn: (todo: Todo) => this.todoService.addTodo(todo), + onSuccess: () => { + this.queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, + })) + + onAddTodo() { + this.mutation.mutate({ + id: Date.now().toString(), + title: 'Do Laundry', + }) + } +} + +@Injectable({ providedIn: 'root' }) +export class TodoService { + private http = inject(HttpClient) + + getTodos(): Promise { + return lastValueFrom( + this.http.get('https://jsonplaceholder.typicode.com/todos'), + ) + } + + addTodo(todo: Todo): Promise { + return lastValueFrom( + this.http.post('https://jsonplaceholder.typicode.com/todos', todo), + ) + } +} + +interface Todo { + id: string + title: string +} +``` + +[//]: # '範例' diff --git a/docs/zh-hant/framework/angular/reference/functions/infinitequeryoptions.md b/docs/zh-hant/framework/angular/reference/functions/infinitequeryoptions.md new file mode 100644 index 00000000000..614a6521b67 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/infinitequeryoptions.md @@ -0,0 +1,134 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:26:11.122Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +--- + +# Function: infiniteQueryOptions() + +Allows to share and re-use infinite query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +## Param + +The infinite query options to tag with the type from `queryFn`. + +## infiniteQueryOptions(options) + +```ts +function infiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>( + options, +): UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + object +``` + +Allows to share and re-use infinite query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **options**: [`UndefinedInitialDataInfiniteOptions`](../type-aliases/undefinedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> + +The infinite query options to tag with the type from `queryFn`. + +### Returns + +[`UndefinedInitialDataInfiniteOptions`](../type-aliases/undefinedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` + +The tagged infinite query options. + +The tagged infinite query options. + +### Param + +The infinite query options to tag with the type from `queryFn`. + +### Defined in + +[infinite-query-options.ts:59](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L59) + +## infiniteQueryOptions(options) + +```ts +function infiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>( + options, +): DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam +> & + object +``` + +Allows to share and re-use infinite query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **options**: [`DefinedInitialDataInfiniteOptions`](../type-aliases/definedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> + +The infinite query options to tag with the type from `queryFn`. + +### Returns + +[`DefinedInitialDataInfiniteOptions`](../type-aliases/definedinitialdatainfiniteoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` + +The tagged infinite query options. + +The tagged infinite query options. + +### Param + +The infinite query options to tag with the type from `queryFn`. + +### Defined in + +[infinite-query-options.ts:91](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L91) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectinfinitequery.md b/docs/zh-hant/framework/angular/reference/functions/injectinfinitequery.md new file mode 100644 index 00000000000..766d44e8d5b --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectinfinitequery.md @@ -0,0 +1,190 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:26:11.109Z' +id: injectInfiniteQuery +title: injectInfiniteQuery +--- + +# Function: injectInfiniteQuery() + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +## Param + +A function that returns infinite query options. + +## Param + +The Angular injector to use. + +## injectInfiniteQuery(optionsFn, injector) + +```ts +function injectInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(optionsFn, injector?): CreateInfiniteQueryResult +``` + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **optionsFn** + +A function that returns infinite query options. + +• **injector?**: `Injector` + +The Angular injector to use. + +### Returns + +[`CreateInfiniteQueryResult`](../type-aliases/createinfinitequeryresult.md)\<`TData`, `TError`\> + +The infinite query result. + +The infinite query result. + +### Param + +A function that returns infinite query options. + +### Param + +The Angular injector to use. + +### Defined in + +[inject-infinite-query.ts:30](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-infinite-query.ts#L30) + +## injectInfiniteQuery(optionsFn, injector) + +```ts +function injectInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(optionsFn, injector?): DefinedCreateInfiniteQueryResult +``` + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **optionsFn** + +A function that returns infinite query options. + +• **injector?**: `Injector` + +The Angular injector to use. + +### Returns + +[`DefinedCreateInfiniteQueryResult`](../type-aliases/definedcreateinfinitequeryresult.md)\<`TData`, `TError`\> + +The infinite query result. + +The infinite query result. + +### Param + +A function that returns infinite query options. + +### Param + +The Angular injector to use. + +### Defined in + +[inject-infinite-query.ts:57](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-infinite-query.ts#L57) + +## injectInfiniteQuery(optionsFn, injector) + +```ts +function injectInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(optionsFn, injector?): CreateInfiniteQueryResult +``` + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or "infinite scroll" + +### Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +### Parameters + +• **optionsFn** + +A function that returns infinite query options. + +• **injector?**: `Injector` + +The Angular injector to use. + +### Returns + +[`CreateInfiniteQueryResult`](../type-aliases/createinfinitequeryresult.md)\<`TData`, `TError`\> + +The infinite query result. + +The infinite query result. + +### Param + +A function that returns infinite query options. + +### Param + +The Angular injector to use. + +### Defined in + +[inject-infinite-query.ts:84](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-infinite-query.ts#L84) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectisfetching.md b/docs/zh-hant/framework/angular/reference/functions/injectisfetching.md new file mode 100644 index 00000000000..b654f78d9f1 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectisfetching.md @@ -0,0 +1,37 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:26:11.097Z' +id: injectIsFetching +title: injectIsFetching +--- + +# Function: injectIsFetching() + +```ts +function injectIsFetching(filters?, injector?): Signal +``` + +Injects a signal that tracks the number of queries that your application is loading or +fetching in the background. + +Can be used for app-wide loading indicators + +## Parameters + +• **filters?**: `QueryFilters` + +The filters to apply to the query. + +• **injector?**: `Injector` + +The Angular injector to use. + +## Returns + +`Signal`\<`number`\> + +signal with number of loading or fetching queries. + +## Defined in + +[inject-is-fetching.ts:17](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-is-fetching.ts#L17) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectismutating.md b/docs/zh-hant/framework/angular/reference/functions/injectismutating.md new file mode 100644 index 00000000000..1210a6d0ab4 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectismutating.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:26:11.084Z' +id: injectIsMutating +title: injectIsMutating +--- + +# Function: injectIsMutating() + +```ts +function injectIsMutating(filters?, injector?): Signal +``` + +Injects a signal that tracks the number of mutations that your application is fetching. + +Can be used for app-wide loading indicators + +## Parameters + +• **filters?**: `MutationFilters` + +The filters to apply to the query. + +• **injector?**: `Injector` + +The Angular injector to use. + +## Returns + +`Signal`\<`number`\> + +signal with number of fetching mutations. + +## Defined in + +[inject-is-mutating.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-is-mutating.ts#L16) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectmutation.md b/docs/zh-hant/framework/angular/reference/functions/injectmutation.md new file mode 100644 index 00000000000..d0ea43baac9 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectmutation.md @@ -0,0 +1,49 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:26:12.515Z' +id: injectMutation +title: 函數 / injectMutation +--- + +# 函式: injectMutation() + +```ts +function injectMutation( + optionsFn, + injector?, +): CreateMutationResult +``` + +注入一個 mutation (變更操作):這是一個可主動調用的命令式函式,通常用於執行伺服器端的副作用。 + +與查詢 (queries) 不同,mutation 不會自動執行。 + +## 型別參數 + +• **TData** = `unknown` + +• **TError** = `Error` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## 參數 + +• **optionsFn** + +一個回傳 mutation 選項的函式。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器 (injector)。 + +## 回傳值 + +[`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`\> + +該 mutation 操作。 + +## 定義於 + +[inject-mutation.ts:38](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-mutation.ts#L38) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectmutationstate.md b/docs/zh-hant/framework/angular/reference/functions/injectmutationstate.md new file mode 100644 index 00000000000..671c5a78cd3 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectmutationstate.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:56.397Z' +id: injectMutationState +title: injectMutationState +--- + +# Function: injectMutationState() + +```ts +function injectMutationState( + mutationStateOptionsFn, + options?, +): Signal +``` + +Injects a signal that tracks the state of all mutations. + +## Type Parameters + +• **TResult** = `MutationState`\<`unknown`, `Error`, `unknown`, `unknown`\> + +## Parameters + +• **mutationStateOptionsFn** = `...` + +A function that returns mutation state options. + +• **options?**: [`InjectMutationStateOptions`](../interfaces/injectmutationstateoptions.md) + +The Angular injector to use. + +## Returns + +`Signal`\<`TResult`[]\> + +The signal that tracks the state of all mutations. + +## Defined in + +[inject-mutation-state.ts:53](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-mutation-state.ts#L53) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectqueries.md b/docs/zh-hant/framework/angular/reference/functions/injectqueries.md new file mode 100644 index 00000000000..86ae4088bcc --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectqueries.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:56.384Z' +id: injectQueries +title: injectQueries +--- + +# Function: injectQueries() + +```ts +function injectQueries( + __namedParameters, + injector?, +): Signal +``` + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TCombinedResult** = `T` _extends_ [] ? [] : `T` _extends_ [`Head`] ? [`GetResults`\<`Head`\>] : `T` _extends_ [`Head`, `...Tail[]`] ? [`...Tail[]`] _extends_ [] ? [] : [`...Tail[]`] _extends_ [`Head`] ? [`GetResults`\<`Head`\>, `GetResults`\<`Head`\>] : [`...Tail[]`] _extends_ [`Head`, `...Tail[]`] ? [`...Tail[]`] _extends_ [] ? [] : [`...Tail[]`] _extends_ [`Head`] ? [`GetResults`\<`Head`\>, `GetResults`\<`Head`\>, `GetResults`\<`Head`\>] : [`...Tail[]`] _extends_ [`Head`, `...Tail[]`] ? [`...(...)[]`] _extends_ [] ? [] : ... _extends_ ... ? ... : ... : [`...(...)[]`] _extends_ ...[] ? ...[] : ...[] : [`...Tail[]`] _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] : `T` _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] + +## Parameters + +• **\_\_namedParameters** + +• **\_\_namedParameters.combine?** + +• **\_\_namedParameters.queries?**: `Signal`\<[`...(T extends [] ? [] : T extends [Head] ? [GetOptions] : T extends [Head, ...Tail[]] ? [...Tail[]] extends [] ? [] : [...Tail[]] extends [Head] ? [GetOptions, GetOptions] : [...Tail[]] extends [Head, ...Tail[]] ? [...(...)[]] extends [] ? [] : (...) extends (...) ? (...) : (...) : readonly (...)[] extends [...(...)[]] ? [...(...)[]] : (...) extends (...) ? (...) : (...) : readonly unknown[] extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[])[]`]\> + +• **injector?**: `Injector` + +## Returns + +`Signal`\<`TCombinedResult`\> + +## Defined in + +[inject-queries.ts:188](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-queries.ts#L188) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectquery.md b/docs/zh-hant/framework/angular/reference/functions/injectquery.md new file mode 100644 index 00000000000..bcba90caeb2 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectquery.md @@ -0,0 +1,323 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:27:11.129Z' +id: injectQuery +title: 函數 / injectQuery +--- + +# 函式: injectQuery() + +注入一個查詢 (query):宣告式地依賴於與唯一鍵綁定的非同步資料來源。 + +**基本範例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +類似 Angular 的 `computed`,傳遞給 `injectQuery` 的函式會在反應式上下文 (reactive context) 中執行。 +在以下範例中,當 filter signal 變更為真值 (truthy value) 時,查詢會自動啟用並執行。 +當 filter signal 變回假值 (falsy value) 時,查詢會被停用。 + +**反應式範例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // Signal 可以與表達式結合使用 + enabled: !!this.filter(), + })) +} +``` + +## 參數 + +一個回傳查詢選項的函式。 + +## 參數 + +要使用的 Angular 注入器 (injector)。 + +## 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +## injectQuery(optionsFn, injector) + +```ts +function injectQuery( + optionsFn, + injector?, +): DefinedCreateQueryResult +``` + +注入一個查詢:宣告式地依賴於與唯一鍵綁定的非同步資料來源。 + +**基本範例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +類似 Angular 的 `computed`,傳遞給 `injectQuery` 的函式會在反應式上下文中執行。 +在以下範例中,當 filter signal 變更為真值時,查詢會自動啟用並執行。 +當 filter signal 變回假值時,查詢會被停用。 + +**反應式範例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // Signal 可以與表達式結合使用 + enabled: !!this.filter(), + })) +} +``` + +### 型別參數 + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _繼承自_ `QueryKey` = `QueryKey` + +### 參數 + +• **optionsFn** + +一個回傳查詢選項的函式。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器。 + +### 回傳值 + +[`DefinedCreateQueryResult`](../type-aliases/definedcreatequeryresult.md)\<`TData`, `TError`\> + +查詢結果。 + +查詢結果。 + +### 參數 + +一個回傳查詢選項的函式。 + +### 參數 + +要使用的 Angular 注入器。 + +### 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 定義於 + +[inject-query.ts:53](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query.ts#L53) + +## injectQuery(optionsFn, injector) + +```ts +function injectQuery( + optionsFn, + injector?, +): CreateQueryResult +``` + +注入一個查詢:宣告式地依賴於與唯一鍵綁定的非同步資料來源。 + +**基本範例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +類似 Angular 的 `computed`,傳遞給 `injectQuery` 的函式會在反應式上下文中執行。 +在以下範例中,當 filter signal 變更為真值時,查詢會自動啟用並執行。 +當 filter signal 變回假值時,查詢會被停用。 + +**反應式範例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // Signal 可以與表達式結合使用 + enabled: !!this.filter(), + })) +} +``` + +### 型別參數 + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _繼承自_ `QueryKey` = `QueryKey` + +### 參數 + +• **optionsFn** + +一個回傳查詢選項的函式。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器。 + +### 回傳值 + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +查詢結果。 + +查詢結果。 + +### 參數 + +一個回傳查詢選項的函式。 + +### 參數 + +要使用的 Angular 注入器。 + +### 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 定義於 + +[inject-query.ts:102](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query.ts#L102) + +## injectQuery(optionsFn, injector) + +```ts +function injectQuery( + optionsFn, + injector?, +): CreateQueryResult +``` + +注入一個查詢:宣告式地依賴於與唯一鍵綁定的非同步資料來源。 + +**基本範例** + +```ts +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + this.#http.get('https://api.github.com/repos/tanstack/query'), + })) +} +``` + +類似 Angular 的 `computed`,傳遞給 `injectQuery` 的函式會在反應式上下文中執行。 +在以下範例中,當 filter signal 變更為真值時,查詢會自動啟用並執行。 +當 filter signal 變回假值時,查詢會被停用。 + +**反應式範例** + +```ts +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // Signal 可以與表達式結合使用 + enabled: !!this.filter(), + })) +} +``` + +### 型別參數 + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _繼承自_ `QueryKey` = `QueryKey` + +### 參數 + +• **optionsFn** + +一個回傳查詢選項的函式。 + +• **injector?**: `Injector` + +要使用的 Angular 注入器。 + +### 回傳值 + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +查詢結果。 + +查詢結果。 + +### 參數 + +一個回傳查詢選項的函式。 + +### 參數 + +要使用的 Angular 注入器。 + +### 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 參見 + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +### 定義於 + +[inject-query.ts:151](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query.ts#L151) diff --git a/docs/zh-hant/framework/angular/reference/functions/injectqueryclient.md b/docs/zh-hant/framework/angular/reference/functions/injectqueryclient.md new file mode 100644 index 00000000000..0696639d45e --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/injectqueryclient.md @@ -0,0 +1,90 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.830Z' +id: injectQueryClient +title: injectQueryClient +--- + +# Function: injectQueryClient() + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +## injectQueryClient() + +```ts +function injectQueryClient(): QueryClient +``` + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +### Returns + +`QueryClient` + +### Defined in + +[inject-query-client.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L16) + +## injectQueryClient(injectOptions) + +```ts +function injectQueryClient(injectOptions): QueryClient +``` + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +### Parameters + +• **injectOptions**: `InjectOptions` & `object` & `object` + +### Returns + +`QueryClient` + +### Defined in + +[inject-query-client.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L16) + +## injectQueryClient(injectOptions) + +```ts +function injectQueryClient(injectOptions): null | QueryClient +``` + +Injects the `QueryClient` instance into the component or service. + +**Example** + +```ts +const queryClient = injectQueryClient() +``` + +### Parameters + +• **injectOptions**: `InjectOptions` & `object` + +### Returns + +`null` \| `QueryClient` + +### Defined in + +[inject-query-client.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L16) diff --git a/docs/zh-hant/framework/angular/reference/functions/provideangularquery.md b/docs/zh-hant/framework/angular/reference/functions/provideangularquery.md new file mode 100644 index 00000000000..8e62e9e9092 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/provideangularquery.md @@ -0,0 +1,66 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.817Z' +id: provideAngularQuery +title: provideAngularQuery +--- + +# Function: provideAngularQuery() + +```ts +function provideAngularQuery(queryClient): EnvironmentProviders +``` + +Sets up providers necessary to enable TanStack Query functionality for Angular applications. + +Allows to configure a `QueryClient`. + +**Example - standalone** + +```ts +import { + provideAngularQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +bootstrapApplication(AppComponent, { + providers: [provideAngularQuery(new QueryClient())], +}) +``` + +**Example - NgModule-based** + +```ts +import { + provideAngularQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' + +@NgModule({ + declarations: [AppComponent], + imports: [BrowserModule], + providers: [provideAngularQuery(new QueryClient())], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +## Parameters + +• **queryClient**: `QueryClient` + +A `QueryClient` instance. + +## Returns + +`EnvironmentProviders` + +A set of providers to set up TanStack Query. + +## See + +https://tanstack.com/query/v5/docs/framework/angular/quick-start + +## Defined in + +[providers.ts:50](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/providers.ts#L50) diff --git a/docs/zh-hant/framework/angular/reference/functions/providequeryclient.md b/docs/zh-hant/framework/angular/reference/functions/providequeryclient.md new file mode 100644 index 00000000000..c32dadcdf47 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/providequeryclient.md @@ -0,0 +1,29 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.804Z' +id: provideQueryClient +title: provideQueryClient +--- + +# Function: provideQueryClient() + +```ts +function provideQueryClient(value): Provider +``` + +Usually [provideAngularQuery](provideangularquery.md) is used once to set up TanStack Query and the +[https://tanstack.com/query/latest/docs/reference/QueryClient|QueryClient](https://tanstack.com/query/latest/docs/reference/QueryClient|QueryClient) +for the entire application. You can use `provideQueryClient` to provide a +different `QueryClient` instance for a part of the application. + +## Parameters + +• **value**: `QueryClient` \| () => `QueryClient` + +## Returns + +`Provider` + +## Defined in + +[inject-query-client.ts:25](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-query-client.ts#L25) diff --git a/docs/zh-hant/framework/angular/reference/functions/queryoptions.md b/docs/zh-hant/framework/angular/reference/functions/queryoptions.md new file mode 100644 index 00000000000..a26bdc7e244 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/functions/queryoptions.md @@ -0,0 +1,146 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.792Z' +id: queryOptions +title: queryOptions +--- + +# Function: queryOptions() + +Allows to share and re-use query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** + +```ts +const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise +}) + +const queryClient = new QueryClient() +const data = queryClient.getQueryData(queryKey) +// ^? number | undefined +``` + +## Param + +The query options to tag with the type from `queryFn`. + +## queryOptions(options) + +```ts +function queryOptions( + options, +): UndefinedInitialDataOptions & object +``` + +Allows to share and re-use query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** + +```ts +const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise +}) + +const queryClient = new QueryClient() +const data = queryClient.getQueryData(queryKey) +// ^? number | undefined +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +The query options to tag with the type from `queryFn`. + +### Returns + +[`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +The tagged query options. + +The tagged query options. + +### Param + +The query options to tag with the type from `queryFn`. + +### Defined in + +[query-options.ts:52](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L52) + +## queryOptions(options) + +```ts +function queryOptions( + options, +): DefinedInitialDataOptions & object +``` + +Allows to share and re-use query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** + +```ts +const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise +}) + +const queryClient = new QueryClient() +const data = queryClient.getQueryData(queryKey) +// ^? number | undefined +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +The query options to tag with the type from `queryFn`. + +### Returns + +[`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +The tagged query options. + +The tagged query options. + +### Param + +The query options to tag with the type from `queryFn`. + +### Defined in + +[query-options.ts:85](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L85) diff --git a/docs/zh-hant/framework/angular/reference/index.md b/docs/zh-hant/framework/angular/reference/index.md new file mode 100644 index 00000000000..c0b2bb5fd2d --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/index.md @@ -0,0 +1,52 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:24:19.836Z' +id: '@tanstack/angular-query-experimental' +title: '@tanstack/angular-query-experimental' +--- + +# @tanstack/angular-query-experimental + +## Interfaces + +- [BaseMutationNarrowing](interfaces/basemutationnarrowing.md) +- [BaseQueryNarrowing](interfaces/basequerynarrowing.md) +- [CreateBaseQueryOptions](interfaces/createbasequeryoptions.md) +- [CreateInfiniteQueryOptions](interfaces/createinfinitequeryoptions.md) +- [CreateMutationOptions](interfaces/createmutationoptions.md) +- [CreateQueryOptions](interfaces/createqueryoptions.md) +- [InjectMutationStateOptions](interfaces/injectmutationstateoptions.md) + +## Type Aliases + +- [CreateBaseMutationResult](type-aliases/createbasemutationresult.md) +- [CreateBaseQueryResult](type-aliases/createbasequeryresult.md) +- [CreateInfiniteQueryResult](type-aliases/createinfinitequeryresult.md) +- [CreateMutateAsyncFunction](type-aliases/createmutateasyncfunction.md) +- [CreateMutateFunction](type-aliases/createmutatefunction.md) +- [CreateMutationResult](type-aliases/createmutationresult.md) +- [CreateQueryResult](type-aliases/createqueryresult.md) +- [DefinedCreateInfiniteQueryResult](type-aliases/definedcreateinfinitequeryresult.md) +- [DefinedCreateQueryResult](type-aliases/definedcreatequeryresult.md) +- [DefinedInitialDataInfiniteOptions](type-aliases/definedinitialdatainfiniteoptions.md) +- [DefinedInitialDataOptions](type-aliases/definedinitialdataoptions.md) +- [NonUndefinedGuard](type-aliases/nonundefinedguard.md) +- [QueriesOptions](type-aliases/queriesoptions.md) +- [QueriesResults](type-aliases/queriesresults.md) +- [UndefinedInitialDataInfiniteOptions](type-aliases/undefinedinitialdatainfiniteoptions.md) +- [UndefinedInitialDataOptions](type-aliases/undefinedinitialdataoptions.md) + +## Functions + +- [infiniteQueryOptions](functions/infinitequeryoptions.md) +- [injectInfiniteQuery](functions/injectinfinitequery.md) +- [injectIsFetching](functions/injectisfetching.md) +- [injectIsMutating](functions/injectismutating.md) +- [injectMutation](functions/injectmutation.md) +- [injectMutationState](functions/injectmutationstate.md) +- [injectQueries](functions/injectqueries.md) +- [injectQuery](functions/injectquery.md) +- [injectQueryClient](functions/injectqueryclient.md) +- [provideAngularQuery](functions/provideangularquery.md) +- [provideQueryClient](functions/providequeryclient.md) +- [queryOptions](functions/queryoptions.md) diff --git a/docs/zh-hant/framework/angular/reference/interfaces/basemutationnarrowing.md b/docs/zh-hant/framework/angular/reference/interfaces/basemutationnarrowing.md new file mode 100644 index 00000000000..b4a6f20dcd7 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/basemutationnarrowing.md @@ -0,0 +1,98 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.780Z' +id: BaseMutationNarrowing +title: BaseMutationNarrowing +--- + +# Interface: BaseMutationNarrowing\ + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Properties + +### isError() + +```ts +isError: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:248](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L248) + +--- + +### isIdle() + +```ts +isIdle: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:278](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L278) + +--- + +### isPending() + +```ts +isPending: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:263](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L263) + +--- + +### isSuccess() + +```ts +isSuccess: (this) => this is CreateMutationResult, Object> & Object>; +``` + +#### Parameters + +• **this**: [`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`, `CreateStatusBasedMutationResult`\<`"error"` \| `"success"` \| `"pending"` \| `"idle"`, `TData`, `TError`, `TVariables`, `TContext`\>\> + +#### Returns + +`this is CreateMutationResult, Object> & Object>` + +#### Defined in + +[types.ts:233](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L233) diff --git a/docs/zh-hant/framework/angular/reference/interfaces/basequerynarrowing.md b/docs/zh-hant/framework/angular/reference/interfaces/basequerynarrowing.md new file mode 100644 index 00000000000..24fb55ecca8 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/basequerynarrowing.md @@ -0,0 +1,74 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.768Z' +id: BaseQueryNarrowing +title: BaseQueryNarrowing +--- + +# Interface: BaseQueryNarrowing\ + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Properties + +### isError() + +```ts +isError: (this) => this is CreateBaseQueryResult>; +``` + +#### Parameters + +• **this**: [`CreateBaseQueryResult`](../type-aliases/createbasequeryresult.md)\<`TData`, `TError`, `QueryObserverResult`\<`TData`, `TError`\>\> + +#### Returns + +`this is CreateBaseQueryResult>` + +#### Defined in + +[types.ts:75](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L75) + +--- + +### isPending() + +```ts +isPending: (this) => this is CreateBaseQueryResult>; +``` + +#### Parameters + +• **this**: [`CreateBaseQueryResult`](../type-aliases/createbasequeryresult.md)\<`TData`, `TError`, `QueryObserverResult`\<`TData`, `TError`\>\> + +#### Returns + +`this is CreateBaseQueryResult>` + +#### Defined in + +[types.ts:82](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L82) + +--- + +### isSuccess() + +```ts +isSuccess: (this) => this is CreateBaseQueryResult>; +``` + +#### Parameters + +• **this**: [`CreateBaseQueryResult`](../type-aliases/createbasequeryresult.md)\<`TData`, `TError`, `QueryObserverResult`\<`TData`, `TError`\>\> + +#### Returns + +`this is CreateBaseQueryResult>` + +#### Defined in + +[types.ts:68](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L68) diff --git a/docs/zh-hant/framework/angular/reference/interfaces/createbasequeryoptions.md b/docs/zh-hant/framework/angular/reference/interfaces/createbasequeryoptions.md new file mode 100644 index 00000000000..cb8a273bb86 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/createbasequeryoptions.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.755Z' +id: CreateBaseQueryOptions +title: CreateBaseQueryOptions +--- + +# Interface: CreateBaseQueryOptions\ + +## Extends + +- `QueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryData`, `TQueryKey`\> + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` diff --git a/docs/zh-hant/framework/angular/reference/interfaces/createinfinitequeryoptions.md b/docs/zh-hant/framework/angular/reference/interfaces/createinfinitequeryoptions.md new file mode 100644 index 00000000000..b73a33033c9 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/createinfinitequeryoptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.743Z' +id: CreateInfiniteQueryOptions +title: CreateInfiniteQueryOptions +--- + +# Interface: CreateInfiniteQueryOptions\ + +## Extends + +- `OmitKeyof`\<`InfiniteQueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryData`, `TQueryKey`, `TPageParam`\>, `"suspense"`\> + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` diff --git a/docs/zh-hant/framework/angular/reference/interfaces/createmutationoptions.md b/docs/zh-hant/framework/angular/reference/interfaces/createmutationoptions.md new file mode 100644 index 00000000000..365e90b5374 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/createmutationoptions.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.731Z' +id: CreateMutationOptions +title: CreateMutationOptions +--- + +# Interface: CreateMutationOptions\ + +## Extends + +- `OmitKeyof`\<`MutationObserverOptions`\<`TData`, `TError`, `TVariables`, `TContext`\>, `"_defaulted"`\> + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` diff --git a/docs/zh-hant/framework/angular/reference/interfaces/createqueryoptions.md b/docs/zh-hant/framework/angular/reference/interfaces/createqueryoptions.md new file mode 100644 index 00000000000..0b90e4cdc6d --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/createqueryoptions.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.718Z' +id: CreateQueryOptions +title: CreateQueryOptions +--- + +# Interface: CreateQueryOptions\ + +## Extends + +- `OmitKeyof`\<[`CreateBaseQueryOptions`](createbasequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`\>, `"suspense"`\> + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` diff --git a/docs/zh-hant/framework/angular/reference/interfaces/injectmutationstateoptions.md b/docs/zh-hant/framework/angular/reference/interfaces/injectmutationstateoptions.md new file mode 100644 index 00000000000..62d4ed91787 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/interfaces/injectmutationstateoptions.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.706Z' +id: InjectMutationStateOptions +title: InjectMutationStateOptions +--- + +# Interface: InjectMutationStateOptions + +## Properties + +### injector? + +```ts +optional injector: Injector; +``` + +#### Defined in + +[inject-mutation-state.ts:43](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-mutation-state.ts#L43) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createbasemutationresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/createbasemutationresult.md new file mode 100644 index 00000000000..66601115506 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createbasemutationresult.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.694Z' +id: CreateBaseMutationResult +title: CreateBaseMutationResult +--- + +# Type Alias: CreateBaseMutationResult\ + +```ts +type CreateBaseMutationResult: Override, object> & object; +``` + +## Type declaration + +### mutateAsync + +```ts +mutateAsync: CreateMutateAsyncFunction +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Defined in + +[types.ts:198](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L198) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createbasequeryresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/createbasequeryresult.md new file mode 100644 index 00000000000..28d65de596b --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createbasequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.682Z' +id: CreateBaseQueryResult +title: CreateBaseQueryResult +--- + +# Type Alias: CreateBaseQueryResult\ + +```ts +type CreateBaseQueryResult: BaseQueryNarrowing & MapToSignals>; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TState** = `QueryObserverResult`\<`TData`, `TError`\> + +## Defined in + +[types.ts:116](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L116) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createinfinitequeryresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/createinfinitequeryresult.md new file mode 100644 index 00000000000..cd3231ca202 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createinfinitequeryresult.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.669Z' +id: CreateInfiniteQueryResult +title: CreateInfiniteQueryResult +--- + +# Type Alias: CreateInfiniteQueryResult\ + +```ts +type CreateInfiniteQueryResult: MapToSignals>; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[types.ts:143](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L143) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createmutateasyncfunction.md b/docs/zh-hant/framework/angular/reference/type-aliases/createmutateasyncfunction.md new file mode 100644 index 00000000000..d19b6a4f1ea --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createmutateasyncfunction.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.657Z' +id: CreateMutateAsyncFunction +title: CreateMutateAsyncFunction +--- + +# Type Alias: CreateMutateAsyncFunction\ + +```ts +type CreateMutateAsyncFunction: MutateFunction; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Defined in + +[types.ts:188](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L188) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createmutatefunction.md b/docs/zh-hant/framework/angular/reference/type-aliases/createmutatefunction.md new file mode 100644 index 00000000000..24cb45f5c76 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createmutatefunction.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.644Z' +id: CreateMutateFunction +title: CreateMutateFunction +--- + +# Type Alias: CreateMutateFunction()\ + +```ts +type CreateMutateFunction: (...args) => void; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Parameters + +• ...**args**: `Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TContext`\>\> + +## Returns + +`void` + +## Defined in + +[types.ts:176](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L176) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createmutationresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/createmutationresult.md new file mode 100644 index 00000000000..b1c07ea4a74 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createmutationresult.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.632Z' +id: CreateMutationResult +title: CreateMutationResult +--- + +# Type Alias: CreateMutationResult\ + +```ts +type CreateMutationResult: BaseMutationNarrowing & MapToSignals>; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +• **TState** = `CreateStatusBasedMutationResult`\<[`CreateBaseMutationResult`](createbasemutationresult.md)\[`"status"`\], `TData`, `TError`, `TVariables`, `TContext`\> + +## Defined in + +[types.ts:292](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L292) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/createqueryresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/createqueryresult.md new file mode 100644 index 00000000000..15aec3f6226 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/createqueryresult.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.619Z' +id: CreateQueryResult +title: CreateQueryResult +--- + +# Type Alias: CreateQueryResult\ + +```ts +type CreateQueryResult: CreateBaseQueryResult; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[types.ts:126](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L126) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md new file mode 100644 index 00000000000..d9daf1e91d7 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/definedcreateinfinitequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.606Z' +id: DefinedCreateInfiniteQueryResult +title: DefinedCreateInfiniteQueryResult +--- + +# Type Alias: DefinedCreateInfiniteQueryResult\ + +```ts +type DefinedCreateInfiniteQueryResult: MapToSignals; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TDefinedInfiniteQueryObserver** = `DefinedInfiniteQueryObserverResult`\<`TData`, `TError`\> + +## Defined in + +[types.ts:151](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L151) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/definedcreatequeryresult.md b/docs/zh-hant/framework/angular/reference/type-aliases/definedcreatequeryresult.md new file mode 100644 index 00000000000..42b91a345e7 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/definedcreatequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.594Z' +id: DefinedCreateQueryResult +title: DefinedCreateQueryResult +--- + +# Type Alias: DefinedCreateQueryResult\ + +```ts +type DefinedCreateQueryResult: MapToSignals; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TDefinedQueryObserver** = `DefinedQueryObserverResult`\<`TData`, `TError`\> + +## Defined in + +[types.ts:134](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L134) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md b/docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md new file mode 100644 index 00000000000..29ec54cefac --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdatainfiniteoptions.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.582Z' +id: DefinedInitialDataInfiniteOptions +title: DefinedInitialDataInfiniteOptions +--- + +# Type Alias: DefinedInitialDataInfiniteOptions\ + +```ts +type DefinedInitialDataInfiniteOptions: CreateInfiniteQueryOptions & object; +``` + +## Type declaration + +### initialData + +```ts +initialData: NonUndefinedGuard> | () => NonUndefinedGuard>; +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `DefaultError` + +• **TData** = `InfiniteData`\<`TQueryFnData`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Defined in + +[infinite-query-options.ts:32](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L32) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdataoptions.md b/docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdataoptions.md new file mode 100644 index 00000000000..9b04e8f8f3a --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/definedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.570Z' +id: DefinedInitialDataOptions +title: DefinedInitialDataOptions +--- + +# Type Alias: DefinedInitialDataOptions\ + +```ts +type DefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData + +```ts +initialData: NonUndefinedGuard | () => NonUndefinedGuard; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[query-options.ts:19](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L19) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/nonundefinedguard.md b/docs/zh-hant/framework/angular/reference/type-aliases/nonundefinedguard.md new file mode 100644 index 00000000000..6eef4a6a55f --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/nonundefinedguard.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.557Z' +id: NonUndefinedGuard +title: NonUndefinedGuard +--- + +# Type Alias: NonUndefinedGuard\ + +```ts +type NonUndefinedGuard: T extends undefined ? never : T; +``` + +## Type Parameters + +• **T** + +## Defined in + +[types.ts:316](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/types.ts#L316) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/queriesoptions.md b/docs/zh-hant/framework/angular/reference/type-aliases/queriesoptions.md new file mode 100644 index 00000000000..3791ed0902c --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/queriesoptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.545Z' +id: QueriesOptions +title: QueriesOptions +--- + +# Type Alias: QueriesOptions\ + +```ts +type QueriesOptions: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResult, GetOptions] : T extends [infer Head, ...(infer Tail)] ? QueriesOptions<[...Tail], [...TResult, GetOptions], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; +``` + +QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResult** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[inject-queries.ts:108](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-queries.ts#L108) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/queriesresults.md b/docs/zh-hant/framework/angular/reference/type-aliases/queriesresults.md new file mode 100644 index 00000000000..2a21cf25658 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/queriesresults.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.533Z' +id: QueriesResults +title: QueriesResults +--- + +# Type Alias: QueriesResults\ + +```ts +type QueriesResults: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResult, GetResults] : T extends [infer Head, ...(infer Tail)] ? QueriesResults<[...Tail], [...TResult, GetResults], [...TDepth, 1]> : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverResult[] : QueryObserverResult[]; +``` + +QueriesResults reducer recursively maps type param to results + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResult** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[inject-queries.ts:151](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/inject-queries.ts#L151) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md b/docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md new file mode 100644 index 00000000000..fbd7b7ac8d1 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdatainfiniteoptions.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.521Z' +id: UndefinedInitialDataInfiniteOptions +title: UndefinedInitialDataInfiniteOptions +--- + +# Type Alias: UndefinedInitialDataInfiniteOptions\ + +```ts +type UndefinedInitialDataInfiniteOptions: CreateInfiniteQueryOptions & object; +``` + +## Type declaration + +### initialData? + +```ts +optional initialData: undefined; +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `DefaultError` + +• **TData** = `InfiniteData`\<`TQueryFnData`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Defined in + +[infinite-query-options.ts:12](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/infinite-query-options.ts#L12) diff --git a/docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md b/docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md new file mode 100644 index 00000000000..8f449e581d0 --- /dev/null +++ b/docs/zh-hant/framework/angular/reference/type-aliases/undefinedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.508Z' +id: UndefinedInitialDataOptions +title: UndefinedInitialDataOptions +--- + +# Type Alias: UndefinedInitialDataOptions\ + +```ts +type UndefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData? + +```ts +optional initialData: undefined; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[query-options.ts:7](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/angular-query-experimental/src/query-options.ts#L7) diff --git a/docs/zh-hant/framework/angular/typescript.md b/docs/zh-hant/framework/angular/typescript.md new file mode 100644 index 00000000000..8f205dcc1ce --- /dev/null +++ b/docs/zh-hant/framework/angular/typescript.md @@ -0,0 +1,293 @@ +--- +source-updated-at: '2024-11-20T12:58:00.000Z' +translation-updated-at: '2025-05-08T20:18:19.369Z' +id: typescript +title: TypeScript +--- + +TanStack Query 現已採用 **TypeScript** 編寫,以確保函式庫與您的專案具備型別安全! + +注意事項: + +- 目前型別系統要求使用 TypeScript **v4.7** 或更高版本 +- 此儲存庫中的型別變更視為**非破壞性變更**,通常以 **patch** 版號發佈(否則每個型別增強都會導致主版號變更!) +- **強烈建議您將 angular-query-experimental 套件版本鎖定至特定 patch 版本**,並在升級時預期型別可能在任何版本間被修正或升級 +- TanStack Query 的非型別相關公開 API(以及實驗階段後的 angular-query 套件)仍嚴格遵循語意化版本控制 + +## 型別推論 + +TanStack Query 的型別通常能良好流動,因此您無需自行添加型別註解 + +```angular-ts +@Component({ + // ... + template: `@let data = query.data();`, + // ^? data: number | undefined +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + })) +} +``` + +```angular-ts +@Component({ + // ... + template: `@let data = query.data();`, + // ^? data: string | undefined +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), + })) +} +``` + +若您的 `queryFn` 有明確定義的返回型別,此機制效果最佳。請注意,多數資料獲取函式庫預設返回 `any`,因此請確保將其提取至正確型別的函式。 + +此範例中,我們將 Group[] 傳遞給 HttpClient `get` 方法的型別參數: + +```angular-ts +@Component({ + template: `@let data = query.data();`, + // ^? data: Group[] | undefined +}) +class MyComponent { + http = inject(HttpClient) + + query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: () => lastValueFrom(this.http.get('/groups')), + })) +} +``` + +## 型別縮窄 + +TanStack Query 使用[可辨識聯合型別 (discriminated union type)](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) 作為查詢結果,透過 `status` 欄位與衍生的狀態布林標記進行辨識。這讓您能檢查例如 `isSuccess()` 狀態來確保 `data` 已定義: + +```angular-ts +@Component({ + // ... + template: ` + @if (query.isSuccess()) { + @let data = query.data(); + // ^? data: number + } + `, +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + })) +} +``` + +> TypeScript 目前不支援物件方法上的可辨識聯合型別。在物件(如查詢結果)上的訊號欄位縮窄僅適用於返回布林值的訊號。建議優先使用 `isSuccess()` 等布林狀態訊號,而非 `status() === 'success'`。 + +## 錯誤欄位型別 + +錯誤型別預設為 `Error`,因這符合多數使用者的預期: + +```angular-ts +@Component({ + // ... + template: `@let error = query.error();`, + // ^? error: Error | null +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups + })) +} +``` + +若想拋出自訂錯誤或非 `Error` 的內容,可指定錯誤欄位的型別: + +```angular-ts +@Component({ + // ... + template: `@let error = query.error();`, + // ^? error: string | null +}) +class MyComponent { + query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, + })) +} +``` + +但此做法有缺點:`injectQuery` 的其他泛型參數將無法進行型別推論。通常不建議拋出非 `Error` 的內容,若您有像 `AxiosError` 的子類別,可使用**型別縮窄**使錯誤欄位更明確: + +```ts +import axios from 'axios' + +query = injectQuery(() => ({ queryKey: ['groups'], queryFn: fetchGroups })) + +computed(() => { + const error = query.error() + // ^? error: Error | null + + if (axios.isAxiosError(error)) { + error + // ^? const error: AxiosError + } +}) +``` + +### 註冊全域錯誤型別 + +TanStack Query v5 允許設定全域錯誤型別,無需在呼叫端指定泛型,透過擴充 `Register` 介面實現。這能確保型別推論仍有效,同時錯誤欄位會是指定型別: + +```ts +import '@tanstack/angular-query-experimental' + +declare module '@tanstack/angular-query-experimental' { + interface Register { + defaultError: AxiosError + } +} + +const query = injectQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +computed(() => { + const error = query.error() + // ^? error: AxiosError | null +}) +``` + +## 型別化 meta + +### 註冊全域 Meta + +類似註冊[全域錯誤型別](#registering-a-global-error),您也可註冊全域 `Meta` 型別。這確保[查詢](./reference/injectQuery.md)與[變異](./reference/injectMutation.md)上的選填 `meta` 欄位保持一致且型別安全。注意註冊的型別必須擴展 `Record`,以維持 `meta` 為物件的特性。 + +```ts +import '@tanstack/angular-query-experimental' + +interface MyMeta extends Record { + // 您的 meta 型別定義 +} + +declare module '@tanstack/angular-query-experimental' { + interface Register { + queryMeta: MyMeta + mutationMeta: MyMeta + } +} +``` + +## 型別化查詢與變異鍵 + +### 註冊查詢與變異鍵型別 + +同樣類似註冊[全域錯誤型別](#registering-a-global-error),您可註冊全域 `QueryKey` 與 `MutationKey` 型別。這讓您能為鍵提供更符合應用層級的結構化型別,並在函式庫所有相關功能中保持型別化。注意註冊的型別必須擴展 `Array` 型別,以維持鍵為陣列的特性。 + +```ts +import '@tanstack/angular-query-experimental' + +type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray] + +declare module '@tanstack/angular-query-experimental' { + interface Register { + queryKey: QueryKey + mutationKey: QueryKey + } +} +``` + +## 型別化查詢選項 + +若將查詢選項直接內嵌至 `injectQuery`,會獲得自動型別推論。但您可能想將查詢選項提取至獨立函式,以便在 `injectQuery` 與如 `prefetchQuery` 之間共享,或在服務中管理。此時會失去型別推論,可透過 `queryOptions` 輔助工具恢復: + +```ts +@Injectable({ + providedIn: 'root', +}) +export class QueriesService { + private http = inject(HttpClient) + + post(postId: number) { + return queryOptions({ + queryKey: ['post', postId], + queryFn: () => { + return lastValueFrom( + this.http.get( + `https://jsonplaceholder.typicode.com/posts/${postId}`, + ), + ) + }, + }) + } +} + +@Component({ + // ... +}) +export class Component { + queryClient = inject(QueryClient) + + postId = signal(1) + + queries = inject(QueriesService) + optionsSignal = computed(() => this.queries.post(this.postId())) + + postQuery = injectQuery(() => this.queries.post(1)) + postQuery = injectQuery(() => this.queries.post(this.postId())) + + // 也可傳遞返回查詢選項的訊號 + postQuery = injectQuery(this.optionsSignal) + + someMethod() { + this.queryClient.prefetchQuery(this.queries.post(23)) + } +} +``` + +此外,`queryOptions` 返回的 `queryKey` 會知道關聯的 `queryFn`,我們可利用此型別資訊讓如 `queryClient.getQueryData` 等方法也能感知這些型別: + +```ts +data = this.queryClient.getQueryData(groupOptions().queryKey) +// ^? data: Post | undefined +``` + +若無 `queryOptions`,資料型別會是 unknown,除非傳遞型別參數: + +```ts +data = queryClient.getQueryData(['post', 1]) +``` + +## 型別化變異選項 + +類似 `queryOptions`,您可使用 `mutationOptions` 將變異選項提取至獨立函式: + +```ts +export class QueriesService { + private http = inject(HttpClient) + + updatePost(id: number) { + return mutationOptions({ + mutationFn: (post: Post) => Promise.resolve(post), + mutationKey: ['updatePost', id], + onSuccess: (newPost) => { + // ^? newPost: Post + this.queryClient.setQueryData(['posts', id], newPost) + }, + }) + } +} +``` + +## 使用 `skipToken` 安全停用查詢 + +若使用 TypeScript,可使用 `skipToken` 停用查詢。這適用於需根據條件停用查詢,但仍希望保持查詢型別安全的場景。詳見[停用查詢](./guides/disabling-queries.md)指南。 diff --git a/docs/zh-hant/framework/angular/zoneless.md b/docs/zh-hant/framework/angular/zoneless.md new file mode 100644 index 00000000000..5418fe55147 --- /dev/null +++ b/docs/zh-hant/framework/angular/zoneless.md @@ -0,0 +1,13 @@ +--- +source-updated-at: '2024-06-20T22:57:31.000Z' +translation-updated-at: '2025-05-08T20:16:52.716Z' +id: zoneless +title: 無區域 (Zoneless) +--- + +由於 TanStack Query 的 Angular 適配器是基於 signals 構建的,因此它完全支援無區域 (Zoneless) 模式! + +無區域 (Zoneless) 的優勢包括提升效能與改善除錯體驗。詳情請參閱 [Angular 文件](https://angular.dev/guide/experimental/zoneless)。 + +> 請注意,Angular 無區域 (Zoneless) 的 API 目前仍處於實驗階段,可能在 Angular 的修訂版本中變更。 +> 除了無區域 (Zoneless) 模式外,也完全支援 ZoneJS 變更偵測機制。 diff --git a/docs/zh-hant/framework/react/community/community-projects.md b/docs/zh-hant/framework/react/community/community-projects.md new file mode 100644 index 00000000000..21babfc04df --- /dev/null +++ b/docs/zh-hant/framework/react/community/community-projects.md @@ -0,0 +1,161 @@ +--- +source-updated-at: '2025-03-30T12:50:39.000Z' +translation-updated-at: '2025-05-08T20:21:02.174Z' +id: community-projects +title: 社群專案 +--- + +有許多社群專案建立在 React Query 之上,並利用它來提供額外功能或增強開發者體驗。以下專案按字母順序排列。如果您有專案想加入此列表,請提交 PR! + +> 請注意,這些專案完全由社群維護。如有相關問題,請直接聯繫專案維護者。 + +## Atomic CRM + +一個功能完整的 CRM 系統,基於 React、react-admin 和 Supabase 構建。 + +連結: https://marmelab.com/atomic-crm/ + +## batshit + +批次管理工具,可將特定時間窗口內對相同資料類型的請求進行去重和批次處理 + +連結: https://github.com/yornaath/batshit + +## Blitz + +Next.js 的全端工具套件 + +連結: https://blitzjs.com/ + +## Connect + +一套用於構建瀏覽器相容與 gRPC 相容 HTTP API 的函式庫系列 + +連結: https://connectrpc.com/docs + +## GraphQL Code Generator + +從 GraphQL 結構描述生成 React Query hooks + +連結: https://the-guild.dev/graphql/codegen + +## Http-wizard + +具備 TypeScript 魔法 ✨ 的端到端類型安全 Fastify API + +連結: https://http-wizard.com + +## Kubb + +為所有 API 生成 SDK + +連結: https://www.kubb.dev/ + +## NgQuery + +Angular 適用的查詢轉接層 + +連結: https://ngneat.github.io/query/ + +## Normy + +為資料獲取函式庫提供自動化正規化與資料更新功能 + +連結: https://github.com/klis87/normy + +## OpenAPI codegen + +基於 OpenAPI 結構描述生成程式碼的工具 + +連結: https://github.com/fabien0102/openapi-codegen + +## OpenAPI Qraft React + +直接從 OpenAPI 文件生成類型安全的 API 客戶端與 TanStack Query Hooks。 +零運行時開銷、基於 Proxy 的設計、無縫 SSR 支援,完整 TanStack Query 功能。 + +連結: https://github.com/OpenAPI-Qraft/openapi-qraft + +## OpenAPI React Query codegen + +基於 OpenAPI 規範文件生成 TanStack Query hooks + +連結: https://github.com/7nohe/openapi-react-query-codegen + +### OpenAPI zod client + +從 OpenAPI 規範生成 zodios 客戶端 + +連結: https://github.com/astahmer/openapi-zod-client + +## openapi-fetch + +僅 2KB 的類型安全 fetch 封裝,使用靜態 TypeScript 類型推斷且無需運行時檢查 + +連結: https://openapi-ts.dev/openapi-react-query/ + +## Orval + +從 OpenAPI 規範生成 TypeScript 客戶端 + +連結: https://orval.dev/ + +## Query Key factory + +用於創建類型安全標準化查詢鍵的函式庫,適用於 `@tanstack/query` 的快取管理 + +連結: https://github.com/lukemorales/query-key-factory + +## Rapini + +🥬 OpenAPI 轉 React Query (或 SWR) & Axios 工具 + +連結: https://github.com/rametta/rapini + +## React Query Kit + +🕊️ 讓 ReactQuery hooks 可複用且類型安全的工具套件 + +連結: https://github.com/liaoliao666/react-query-kit + +## React Query Rewind + +在開發過程中進行狀態時光旅行與視覺化 + +連結: https://reactqueryrewind.com/ + +## React Query Swagger + +基於 Swagger API 定義生成 React Query hooks + +連結: https://github.com/Shaddix/react-query-swagger + +## Suspensive React Query + +為 React Query 增強 Suspense 支援,實現更簡單且宣告式的資料獲取 + +連結: https://suspensive.org/docs/react-query/motivation + +## tRPC + +輕鬆實現端到端類型安全的 API + +連結: https://trpc.io/ + +## ts-rest + +為新舊 API 提供可漸進採用的類型安全方案 + +連結: https://ts-rest.com/ + +## wagmi + +基於 `@tanstack/react-query` 的 Ethereum React Hooks + +連結: https://wagmi.sh/ + +## zodios + +端到端類型安全的 REST API 工具箱 + +連結: https://www.zodios.org/ diff --git a/docs/zh-hant/framework/react/community/tkdodos-blog.md b/docs/zh-hant/framework/react/community/tkdodos-blog.md new file mode 100644 index 00000000000..e21296087cd --- /dev/null +++ b/docs/zh-hant/framework/react/community/tkdodos-blog.md @@ -0,0 +1,88 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:21:39.068Z' +id: tkdodos-blog +title: TkDodo 的部落格 +--- + +TanStack Query 的維護者 [TkDodo](https://bsky.app/profile/tkdodo.eu) 撰寫了一系列關於使用該函式庫的部落格文章。部分文章展示了通用的最佳實踐,但大多數都帶有鮮明的個人觀點。 + +## [#1: 實用 React Query](https://tkdodo.eu/blog/practical-react-query) + +> 這是一篇進階的 React Query 介紹,展示了超越官方文件的實用技巧。內容涵蓋解釋預設值(`staleTime` 與 `gcTime`)、保持伺服器與客戶端狀態分離的概念、處理依賴項與創建自訂 Hook,以及說明為何 `enabled` 選項非常強大。[閱讀更多...](https://tkdodo.eu/blog/practical-react-query) + +## [#2: React Query 資料轉換](https://tkdodo.eu/blog/react-query-data-transformations) + +> 學習如何使用 React Query 執行常見且重要的資料轉換任務。從在 `queryFn` 中轉換到使用 `select` 選項,本文概述了各種不同方法的優缺點。[閱讀更多...](https://tkdodo.eu/blog/react-query-data-transformations) + +## [#3: React Query 渲染優化](https://tkdodo.eu/blog/react-query-render-optimizations) + +> 讓我們看看當使用 React Query 時,若元件過於頻繁重新渲染該如何處理。該函式庫本身已經相當優化,但仍有一些可選功能(如 `tracked queries`)可用來避免 `isFetching` 過渡。我們也將探討 `structural sharing` 的含義。[閱讀更多...](https://tkdodo.eu/blog/react-query-render-optimizations) + +## [#4: React Query 中的狀態檢查](https://tkdodo.eu/blog/status-checks-in-react-query) + +> 我們通常會先檢查 `isPending` 再檢查 `isError`,但有時應優先檢查 `data` 是否可用。本文展示錯誤的狀態檢查順序如何對使用者體驗產生負面影響。[閱讀更多...](https://tkdodo.eu/blog/status-checks-in-react-query) + +## [#5: 測試 React Query](https://tkdodo.eu/blog/testing-react-query) + +> 官方文件已涵蓋了測試 React Query 所需的基礎知識。本文提供了一些額外技巧(如關閉 `retries` 或靜默 `console`),適用於測試自訂 Hook 或使用它們的元件。文中還連結了一個[範例儲存庫](https://github.com/TkDodo/testing-react-query),包含成功與錯誤狀態的測試案例,使用 `mock-service-worker` 驅動。[閱讀更多...](https://tkdodo.eu/blog/testing-react-query) + +## [#6: React Query 與 TypeScript](https://tkdodo.eu/blog/react-query-and-type-script) + +> 由於 React Query 本身使用 TypeScript 編寫,因此對其有極佳的支持。這篇部落格文章解釋了各種泛型、如何利用型別推論避免顯式標註 `useQuery` 等函式、如何處理 `unknown` 錯誤、型別縮窄如何運作等內容![閱讀更多...](https://tkdodo.eu/blog/react-query-and-type-script) + +## [#7: 在 React Query 中使用 WebSockets](https://tkdodo.eu/blog/using-web-sockets-with-react-query) + +> 逐步指南,教你如何透過事件訂閱或直接推送完整資料到客戶端,實現 React Query 的即時通知功能。適用於瀏覽器原生 WebSocket API、Firebase 甚至 GraphQL 訂閱等場景。[閱讀更多...](https://tkdodo.eu/blog/using-web-sockets-with-react-query) + +## [#8: 有效的 React Query 鍵值設計](https://tkdodo.eu/blog/effective-react-query-keys) + +> 多數範例僅使用簡單的字串或陣列作為查詢鍵值,但當應用程式超越待辦事項清單規模後,該如何有效組織鍵值?本文展示如何透過共置與查詢鍵值工廠簡化工作。[閱讀更多...](https://tkdodo.eu/blog/effective-react-query-keys) + +## [#8a: 活用查詢函式上下文](https://tkdodo.eu/blog/leveraging-the-query-function-context) + +> 這是對前一篇部落格文章的補充,探討如何隨著應用程式成長,利用查詢函式上下文與物件查詢鍵值實現最高安全性。[閱讀更多...](https://tkdodo.eu/blog/leveraging-the-query-function-context) + +## [#9: React Query 中的預留位置與初始資料](https://tkdodo.eu/blog/placeholder-and-initial-data-in-react-query) + +> 預留位置與初始資料是兩個相似但不同的概念,都能同步顯示資料而非載入動畫以提升應用程式 UX。本文比較兩者並說明各自適用的場景。[閱讀更多...](https://tkdodo.eu/blog/placeholder-and-initial-data-in-react-query) + +## [#10: 將 React Query 作為狀態管理器](https://tkdodo.eu/blog/react-query-as-a-state-manager) + +> React Query 不會替你獲取資料——它是一個資料同步工具,特別擅長處理伺服器狀態。本文包含將 React Query 作為非同步狀態單一來源所需的一切知識。你將學習如何讓 React Query 發揮魔力,以及為何自訂 `staleTime` 可能就是你需要的全部。[閱讀更多...](https://tkdodo.eu/blog/react-query-as-a-state-manager) + +## [#11: React Query 錯誤處理](https://tkdodo.eu/blog/react-query-error-handling) + +> 處理錯誤是處理非同步資料(尤其是資料獲取)不可或缺的部分。我們必須面對現實:並非所有請求都會成功,也並非所有 Promise 都會實現。這篇部落格文章描述了 React Query 中應對錯誤的多種方式,如錯誤屬性、使用錯誤邊界或 onError 回調,讓你能為「出了問題」的情況做好準備。[閱讀更多...](https://tkdodo.eu/blog/react-query-error-handling) + +## [#12: 精通 React Query 變異](https://tkdodo.eu/blog/mastering-mutations-in-react-query) + +> 變異是處理伺服器資料時不可或缺的第二部分——用於需要更新資料的情境。這篇部落格文章涵蓋了什麼是變異及其與查詢的差異。你將學習 `mutate` 與 `mutateAsync` 的區別,以及如何將查詢與變異綁定在一起。[閱讀更多...](https://tkdodo.eu/blog/mastering-mutations-in-react-query) + +## [#13: 離線狀態下的 React Query](https://tkdodo.eu/blog/offline-react-query) + +> 產生 Promise 的方式有很多——這也是 React Query 所需的全部——但最常見的用例無疑是資料獲取。這通常需要活躍的網路連線。但有時,特別是在網路可能不穩定的行動裝置上,你的應用程式也需要在離線時運作。本文將介紹 React Query 提供的不同離線策略。[閱讀更多...](https://tkdodo.eu/blog/offline-react-query) + +## [#14: React Query 與表單](https://tkdodo.eu/blog/react-query-and-forms) + +> 表單往往模糊了伺服器狀態與客戶端狀態的界線。在多數應用程式中,我們不僅想顯示狀態,還希望讓使用者與之互動。本文展示了兩種不同方法,以及一些將 React Query 與表單結合使用的技巧。[閱讀更多...](https://tkdodo.eu/blog/react-query-and-forms) + +## [#15: React Query 常見問題](https://tkdodo.eu/blog/react-query-fa-qs) + +> 本文試圖回答關於 React Query 最常見的問題。[閱讀更多...](https://tkdodo.eu/blog/react-query-fa-qs) + +## [#16: React Query 與 React Router 的結合](https://tkdodo.eu/blog/react-query-meets-react-router) + +> Remix 和 React Router 正在改變我們對「何時獲取資料」的思考方式。本文深入探討為何 React Query 與支援資料載入的路由器是天作之合。[閱讀更多...](https://tkdodo.eu/blog/react-query-meets-react-router) + +## [#17: 預填查詢快取](https://tkdodo.eu/blog/seeding-the-query-cache) + +> 這篇部落格文章展示了多種在渲染開始前將資料存入查詢快取的方法,以最小化應用程式中顯示的載入動畫數量。選項範圍涵蓋從伺服器端預取、在路由器中預取到透過 `setQueryData` 預填快取項目。[閱讀更多...](https://tkdodo.eu/blog/seeding-the-query-cache) + +## [#18: React Query 內部機制](https://tkdodo.eu/blog/inside-react-query) + +> 如果你曾好奇 React Query 的底層運作原理——這篇文章就是為你準備的。它解釋了架構(包含視覺化圖表),從框架無關的 Query Core 開始,到它如何與框架特定適配器溝通。[閱讀更多...](https://tkdodo.eu/blog/inside-react-query) + +## [#19: 型別安全的 React Query](https://tkdodo.eu/blog/type-safe-react-query) + +> 「擁有型別」與「具備型別安全」之間存在巨大差異。本文試圖釐清這些差異,並展示如何在 TypeScript 中使用 React Query 獲得最佳的型別安全性。[閱讀更多...](https://tkdodo.eu/blog/type-safe-react-query) diff --git a/docs/zh-hant/framework/react/comparison.md b/docs/zh-hant/framework/react/comparison.md new file mode 100644 index 00000000000..3819d5ba872 --- /dev/null +++ b/docs/zh-hant/framework/react/comparison.md @@ -0,0 +1,113 @@ +--- +source-updated-at: '2024-07-11T12:20:14.000Z' +translation-updated-at: '2025-05-08T20:18:52.803Z' +id: comparison +title: 比較 +--- + +> 本比較表力求準確且不偏頗。若您使用過其中任何函式庫並認為資訊可改進,歡迎透過頁面底部的「在 Github 上編輯此頁」連結提出修改建議(需附上說明或佐證依據)。 + +功能/能力對照鍵: + +- ✅ 一級支援、內建且無需額外配置或程式碼即可使用 +- 🟡 支援,但屬於非官方的第三方或社群函式庫/貢獻 +- 🔶 支援並有文件說明,但需使用者自行編寫額外程式碼來實現 +- 🛑 無官方支援或文件記載 + +| | React Query | SWR [_(官網)_][swr] | Apollo Client [_(官網)_][apollo] | RTK-Query [_(官網)_][rtk-query] | React Router [_(官網)_][react-router] | +| --------------------------------- | ---------------------------------------- | -------------------------------- | ---------------------------------- | ------------------------------------ | ------------------------------------------------------------------------- | +| Github 儲存庫 / 星數 | [![][stars-react-query]][gh-react-query] | [![][stars-swr]][gh-swr] | [![][stars-apollo]][gh-apollo] | [![][stars-rtk-query]][gh-rtk-query] | [![][stars-react-router]][gh-react-router] | +| 平台需求 | React | React | React, GraphQL | Redux | React | +| 官方比較文件 | | (無) | (無) | [比較][rtk-query-comparison] | (無) | +| 支援的查詢語法 | Promise, REST, GraphQL | Promise, REST, GraphQL | GraphQL, 任意 (Reactive Variables) | Promise, REST, GraphQL | Promise, REST, GraphQL | +| 支援的框架 | React | React | React + 其他 | 任意 | React | +| 快取策略 | 階層式鍵值對 (Hierarchical Key -> Value) | 唯一鍵值對 (Unique Key -> Value) | 正規化結構 (Normalized Schema) | 唯一鍵值對 (Unique Key -> Value) | 巢狀路由對應值 (Nested Route -> value) | +| 快取鍵生成策略 | JSON | JSON | GraphQL 查詢 | JSON | 路由路徑 (Route Path) | +| 快取變更偵測 | 深度比對鍵值 (穩定序列化) | 深度比對鍵值 (穩定序列化) | 深度比對鍵值 (不穩定序列化) | 鍵值參照相等性 (===) | 路由變更 | +| 資料變更偵測 | 深度比對 + 結構共享 | 深度比對 (透過 `stable-hash`) | 深度比對 (不穩定序列化) | 鍵值參照相等性 (===) | 載入器執行 (Loader Run) | +| 資料記憶化 | 完整結構共享 | 識別相等性 (===) | 正規化識別性 | 識別相等性 (===) | 識別相等性 (===) | +| 套件體積 | [![][bp-react-query]][bpl-react-query] | [![][bp-swr]][bpl-swr] | [![][bp-apollo]][bpl-apollo] | [![][bp-rtk-query]][bpl-rtk-query] | [![][bp-react-router]][bpl-react-router] + [![][bp-history]][bpl-history] | +| API 定義位置 | 元件內、外部配置檔 | 元件內 | GraphQL Schema | 外部配置檔 | 路由樹配置 | +| 查詢功能 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 快取持久化 | ✅ | ✅ | ✅ | ✅ | 🛑 僅當前路由有效 8 | +| 開發者工具 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 輪詢/間隔查詢 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 平行查詢 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 依賴查詢 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 分頁查詢 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 無限查詢 | ✅ | ✅ | ✅ | 🛑 | 🛑 | +| 雙向無限查詢 | ✅ | 🔶 | 🔶 | 🛑 | 🛑 | +| 無限查詢重新取得 | ✅ | ✅ | 🛑 | 🛑 | 🛑 | +| 滯後查詢資料1 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 選擇器 | ✅ | 🛑 | ✅ | ✅ | N/A | +| 初始資料 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 滾動位置恢復 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 快取操作 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 過時查詢自動棄置 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 渲染批次處理與優化2 | ✅ | ✅ | 🛑 | ✅ | ✅ | +| 自動垃圾回收 | ✅ | 🛑 | 🛑 | ✅ | N/A | +| 變異鉤子 | ✅ | ✅ | ✅ | ✅ | ✅ | +| 離線變異支援 | ✅ | 🛑 | 🟡 | 🛑 | 🛑 | +| 預取 API | ✅ | ✅ | ✅ | ✅ | ✅ | +| 查詢取消 | ✅ | 🛑 | 🛑 | 🛑 | ✅ | +| 部分查詢匹配3 | ✅ | 🔶 | ✅ | ✅ | N/A | +| 重新驗證時保留過期資料 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 過期時間配置 | ✅ | 🛑7 | 🛑 | ✅ | 🛑 | +| 預先配置查詢/變異行為4 | ✅ | 🛑 | ✅ | ✅ | ✅ | +| 視窗焦點時重新取得 | ✅ | ✅ | 🛑 | ✅ | 🛑 | +| 網路狀態變更時重新取得 | ✅ | ✅ | ✅ | ✅ | 🛑 | +| 通用快取脫水/再水合 | ✅ | 🛑 | ✅ | ✅ | ✅ | +| 離線快取 | ✅ | 🛑 | ✅ | 🔶 | 🛑 | +| React Suspense 支援 | ✅ | ✅ | ✅ | 🛑 | ✅ | +| 抽象化/框架無關核心 | ✅ | 🛑 | ✅ | ✅ | 🛑 | +| 變異後自動重新取得5 | 🔶 | 🔶 | ✅ | ✅ | ✅ | +| 正規化快取6 | 🛑 | 🛑 | ✅ | 🛑 | 🛑 | + +### 備註 + +> **1 滯後查詢資料** - React Query 提供了一種方式,讓您能在新查詢載入時繼續顯示現有查詢的資料(類似於 Suspense 未來將原生提供的 UX)。這在實作分頁 UI 或無限載入 UI 時極為重要,因為您不會希望每次新查詢請求時都顯示生硬的載入狀態。其他函式庫不具備此能力,會在新查詢載入時(除非已預先取得)顯示生硬的載入狀態。 + +> **2 渲染優化** - React Query 具有出色的渲染效能。預設情況下,它會自動追蹤哪些欄位被存取,並僅在這些欄位變更時重新渲染。若想關閉此優化,將 `notifyOnChangeProps` 設為 `'all'` 會讓元件在查詢更新時(例如有新資料或正在載入狀態)重新渲染。若只關注 `data` 或 `error` 屬性,可將 `notifyOnChangeProps` 設為 `['data', 'error']` 進一步減少渲染次數。 + +> **3 部分查詢匹配** - 由於 React Query 使用確定性的查詢鍵序列化,這讓您能操作一組變數查詢,而無需知道每個要匹配的具體查詢鍵。例如,您可以重新取得所有鍵以 `todos` 開頭的查詢(無論變數為何),或是針對帶有(或不帶)變數或巢狀屬性的特定查詢,甚至使用過濾函式只匹配符合特定條件的查詢。 + +> **4 預先配置查詢行為** - 這其實就是能在查詢和變異被使用前,預先配置其行為的進階功能。例如,查詢可事先完整配置預設值,使用時只需呼叫 `useQuery({ queryKey })`,而無需每次使用時都傳遞獲取函式或選項。SWR 透過允許配置全域預設獲取函式,部分支援此功能,但無法針對單一查詢或變異進行配置。 + +> **5 變異後自動重新取得** - 要真正在變異後自動重新取得資料,需具備結構描述(如 GraphQL 提供的)以及協助函式庫識別該結構描述中個體與個體類型的啟發式方法。 + +> **6 正規化快取** - React Query、SWR 和 RTK-Query 目前不支援自動正規化快取,該技術透過將個體儲存為扁平結構來避免高階資料重複。 + +> **7 SWR 的不可變模式** - SWR 提供「不可變」模式,允許查詢在快取生命週期內僅取得一次,但仍不具備過期時間概念或有條件的自動重新驗證功能。 + +> **8 React Router 的快取持久性** - React Router 不會快取超出當前匹配路由的資料。離開路由後,其資料即遺失。 + +[bpl-react-query]: https://bundlephobia.com/result?p=react-query +[bp-react-query]: https://badgen.net/bundlephobia/minzip/react-query?label=💾 +[gh-react-query]: https://github.com/tannerlinsley/react-query +[stars-react-query]: https://img.shields.io/github/stars/tannerlinsley/react-query?label=%F0%9F%8C%9F +[swr]: https://github.com/vercel/swr +[bp-swr]: https://badgen.net/bundlephobia/minzip/swr?label=💾 +[gh-swr]: https://github.com/vercel/swr +[stars-swr]: https://img.shields.io/github/stars/vercel/swr?label=%F0%9F%8C%9F +[bpl-swr]: https://bundlephobia.com/result?p=swr +[apollo]: https://github.com/apollographql/apollo-client +[bp-apollo]: https://badgen.net/bundlephobia/minzip/@apollo/client?label=💾 +[gh-apollo]: https://github.com/apollographql/apollo-client +[stars-apollo]: https://img.shields.io/github/stars/apollographql/apollo-client?label=%F0%9F%8C%9F +[bpl-apollo]: https://bundlephobia.com/result?p=@apollo/client +[rtk-query]: https://redux-toolkit.js.org/rtk-query/overview +[rtk-query-comparison]: https://redux-toolkit.js.org/rtk-query/comparison +[rtk-query-bundle-size]: https://redux-toolkit.js.org/rtk-query/comparison#bundle-size +[bp-rtk]: https://badgen.net/bundlephobia/minzip/@reduxjs/toolkit?label=💾 +[bp-rtk-query]: https://badgen.net/bundlephobia/minzip/@reduxjs/toolkit?label=💾 +[gh-rtk-query]: https://github.com/reduxjs/redux-toolkit +[stars-rtk-query]: https://img.shields.io/github/stars/reduxjs/redux-toolkit?label=🌟 +[bpl-rtk]: https://bundlephobia.com/result?p=@reduxjs/toolkit +[bpl-rtk-query]: https://bundlephobia.com/package/@reduxjs/toolkit +[react-router]: https://github.com/remix-run/react-router +[bp-react-router]: https://badgen.net/bundlephobia/minzip/react-router-dom?label=💾 +[gh-react-router]: https://github.com/remix-run/react-router +[stars-react-router]: https://img.shields.io/github/stars/remix-run/react-router?label=%F0%9F%8C%9F +[bpl-react-router]: https://bundlephobia.com/result?p=react-router-dom +[bp-history]: https://badgen.net/bundlephobia/minzip/history?label=💾 +[bpl-history]: https://bundlephobia.com/result?p=history diff --git a/docs/zh-hant/framework/react/devtools.md b/docs/zh-hant/framework/react/devtools.md new file mode 100644 index 00000000000..e3bde991517 --- /dev/null +++ b/docs/zh-hant/framework/react/devtools.md @@ -0,0 +1,197 @@ +--- +source-updated-at: '2024-11-04T06:38:47.000Z' +translation-updated-at: '2025-05-08T20:18:01.444Z' +id: devtools +title: 開發工具 +--- + +舉起雙手歡呼吧,因為 React Query 附帶了專用的開發者工具 (DevTools)! 🥳 + +當你開始使用 React Query 時,這些開發者工具將成為你的得力助手。它們能可視化 React Query 的所有內部運作,當你遇到棘手問題時,很可能為你節省數小時的除錯時間! + +> 請注意,目前開發者工具 **不支援 React Native**。如果你願意協助我們讓開發者工具跨平台通用,請告訴我們! + +> 好消息:我們現在為 React Native 提供了獨立的 React Query 開發者工具套件!這個新功能帶來了原生支援,讓你能直接將開發者工具整合到 React Native 專案中。查看並貢獻於此:[react-native-react-query-devtools](https://github.com/LovesWorking/react-native-react-query-devtools) + +> 另有一個外部工具可透過外部儀表板使用 React Query 開發者工具。詳情與貢獻請見:[react-query-external-sync](https://github.com/LovesWorking/react-query-external-sync) + +> 注意:自第 5 版起,開發者工具也支援觀察變異 (mutations)。 + +## 安裝與導入開發者工具 + +開發者工具是一個獨立的套件,需另行安裝: + +```bash +npm i @tanstack/react-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/react-query-devtools +``` + +或 + +```bash +yarn add @tanstack/react-query-devtools +``` + +或 + +```bash +bun add @tanstack/react-query-devtools +``` + +對於 Next 13+ 的 App 目錄,必須將其安裝為開發依賴 (dev dependency) 才能運作。 + +導入開發者工具的方式如下: + +```tsx +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +``` + +預設情況下,React Query 開發者工具僅在 `process.env.NODE_ENV === 'development'` 時包含在打包檔案中,因此無需擔心在生產環境建置時需排除它們。 + +## 浮動模式 (Floating Mode) + +浮動模式會將開發者工具作為一個固定的浮動元素掛載到你的應用中,並在畫面角落提供一個切換按鈕來顯示或隱藏開發者工具。此切換狀態會儲存在 localStorage 中,並在重新載入時記住。 + +將以下程式碼放在 React 應用的最上層,越靠近頁面根目錄效果越好! + +```tsx +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' + +function App() { + return ( + + {/* 應用程式的其餘部分 */} + + + ) +} +``` + +### 選項 + +- `initialIsOpen: Boolean` + - 設為 `true` 可讓開發者工具預設為開啟狀態 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` + - 預設為 `bottom-right` + - 設定 React Query 標誌按鈕的位置以開啟和關閉開發者工具面板 + - 若設為 `relative`,按鈕會放在你渲染開發者工具的位置。 +- `position?: "top" | "bottom" | "left" | "right"` + - 預設為 `bottom` + - 設定 React Query 開發者工具面板的位置 +- `client?: QueryClient`, + - 使用此選項可自訂 QueryClient,否則會使用最近上下文中的 QueryClient。 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}[]` + - 用於預定義可在查詢中觸發的錯誤。當從 UI 切換該錯誤時,初始化器(帶有特定查詢)會被呼叫。它必須回傳一個錯誤。 +- `styleNonce?: string` + - 用於傳遞 nonce 給添加到文件頭的 style 標籤。這在使用內容安全策略 (CSP) nonce 允許內聯樣式時很有用。 +- `shadowDOMTarget?: ShadowRoot` + - 預設行為會將開發者工具的樣式應用到 DOM 中的 head 標籤。 + - 使用此選項可傳遞 shadow DOM 目標給開發者工具,讓樣式在 shadow DOM 中生效,而非在 light DOM 的 head 標籤中。 + +## 嵌入模式 (Embedded Mode) + +嵌入模式會將開發者工具作為固定元素顯示在你的應用中,讓你能在自己的開發工具中使用我們的面板。 + +將以下程式碼放在 React 應用的最上層,越靠近頁面根目錄效果越好! + +```tsx +import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools' + +function App() { + const [isOpen, setIsOpen] = React.useState(false) + + return ( + + {/* 應用程式的其餘部分 */} + + {isOpen && setIsOpen(false)} />} + + ) +} +``` + +### 選項 + +- `style?: React.CSSProperties` + - 開發者工具面板的自訂樣式 + - 預設:`{ height: '500px' }` + - 範例:`{ height: '100%' }` + - 範例:`{ height: '100%', width: '100%' }` +- `onClose?: () => unknown` + - 開發者工具面板關閉時呼叫的回呼函式 +- `client?: QueryClient`, + - 使用此選項可自訂 QueryClient,否則會使用最近上下文中的 QueryClient。 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}[]` + - 用於預定義可在查詢中觸發的錯誤。當從 UI 切換該錯誤時,初始化器(帶有特定查詢)會被呼叫。它必須回傳一個錯誤。 +- `styleNonce?: string` + - 用於傳遞 nonce 給添加到文件頭的 style 標籤。這在使用內容安全策略 (CSP) nonce 允許內聯樣式時很有用。 +- `shadowDOMTarget?: ShadowRoot` + - 預設行為會將開發者工具的樣式應用到 DOM 中的 head 標籤。 + - 使用此選項可傳遞 shadow DOM 目標給開發者工具,讓樣式在 shadow DOM 中生效,而非在 light DOM 的 head 標籤中。 + +## 生產環境中的開發者工具 + +開發者工具在生產環境建置中會被排除。然而,你可能希望在生產環境中懶加載 (lazy load) 開發者工具: + +```tsx +import * as React from 'react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { Example } from './Example' + +const queryClient = new QueryClient() + +const ReactQueryDevtoolsProduction = React.lazy(() => + import('@tanstack/react-query-devtools/build/modern/production.js').then( + (d) => ({ + default: d.ReactQueryDevtools, + }), + ), +) + +function App() { + const [showDevtools, setShowDevtools] = React.useState(false) + + React.useEffect(() => { + // @ts-expect-error + window.toggleDevtools = () => setShowDevtools((old) => !old) + }, []) + + return ( + + + + {showDevtools && ( + + + + )} + + ) +} + +export default App +``` + +如此一來,呼叫 `window.toggleDevtools()` 就會下載開發者工具套件並顯示它們。 + +### 現代打包工具 + +如果你的打包工具支援套件匯出 (package exports),可以使用以下導入路徑: + +```tsx +const ReactQueryDevtoolsProduction = React.lazy(() => + import('@tanstack/react-query-devtools/production').then((d) => ({ + default: d.ReactQueryDevtools, + })), +) +``` + +對於 TypeScript,你需要在 tsconfig 中設定 `moduleResolution: 'nodenext'`,這至少需要 TypeScript v4.7。 diff --git a/docs/zh-hant/framework/react/graphql.md b/docs/zh-hant/framework/react/graphql.md new file mode 100644 index 00000000000..afdbde4ffb4 --- /dev/null +++ b/docs/zh-hant/framework/react/graphql.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:16:56.964Z' +id: graphql +title: GraphQL +--- + +由於 React Query 的資料獲取機制是基於 Promise 無關性 (agnostically) 建構的,你可以將 React Query 與任何非同步資料獲取客戶端搭配使用,包括 GraphQL! + +> 請注意,React Query 不支援正規化快取 (normalized caching)。雖然絕大多數使用者實際上並不需要正規化快取,甚至從中獲得的效益不如他們想像的那麼多,但在極少數情況下可能需要此功能,因此請務必先與我們確認這是否確實是您需要的功能! + +[//]: # 'Codegen' + +## 型別安全與程式碼生成 (Type-Safety and Code Generation) + +React Query 與 `graphql-request^5` 和 [GraphQL Code Generator](https://graphql-code-generator.com/) 結合使用時,可提供完整型別的 GraphQL 操作: + +```tsx +import request from 'graphql-request' +import { useQuery } from '@tanstack/react-query' + +import { graphql } from './gql/gql' + +const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ ` + query allFilmsWithVariablesQuery($first: Int!) { + allFilms(first: $first) { + edges { + node { + id + title + } + } + } + } +`) + +function App() { + // `data` 是完全型別化的! + const { data } = useQuery({ + queryKey: ['films'], + queryFn: async () => + request( + 'https://swapi-graphql.netlify.app/.netlify/functions/index', + allFilmsWithVariablesQueryDocument, + // 變數也會進行型別檢查! + { first: 10 }, + ), + }) + // ... +} +``` + +_你可以在 [專案儲存庫中找到完整範例](https://github.com/dotansimha/graphql-code-generator/tree/7c25c4eeb77f88677fd79da557b7b5326e3f3950/examples/front-end/react/tanstack-react-query)_ + +請參考 [GraphQL Code Generator 文件中的專用指南](https://www.the-guild.dev/graphql/codegen/docs/guides/react-vue) 開始使用。 + +[//]: # 'Codegen' diff --git a/docs/zh-hant/framework/react/guides/advanced-ssr.md b/docs/zh-hant/framework/react/guides/advanced-ssr.md new file mode 100644 index 00000000000..3f39b89df10 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/advanced-ssr.md @@ -0,0 +1,396 @@ +--- +source-updated-at: '2025-04-25T12:36:10.000Z' +translation-updated-at: '2025-05-08T20:26:59.263Z' +id: advanced-ssr +title: 進階伺服器渲染 +--- + +歡迎來到「進階伺服器渲染 (Advanced Server Rendering)」指南,你將在此學習如何將 React Query 與串流 (streaming)、伺服器元件 (Server Components) 及 Next.js 應用路由 (app router) 結合使用。 + +建議先閱讀 [伺服器渲染與水合 (Server Rendering & Hydration)](./ssr.md) 指南,該文介紹了 React Query 與 SSR 搭配的基本用法,此外 [效能與請求瀑布流 (Performance & Request Waterfalls)](./request-waterfalls.md) 和 [預取與路由整合 (Prefetching & Router Integration)](./prefetching.md) 也提供了寶貴的背景知識。 + +開始前請注意,雖然 SSR 指南中提到的 `initialData` 方法也適用於伺服器元件,但本指南將重點放在水合 API (hydration APIs) 的使用。 + +## 伺服器元件與 Next.js 應用路由 + +此處不會深入探討伺服器元件,簡而言之,它們是保證**僅在伺服器端執行**的元件,無論是初始頁面載入**或頁面轉換時**皆然。這類似於 Next.js 的 `getServerSideProps`/`getStaticProps` 和 Remix 的 `loader` 運作方式,這些也總是在伺服器端執行,但只能回傳資料,而伺服器元件則能實現更多功能。不過對 React Query 而言,資料處理仍是核心,因此我們將聚焦於此。 + +如何將伺服器渲染指南中學到的[將框架載入器中預取的資料傳遞至應用程式](./ssr.md#using-the-hydration-apis)應用於伺服器元件和 Next.js 應用路由?最簡單的理解方式是將伺服器元件視為「另一個」框架載入器。 + +### 術語簡要說明 + +截至目前,這些指南中我們一直在談論「伺服器」和「客戶端」。需注意的是,這與「伺服器元件」和「客戶端元件」並非完全對應。伺服器元件保證僅在伺服器端執行,但客戶端元件實際上可在兩端執行,原因是它們也能在初始的**伺服器渲染**階段渲染。 + +一種理解方式是:儘管伺服器元件也會「渲染」,但它們發生在「載入階段」(始終在伺服器端進行),而客戶端元件則在「應用階段」執行。該應用程式既能在 SSR 期間於伺服器端執行,也能在例如瀏覽器中執行。具體執行位置及是否在 SSR 期間執行,可能因框架而異。 + +### 初始設定 + +任何 React Query 設定的第一步總是建立 `queryClient` 並用 `QueryClientProvider` 包裹你的應用程式。對於伺服器元件,各框架的設定大致相同,僅檔案命名慣例有所不同: + +```tsx +// 在 Next.js 中,此檔案名為:app/providers.tsx +'use client' + +// 由於 QueryClientProvider 底層依賴 useContext,我們必須在頂部標記 'use client' +import { + isServer, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 時,通常需設定預設 staleTime + // 大於 0 以避免在客戶端立即重新獲取資料 + staleTime: 60 * 1000, + }, + }, + }) +} + +let browserQueryClient: QueryClient | undefined = undefined + +function getQueryClient() { + if (isServer) { + // 伺服器端:總是建立新的 query client + return makeQueryClient() + } else { + // 瀏覽器端:若尚未建立則建立新的 query client + // 這非常重要,避免在初始渲染時若 React 暫停導致重新建立 client + // 若在 query client 建立下方有暫停邊界 (suspense boundary),則可能不需此處理 + if (!browserQueryClient) browserQueryClient = makeQueryClient() + return browserQueryClient + } +} + +export default function Providers({ children }: { children: React.ReactNode }) { + // 注意:初始化 query client 時應避免使用 useState + // 除非在此與可能暫停的程式碼之間有暫停邊界 + // 因為若無邊界且初始渲染暫停,React 會丟棄該 client + const queryClient = getQueryClient() + + return ( + {children} + ) +} +``` + +```tsx +// 在 Next.js 中,此檔案名為:app/layout.tsx +import Providers from './providers' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + {children} + + + ) +} +``` + +這部分與 SSR 指南中的做法非常相似,僅需將內容拆分至兩個不同檔案。 + +### 預取與脫水/水合資料 + +接下來看看如何實際預取資料並進行脫水 (dehydrate) 和水合 (hydrate)。以下是使用 **Next.js 頁面路由 (pages router)** 的範例: + +```tsx +// pages/posts.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, + useQuery, +} from '@tanstack/react-query' + +// 此處也可使用 getServerSideProps +export async function getStaticProps() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} + +function Posts() { + // 此 useQuery 也可在 的更深層子元件中使用 + // 無論如何,資料都會立即可用 + // + // 注意此處使用 useQuery 而非 useSuspenseQuery + // 因為資料已預取,元件本身無需暫停 + // 若忘記或移除預取,則會在客戶端獲取資料 + // 而使用 useSuspenseQuery 會導致更糟的副作用 + const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts }) + + // 此查詢未在伺服器預取,將在客戶端開始獲取 + // 兩種模式可混合使用 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} + +export default function PostsRoute({ dehydratedState }) { + return ( + + + + ) +} +``` + +轉換至應用路由 (app router) 後看起來非常相似,僅需稍作調整。首先,我們建立一個伺服器元件來處理預取部分: + +```tsx +// app/posts/page.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Posts from './posts' + +export default async function PostsPage() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + // 很棒!序列化現在就像傳遞 props 一樣簡單 + // HydrationBoundary 是客戶端元件,水合將在此進行 + + + + ) +} +``` + +接著,客戶端元件部分如下: + +```tsx +// app/posts/posts.tsx +'use client' + +export default function Posts() { + // 此 useQuery 也可在 的更深層子元件中使用 + // 無論如何,資料都會立即可用 + const { data } = useQuery({ + queryKey: ['posts'], + queryFn: () => getPosts(), + }) + + // 此查詢未在伺服器預取,將在客戶端開始獲取 + // 兩種模式可混合使用 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} +``` + +上述範例的一個優點是,唯一與 Next.js 相關的只有檔案名稱,其餘內容在任何支援伺服器元件的框架中都會看起來相同。 + +在 SSR 指南中,我們提到可以避免在每個路由中使用 `` 的樣板程式碼。但這在伺服器元件中無法實現。 + +> 注意:若在使用非同步伺服器元件時遇到 TypeScript 版本低於 `5.1.3` 或 `@types/react` 版本低於 `18.2.8` 的類型錯誤,建議更新至兩者的最新版本。或者,可在呼叫此元件時暫時使用 `{/* @ts-expect-error Server Component */}` 作為解決方案。詳見 Next.js 13 文件中的 [非同步伺服器元件 TypeScript 錯誤](https://nextjs.org/docs/app/building-your-application/configuring/typescript#async-server-component-typescript-error)。 + +> 注意:若遇到錯誤 `Only plain objects, and a few built-ins, can be passed to Server Actions. Classes or null prototypes are not supported.`,請確保**不要**將函數參考傳遞給 queryFn,而是呼叫該函數,因為 queryFn 的參數具有許多屬性,並非所有都可序列化。詳見 [Server Action 僅在 queryFn 非參考時有效](https://github.com/TanStack/query/issues/6264)。 + +### 嵌套伺服器元件 + +伺服器元件的一個優點是它們可以嵌套存在於 React 樹的多個層級中,使得資料預取更接近實際使用位置,而不僅限於應用程式頂層(類似 Remix 的載入器)。這可以簡單到一個伺服器元件渲染另一個伺服器元件(為簡潔起見,此範例省略客戶端元件): + +```tsx +// app/posts/page.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Posts from './posts' +import CommentsServerComponent from './comments-server' + +export default async function PostsPage() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + + + + + ) +} + +// app/posts/comments-server.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Comments from './comments' + +export default async function CommentsServerComponent() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + return ( + + + + ) +} +``` + +如你所見,完全可以多處使用 ``,並為預取建立和脫水多個 `queryClient`。 + +需注意的是,因為我們在渲染 `CommentsServerComponent` 前等待 `getPosts`,這會導致伺服器端瀑布流: + +``` +1. |> getPosts() +2. |> getComments() +``` + +若伺服器至資料的延遲較低,這可能不是大問題,但仍值得指出。 + +在 Next.js 中,除了在 `page.tsx` 預取資料,還可在 `layout.tsx` 和[平行路由](https://nextjs.org/docs/app/building-your-application/routing/parallel-routes)中進行。由於這些都屬於路由部分,Next.js 知道如何並行獲取它們。因此,若上述 `CommentsServerComponent` 改為平行路由表達,瀑布流將自動扁平化。 + +隨著更多框架支援伺服器元件,它們可能有其他路由慣例。詳見各框架文件。 + +### 替代方案:使用單一 `queryClient` 進行預取 + +在上述範例中,我們為每個獲取資料的伺服器元件建立新的 `queryClient`。這是推薦做法,但你也可以選擇建立一個跨所有伺服器元件共用的單一實例: + +```tsx +// app/getQueryClient.tsx +import { QueryClient } from '@tanstack/react-query' +import { cache } from 'react' + +// cache() 是每個請求範圍的,因此不會在請求間洩漏資料 +const getQueryClient = cache(() => new QueryClient()) +export default getQueryClient +``` + +這樣做的優點是,你可以在任何從伺服器元件呼叫的地方(包括工具函數)使用 `getQueryClient()` 來獲取該客戶端。缺點是每次呼叫 `dehydrate(getQueryClient())` 時,都會序列化**整個** `queryClient`,包括已序列化且與當前伺服器元件無關的查詢,這是不必要的開銷。 + +Next.js 已經對使用 `fetch()` 的請求進行去重,但若你在 `queryFn` 中使用其他方法,或使用的框架**不**自動去重這些請求,則上述使用單一 `queryClient` 的方法可能更合理,儘管存在重複序列化。 + +> 作為未來改進,我們可能研究建立 `dehydrateNew()` 函數(名稱待定),僅脫水自上次 `dehydrateNew()` 呼叫以來**新增**的查詢。若有興趣協助,歡迎聯繫! + +### 資料所有權與重新驗證 + +使用伺服器元件時,需考慮資料所有權與重新驗證。為解釋原因,我們來看一個修改後的範例: + +```tsx +// app/posts/page.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' +import Posts from './posts' + +export default async function PostsPage() { + const queryClient = new QueryClient() + + // 注意現在使用 fetchQuery() + const posts = await queryClient.fetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return ( + + {/* 這是新增部分 */} +
    文章數量: {posts.length}
    + +
    + ) +} +``` + +我們現在在伺服器元件和客戶端元件中都渲染來自 `getPosts` 查詢的資料。初始頁面渲染時這沒問題,但當查詢因某些原因(如 `staleTime` 已過)在客戶端重新驗證時會發生什麼? + +React Query 不知道如何**重新驗證伺服器元件**,因此若它在客戶端重新獲取資料導致 React 重新渲染文章列表,`文章數量: {posts.length}` 將與實際資料不同步。 + +若設定 `staleTime: Infinity` 使 React Query 永不重新驗證則沒問題,但這可能不是你使用 React Query 的初衷。 + +在以下情況使用 React Query 與伺服器元件最為合理: + +- 你有一個使用 React Query 的應用程式,希望遷移至伺服器元件而無需重寫所有資料獲取邏輯 +- 你希望使用熟悉的程式設計模式,但仍能在合適的地方加入伺服器元件的好處 +- 你有某些 React Query 涵蓋但所選框架未涵蓋的使用案例 + +很難給出何時適合將 React Query 與伺服器元件搭配使用的通用建議。**若你剛開始建立新的伺服器元件應用程式,建議先使用框架提供的任何資料獲取工具,直到真正需要時才引入 React Query。**可能永遠不需要,這也沒關係,選擇合適的工具即可! + +若確實使用,一個好的經驗法則是避免使用 `queryClient.fetchQuery`,除非需要捕捉錯誤。若確實使用,請勿在伺服器端渲染其結果或將結果傳遞給其他元件(即使是客戶端元件)。 + +從 React Query 的角度,將伺服器元件視為預取資料的地方即可。 + +當然,讓伺服器元件擁有某些資料,客戶端元件擁有其他資料也沒問題,只需確保這兩種狀態不會不同步。 + +## 與伺服器元件的串流 (Streaming) + +Next.js 應用路由會自動將準備好的應用部分儘快串流至瀏覽器,因此已完成內容可立即顯示,無需等待仍在處理的內容。這是沿著 `` 邊界進行的。注意若建立 `loading.tsx` 檔案,這會自動在背後建立 `` 邊界。 + +使用上述預取模式時,React Query 完全相容於這種串流形式。當每個 Suspense 邊界的資料解析時,Next.js 可渲染並將完成內容串流至瀏覽器。即使你如上所述使用 `useQuery` 也能運作,因為暫停實際上發生在你 `await` 預取時。 + +自 React Query v5.40.0 起,你不需 `await` 所有預取也能運作,因為 `pending` 查詢也可脫水並傳送至客戶端。這讓你能儘早開始預取而不讓它們阻擋整個 Suspense 邊界,並在查詢完成時將**資料**串流至客戶端。這在某些情況下很有用,例如你想預取某些僅在使用者互動後才顯示的內容,或你想 `await` 並渲染無限查詢的第一頁,同時開始預取第二頁而不阻擋渲染。 + +為實現此功能,我們需指示 `queryClient` 也脫水 `pending` 查詢。可全域設定或直接傳遞選項至 `dehydrate`。 + +我們還需將 `getQueryClient()` 函數移出 `app/providers.tsx` 檔案,以便在伺服器元件和客戶端提供者中使用。 + +```tsx +// app/get-query-client.ts +import { + isServer, + QueryClient, + defaultShouldDehydrateQuery, +} from '@tanstack/react-query' + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + }, + dehydrate: { + // 在脫水中包含 pending 查詢 + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === 'pending', + shouldRedactErrors: (error) => { + // 我們不應捕捉 Next.js 伺服器 +``` diff --git a/docs/zh-hant/framework/react/guides/background-fetching-indicators.md b/docs/zh-hant/framework/react/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..484a2bef794 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/background-fetching-indicators.md @@ -0,0 +1,62 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:24:36.640Z' +id: background-fetching-indicators +title: 背景獲取指示器 +--- + +# 背景擷取指示器 (Background Fetching Indicators) + +查詢的 `status === 'pending'` 狀態足以顯示查詢的初始硬載入狀態,但有時您可能希望顯示一個額外的指示器,表示查詢正在背景中重新擷取。為此,查詢還提供了一個 `isFetching` 布林值,您可以用它來顯示查詢處於擷取狀態,無論 `status` 變數的狀態如何: + +[//]: # '範例' + +```tsx +function Todos() { + const { + status, + data: todos, + error, + isFetching, + } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) + + return status === 'pending' ? ( + 載入中... + ) : status === 'error' ? ( + 錯誤: {error.message} + ) : ( + <> + {isFetching ?
    重新整理中...
    : null} + +
    + {todos.map((todo) => ( + + ))} +
    + + ) +} +``` + +[//]: # '範例' + +## 顯示全域背景擷取載入狀態 + +除了個別查詢的載入狀態外,如果您希望在**任何**查詢正在擷取時(包括在背景中)顯示全域載入指示器,您可以使用 `useIsFetching` 鉤子 (hook): + +[//]: # '範例2' + +```tsx +import { useIsFetching } from '@tanstack/react-query' + +function GlobalLoadingIndicator() { + const isFetching = useIsFetching() + + return isFetching ?
    查詢正在背景中擷取...
    : null +} +``` + +[//]: # '範例2' diff --git a/docs/zh-hant/framework/react/guides/caching.md b/docs/zh-hant/framework/react/guides/caching.md new file mode 100644 index 00000000000..b7bb61f88d6 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/caching.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:24:39.360Z' +id: caching +title: 快取 +--- + +> 請在閱讀本指南前,先詳閱 [重要預設值](./important-defaults.md) + +## 基礎範例 + +這個快取範例將說明以下情境的生命週期: + +- 有無快取資料的查詢實例 (Query Instances) +- 背景重新獲取 (Background Refetching) +- 非活躍查詢 (Inactive Queries) +- 垃圾回收機制 (Garbage Collection) + +假設我們使用預設的 `gcTime` 值為 **5 分鐘**,且預設 `staleTime` 為 `0`。 + +- 當一個新的 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 實例掛載時: + - 由於之前沒有使用 `['todos']` 查詢鍵 (query key) 進行過其他查詢,該查詢會顯示硬載入狀態 (hard loading state) 並發起網路請求以獲取資料。 + - 當網路請求完成時,返回的資料會被快取在 `['todos']` 鍵下。 + - 該掛鉤 (hook) 會根據設定的 `staleTime`(預設為 `0`,即立即)將資料標記為過時 (stale)。 +- 當第二個 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 實例在其他地方掛載時: + - 由於快取中已有來自第一個查詢的 `['todos']` 鍵資料,該資料會立即從快取中返回。 + - 新實例會使用其查詢函數觸發一個新的網路請求。 + - 請注意,無論兩個 `fetchTodos` 查詢函數是否相同,兩個查詢的 [`status`](../reference/useQuery.md)(包括 `isFetching`、`isPending` 和其他相關值)都會更新,因為它們擁有相同的查詢鍵。 + - 當請求成功完成時,快取中 `['todos']` 鍵下的資料會更新為新資料,且兩個實例都會同步更新為新資料。 +- 當兩個 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 實例卸載並不再使用時: + - 由於該查詢已無活躍實例,系統會根據 `gcTime` 設定一個垃圾回收超時(預設為 **5 分鐘**)來刪除並回收該查詢。 +- 在快取超時完成前,另一個 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 實例掛載: + - 查詢會立即返回可用的快取資料,同時在背景執行 `fetchTodos` 函數。當成功完成時,會用新資料更新快取。 +- 最後一個 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 實例卸載。 +- 在 **5 分鐘** 內沒有再出現任何 `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` 實例: + - `['todos']` 鍵下的快取資料會被刪除並進行垃圾回收。 diff --git a/docs/zh-hant/framework/react/guides/default-query-function.md b/docs/zh-hant/framework/react/guides/default-query-function.md new file mode 100644 index 00000000000..885894afb52 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/default-query-function.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:24:16.822Z' +id: default-query-function +title: 預設查詢函數 +--- + +如果你基於任何原因,希望能在整個應用程式中共享相同的查詢函式,並僅透過查詢鍵 (query key) 來識別應該獲取什麼資料,你可以透過為 TanStack Query 提供一個 **預設查詢函式 (default query function)** 來實現: + +[//]: # '範例' + +```tsx +// 定義一個預設查詢函式,它會接收查詢鍵 +const defaultQueryFn = async ({ queryKey }) => { + const { data } = await axios.get( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + ) + return data +} + +// 透過 defaultOptions 將預設查詢函式提供給你的應用程式 +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, + }, +}) + +function App() { + return ( + + + + ) +} + +// 現在你只需要傳遞一個鍵即可! +function Posts() { + const { status, data, error, isFetching } = useQuery({ queryKey: ['/posts'] }) + + // ... +} + +// 你甚至可以省略 queryFn,直接傳入選項 +function Post({ postId }) { + const { status, data, error, isFetching } = useQuery({ + queryKey: [`/posts/${postId}`], + enabled: !!postId, + }) + + // ... +} +``` + +[//]: # '範例' + +如果你想覆蓋預設的 queryFn,只需像平常一樣提供你自己的函式即可。 diff --git a/docs/zh-hant/framework/react/guides/dependent-queries.md b/docs/zh-hant/framework/react/guides/dependent-queries.md new file mode 100644 index 00000000000..34b13e2caca --- /dev/null +++ b/docs/zh-hant/framework/react/guides/dependent-queries.md @@ -0,0 +1,97 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:24:19.824Z' +id: dependent-queries +title: 依賴查詢 +--- + +## useQuery 相依查詢 + +相依(或稱序列)查詢需等待前一個查詢完成後才能執行。要實現這一點,只需使用 `enabled` 選項來告訴查詢何時準備好執行: + +[//]: # '範例' + +```tsx +// 取得使用者資料 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, +}) + +const userId = user?.id + +// 接著取得使用者的專案資料 +const { + status, + fetchStatus, + data: projects, +} = useQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + // 此查詢會等到 userId 存在後才會執行 + enabled: !!userId, +}) +``` + +[//]: # '範例' + +`projects` 查詢會以以下狀態開始: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'idle' +``` + +當 `user` 資料可用時,`projects` 查詢會被 `enabled` 並轉換為: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'fetching' +``` + +取得專案資料後,狀態會變為: + +```tsx +status: 'success' +isPending: false +fetchStatus: 'idle' +``` + +## useQueries 相依查詢 + +動態平行查詢 - `useQueries` 也可以依賴前一個查詢,以下是實現方式: + +[//]: # '範例2' + +```tsx +// 取得使用者 ID 列表 +const { data: userIds } = useQuery({ + queryKey: ['users'], + queryFn: getUsersData, + select: (users) => users.map((user) => user.id), +}) + +// 接著取得各使用者的訊息 +const usersMessages = useQueries({ + queries: userIds + ? userIds.map((id) => { + return { + queryKey: ['messages', id], + queryFn: () => getMessagesByUsers(id), + } + }) + : [], // 如果 users 是 undefined,則回傳空陣列 +}) +``` + +[//]: # '範例2' + +**請注意** `useQueries` 會回傳一個**查詢結果陣列** + +## 關於效能的注意事項 + +相依查詢本質上會形成一種[請求瀑布流 (request waterfall)](./request-waterfalls.md),這會影響效能。假設兩個查詢花費相同時間,序列執行總會比平行執行多花一倍時間,這在高延遲的客戶端環境中尤其不利。如果可能,最好重構後端 API 讓兩個查詢能平行取得資料,雖然這在實務上不一定可行。 + +在上面的範例中,與其先取得 `getUserByEmail` 才能執行 `getProjectsByUser`,引入一個新的 `getProjectsByUserEmail` 查詢可以消除瀑布流問題。 diff --git a/docs/zh-hant/framework/react/guides/disabling-queries.md b/docs/zh-hant/framework/react/guides/disabling-queries.md new file mode 100644 index 00000000000..220478121f8 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/disabling-queries.md @@ -0,0 +1,130 @@ +--- +source-updated-at: '2025-01-10T12:49:47.000Z' +translation-updated-at: '2025-05-08T20:24:31.659Z' +id: disabling-queries +title: 停用/暫停查詢 +--- + +若想禁止查詢自動執行,可使用 `enabled = false` 選項。`enabled` 選項也接受回傳布林值的回呼函式。 + +當 `enabled` 為 `false` 時: + +- 若查詢有快取資料,則查詢會初始化為 `status === 'success'` 或 `isSuccess` 狀態。 +- 若查詢無快取資料,則查詢會以 `status === 'pending'` 和 `fetchStatus === 'idle'` 狀態開始。 +- 查詢不會在掛載時自動執行。 +- 查詢不會在背景自動重新取得資料。 +- 查詢會忽略查詢客戶端的 `invalidateQueries` 和 `refetchQueries` 呼叫(這些呼叫通常會導致查詢重新取得資料)。 +- 從 `useQuery` 回傳的 `refetch` 可用於手動觸發查詢取得資料,但與 `skipToken` 併用時無效。 + +> TypeScript 使用者可改用 [skipToken](#typesafe-disabling-of-queries-using-skiptoken) 作為 `enabled = false` 的替代方案。 + +[//]: # '範例' + +```tsx +function Todos() { + const { isLoading, isError, data, error, refetch, isFetching } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + enabled: false, + }) + + return ( +
    + + + {data ? ( + <> +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + + ) : isError ? ( + Error: {error.message} + ) : isLoading ? ( + Loading... + ) : ( + Not ready ... + )} + +
    {isFetching ? 'Fetching...' : null}
    +
    + ) +} +``` + +[//]: # '範例' + +永久禁用查詢會放棄 TanStack Query 提供的許多強大功能(如背景重新取得資料),這也非慣用做法。此做法會從宣告式模式(定義查詢執行時機的依賴條件)轉為命令式模式(點擊按鈕時才取得資料),且無法傳遞參數給 `refetch`。多數情況下,您需要的其實是延遲初始資料取得的「惰性查詢」: + +## 惰性查詢 (Lazy Queries) + +`enabled` 選項不僅能用於永久禁用查詢,還可控制後續啟用/禁用時機。典型範例是篩選表單——僅在使用者輸入篩選值後才發送首次請求: + +[//]: # '範例2' + +```tsx +function Todos() { + const [filter, setFilter] = React.useState('') + + const { data } = useQuery({ + queryKey: ['todos', filter], + queryFn: () => fetchTodos(filter), + // ⬇️ 篩選值為空時禁用查詢 + enabled: !!filter, + }) + + return ( +
    + // 🚀 套用篩選值後會啟用並執行查詢 + + {data && } +
    + ) +} +``` + +[//]: # '範例2' + +### isLoading (原為 `isInitialLoading`) + +惰性查詢會從開始就處於 `status: 'pending'`,因為 `pending` 表示尚未取得資料。技術上雖正確,但由於當前並未取得資料(查詢未被 _啟用_),意味著您可能無法用此標誌顯示載入指示器。 + +若使用禁用或惰性查詢,可改用 `isLoading` 標誌。這是個衍生標誌,由以下條件計算: + +`isPending && isFetching` + +因此僅在查詢首次取得資料時會為 `true`。 + +## 使用 `skipToken` 實現型別安全的查詢禁用 + +若使用 TypeScript,可用 `skipToken` 禁用查詢。這適用於需根據條件禁用查詢,同時保持查詢型別安全的情況。 + +> 重要:`useQuery` 的 `refetch` 與 `skipToken` 併用時無效。除此之外,`skipToken` 的行為與 `enabled: false` 相同。 + +[//]: # '範例3' + +```tsx +import { skipToken, useQuery } from '@tanstack/react-query' + +function Todos() { + const [filter, setFilter] = React.useState() + + const { data } = useQuery({ + queryKey: ['todos', filter], + // ⬇️ 篩選值為 undefined 或空字串時禁用查詢 + queryFn: filter ? () => fetchTodos(filter) : skipToken, + }) + + return ( +
    + // 🚀 套用篩選值後會啟用並執行查詢 + + {data && } +
    + ) +} +``` + +[//]: # '範例3' diff --git a/docs/zh-hant/framework/react/guides/does-this-replace-client-state.md b/docs/zh-hant/framework/react/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..d79b6f06062 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/does-this-replace-client-state.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:23:51.769Z' +id: does-this-replace-client-state +title: '這會取代 [Redux, MobX 等] 嗎?' +--- + +好的,讓我們從幾個重點開始: + +- TanStack Query 是一個 **伺服器狀態 (server-state)** 函式庫,負責管理伺服器與客戶端之間的異步操作 +- Redux、MobX、Zustand 等則是 **客戶端狀態 (client-state)** 函式庫,雖然可用於儲存異步資料,但與 TanStack Query 這類工具相比效率較低 + +理解這些要點後,簡短的回答是:TanStack Query **能取代那些用來管理客戶端狀態快取資料的樣板程式碼與相關配置,並僅需幾行程式碼即可實現相同功能**。 + +對於大多數應用程式而言,當你將所有異步程式碼遷移到 TanStack Query 後,真正剩下的**全域可存取客戶端狀態**通常非常少。 + +> 當然仍有某些情況,例如應用程式確實擁有大量同步的純客戶端狀態(如視覺設計工具或音樂製作軟體),這時你可能還是需要客戶端狀態管理工具。值得注意的是,**TanStack Query 並非用於取代本地/客戶端狀態管理**。不過,你可以毫無問題地將 TanStack Query 與大多數客戶端狀態管理工具一起使用。 + +## 一個刻意設計的範例 + +這裡我們有一個由全域狀態函式庫管理的「全域」狀態: + +```tsx +const globalState = { + projects, + teams, + tasks, + users, + themeMode, + sidebarStatus, +} +``` + +目前,全域狀態管理工具快取了 4 種伺服器狀態:`projects`、`teams`、`tasks` 和 `users`。如果我們將這些伺服器狀態資產移至 TanStack Query,剩下的全域狀態會變成這樣: + +```tsx +const globalState = { + themeMode, + sidebarStatus, +} +``` + +這也意味著,只需使用 `useQuery` 和 `useMutation` 幾個鉤子呼叫,我們就能移除所有用於管理伺服器狀態的樣板程式碼,例如: + +- 連接器 (Connectors) +- 動作創建器 (Action Creators) +- 中介軟體 (Middlewares) +- 歸約器 (Reducers) +- 載入/錯誤/結果狀態 (Loading/Error/Result states) +- 上下文 (Contexts) + +移除這些東西後,你可能會問自己:**「為了這麼少量的全域狀態,是否還值得繼續使用客戶端狀態管理工具?」** + +**這就由你決定了!** + +但 TanStack Query 的角色很明確。它能從你的應用程式中移除異步邏輯與樣板程式碼,並僅用幾行程式碼取代。 + +還在等什麼?現在就試試看吧! diff --git a/docs/zh-hant/framework/react/guides/filters.md b/docs/zh-hant/framework/react/guides/filters.md new file mode 100644 index 00000000000..b496bb0c55d --- /dev/null +++ b/docs/zh-hant/framework/react/guides/filters.md @@ -0,0 +1,92 @@ +--- +source-updated-at: '2025-01-26T18:24:42.000Z' +translation-updated-at: '2025-05-08T20:23:59.294Z' +id: filters +title: 篩選器 +--- + +TanStack Query 中的某些方法接受 `QueryFilters` 或 `MutationFilters` 物件。 + +## `Query Filters` + +查詢過濾器 (Query Filters) 是一個包含特定條件的物件,用來匹配查詢: + +```tsx +// 取消所有查詢 +await queryClient.cancelQueries() + +// 移除所有 key 以 `posts` 開頭的非活躍查詢 +queryClient.removeQueries({ queryKey: ['posts'], type: 'inactive' }) + +// 重新取得所有活躍查詢的資料 +await queryClient.refetchQueries({ type: 'active' }) + +// 重新取得所有 key 以 `posts` 開頭的活躍查詢資料 +await queryClient.refetchQueries({ queryKey: ['posts'], type: 'active' }) +``` + +查詢過濾器物件支援以下屬性: + +- `queryKey?: QueryKey` + - 設定此屬性以定義要匹配的查詢鍵 (query key)。 +- `exact?: boolean` + - 如果您不希望透過查詢鍵進行包容性搜尋,可以傳遞 `exact: true` 選項,僅返回與您傳遞的查詢鍵完全匹配的查詢。 +- `type?: 'active' | 'inactive' | 'all'` + - 預設為 `all` + - 設為 `active` 時會匹配活躍查詢 (active queries)。 + - 設為 `inactive` 時會匹配非活躍查詢 (inactive queries)。 +- `stale?: boolean` + - 設為 `true` 時會匹配過時查詢 (stale queries)。 + - 設為 `false` 時會匹配新鮮查詢 (fresh queries)。 +- `fetchStatus?: FetchStatus` + - 設為 `fetching` 時會匹配目前正在取得資料的查詢。 + - 設為 `paused` 時會匹配想要取得資料但已被暫停的查詢。 + - 設為 `idle` 時會匹配未在取得資料的查詢。 +- `predicate?: (query: Query) => boolean` + - 此謂詞函式 (predicate function) 將作為所有匹配查詢的最終過濾條件。如果未指定其他過濾條件,此函式將針對快取中的每個查詢進行評估。 + +## `Mutation Filters` + +突變過濾器 (Mutation Filters) 是一個包含特定條件的物件,用來匹配突變: + +```tsx +// 取得所有正在執行的突變數量 +await queryClient.isMutating() + +// 透過 mutationKey 過濾突變 +await queryClient.isMutating({ mutationKey: ['post'] }) + +// 使用謂詞函式過濾突變 +await queryClient.isMutating({ + predicate: (mutation) => mutation.state.variables?.id === 1, +}) +``` + +突變過濾器物件支援以下屬性: + +- `mutationKey?: MutationKey` + - 設定此屬性以定義要匹配的突變鍵 (mutation key)。 +- `exact?: boolean` + - 如果您不希望透過突變鍵進行包容性搜尋,可以傳遞 `exact: true` 選項,僅返回與您傳遞的突變鍵完全匹配的突變。 +- `status?: MutationStatus` + - 允許根據突變狀態進行過濾。 +- `predicate?: (mutation: Mutation) => boolean` + - 此謂詞函式將作為所有匹配突變的最終過濾條件。如果未指定其他過濾條件,此函式將針對快取中的每個突變進行評估。 + +## 工具函式 + +### `matchQuery` + +```tsx +const isMatching = matchQuery(filters, query) +``` + +返回一個布林值,表示查詢是否匹配提供的查詢過濾器集合。 + +### `matchMutation` + +```tsx +const isMatching = matchMutation(filters, mutation) +``` + +返回一個布林值,表示突變是否匹配提供的突變過濾器集合。 diff --git a/docs/zh-hant/framework/react/guides/important-defaults.md b/docs/zh-hant/framework/react/guides/important-defaults.md new file mode 100644 index 00000000000..420426f4acb --- /dev/null +++ b/docs/zh-hant/framework/react/guides/important-defaults.md @@ -0,0 +1,44 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:23:42.354Z' +id: important-defaults +title: 重要預設值 +--- + +TanStack Query 預設採用了**積極但合理**的配置。**這些預設值有時會讓新用戶措手不及,或在使用者不知情的情況下使學習/除錯變得困難。** 在繼續學習和使用 TanStack Query 時,請記住以下幾點: + +- 透過 `useQuery` 或 `useInfiniteQuery` 建立的查詢實例,預設會**將快取的資料視為過時的 (stale)**。 + +> 若要變更此行為,您可以在全域或單一查詢中透過 `staleTime` 選項進行配置。設定較長的 `staleTime` 意味著查詢不會頻繁重新取得資料。 + +- 在以下情況下,過時的查詢會在背景自動重新取得資料: + - 新的查詢實例掛載時 + - 視窗重新獲得焦點時 + - 網路重新連線時 + - 查詢可選配置了重新取得間隔 (refetch interval) 時 + +> 若要變更此功能,可以使用 `refetchOnMount`、`refetchOnWindowFocus`、`refetchOnReconnect` 和 `refetchInterval` 等選項。 + +- 當 `useQuery`、`useInfiniteQuery` 或查詢觀察者 (query observers) 沒有活躍的實例時,查詢結果會被標記為「非活躍 (inactive)」,並保留在快取中以便後續再次使用。 +- 預設情況下,「非活躍」的查詢會在 **5 分鐘** 後被垃圾回收。 + + > 若要變更此設定,可以調整查詢的預設 `gcTime`,將其設為 `1000 * 60 * 5` 毫秒以外的值。 + +- 失敗的查詢會**靜默重試 3 次,並採用指數退避延遲 (exponential backoff delay)**,之後才會捕獲錯誤並顯示在 UI 上。 + + > 若要變更此行為,可以調整查詢的預設 `retry` 和 `retryDelay` 選項,將其設為 `3` 和預設的指數退避函式以外的值。 + +- 預設情況下,查詢結果會**進行結構化共享 (structurally shared) 以檢測資料是否實際發生變更**,如果沒有變更,**資料的參考將保持不變**,這有助於提升 `useMemo` 和 `useCallback` 的值穩定性。如果這個概念聽起來陌生,請不必擔心!99.9% 的情況下您不需要停用此功能,它能在零成本的情況下提升應用程式的效能。 + + > 結構化共享僅適用於 JSON 相容的值,其他任何值類型都會被視為已變更。例如,如果您因大型回應而遇到效能問題,可以透過 `config.structuralSharing` 標誌停用此功能。如果您在查詢回應中處理非 JSON 相容的值,但仍想檢測資料是否變更,可以自訂 `config.structuralSharing` 函式,根據舊回應和新回應計算值,並按需保留參考。 + +[//]: # 'Materials' + +## 延伸閱讀 + +請參考以下社群資源文章,進一步了解預設行為的說明: + +- [Practical React Query](../community/tkdodos-blog.md#1-practical-react-query) +- [React Query as a State Manager](../community/tkdodos-blog.md#10-react-query-as-a-state-manager) + +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/react/guides/infinite-queries.md b/docs/zh-hant/framework/react/guides/infinite-queries.md new file mode 100644 index 00000000000..fa7d9ead60c --- /dev/null +++ b/docs/zh-hant/framework/react/guides/infinite-queries.md @@ -0,0 +1,263 @@ +--- +source-updated-at: '2024-11-18T17:22:25.000Z' +translation-updated-at: '2025-05-08T20:24:37.683Z' +id: infinite-queries +title: 無限查詢 +--- + +## 無限查詢 (Infinite Queries) + +能夠以增量方式「載入更多」資料到現有資料集或實現「無限滾動」的列表渲染,也是一種非常常見的 UI 模式。TanStack Query 提供了一個名為 `useInfiniteQuery` 的 `useQuery` 變體,專門用於查詢這類列表。 + +使用 `useInfiniteQuery` 時,你會注意到以下幾點不同: + +- `data` 現在是一個包含無限查詢資料的物件: + - `data.pages` 陣列包含已獲取的頁面 + - `data.pageParams` 陣列包含用於獲取頁面的頁面參數 +- 現在可使用 `fetchNextPage` 和 `fetchPreviousPage` 函數(`fetchNextPage` 是必需的) +- 現在可使用(且必須指定)`initialPageParam` 選項來設定初始頁面參數 +- 可使用 `getNextPageParam` 和 `getPreviousPageParam` 選項來判斷是否有更多資料需要載入,以及獲取這些資料所需的資訊。此資訊會作為查詢函數的附加參數提供 +- 現在提供 `hasNextPage` 布林值,當 `getNextPageParam` 返回的值不是 `null` 或 `undefined` 時為 `true` +- 現在提供 `hasPreviousPage` 布林值,當 `getPreviousPageParam` 返回的值不是 `null` 或 `undefined` 時為 `true` +- 現在提供 `isFetchingNextPage` 和 `isFetchingPreviousPage` 布林值,用於區分背景刷新狀態與載入更多狀態 + +> 注意:選項 `initialData` 或 `placeholderData` 需要符合具有 `data.pages` 和 `data.pageParams` 屬性的物件結構。 + +## 範例 + +假設我們有一個 API,基於 `cursor` 索引每次返回 3 個 `projects` 的頁面,並提供可用於獲取下一組專案的游標: + +```tsx +fetch('/api/projects?cursor=0') +// { data: [...], nextCursor: 3} +fetch('/api/projects?cursor=3') +// { data: [...], nextCursor: 6} +fetch('/api/projects?cursor=6') +// { data: [...], nextCursor: 9} +fetch('/api/projects?cursor=9') +// { data: [...] } +``` + +根據這些資訊,我們可以通過以下方式建立一個「載入更多」的 UI: + +- 預設等待 `useInfiniteQuery` 請求第一組資料 +- 在 `getNextPageParam` 中返回下一查詢的資訊 +- 呼叫 `fetchNextPage` 函數 + +[//]: # '範例' + +```tsx +import { useInfiniteQuery } from '@tanstack/react-query' + +function Projects() { + const fetchProjects = async ({ pageParam }) => { + const res = await fetch('/api/projects?cursor=' + pageParam) + return res.json() + } + + const { + data, + error, + fetchNextPage, + hasNextPage, + isFetching, + isFetchingNextPage, + status, + } = useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + }) + + return status === 'pending' ? ( +

    載入中...

    + ) : status === 'error' ? ( +

    錯誤: {error.message}

    + ) : ( + <> + {data.pages.map((group, i) => ( + + {group.data.map((project) => ( +

    {project.name}

    + ))} +
    + ))} +
    + +
    +
    {isFetching && !isFetchingNextPage ? '獲取中...' : null}
    + + ) +} +``` + +[//]: # '範例' + +必須理解的是,在進行中的獲取過程中呼叫 `fetchNextPage` 有可能會覆蓋正在背景進行的資料刷新。這種情況在渲染列表並同時觸發 `fetchNextPage` 時尤其關鍵。 + +請記住,對於 InfiniteQuery 只能有一個進行中的獲取。所有頁面共享單一快取條目,嘗試同時進行兩次獲取可能會導致資料覆寫。 + +如果你想啟用同時獲取,可以在 `fetchNextPage` 中使用 `{ cancelRefetch: false }` 選項(預設為 true)。 + +為了確保查詢過程順暢無衝突,強烈建議檢查查詢是否處於 `isFetching` 狀態,特別是當使用者不會直接控制該呼叫時。 + +[//]: # '範例1' + +```jsx + !isFetchingNextPage && fetchNextPage()} /> +``` + +[//]: # '範例1' + +## 當無限查詢需要重新獲取時會發生什麼? + +當無限查詢變為 `stale` 並需要重新獲取時,每組資料會從第一組開始「依序」獲取。這確保即使基礎資料發生變更,我們也不會使用過期的游標,從而避免獲取重複或跳過記錄。如果無限查詢的結果從 queryCache 中被移除,分頁將從初始狀態重新開始,僅請求初始組。 + +## 如果想實現雙向無限列表該怎麼辦? + +雙向列表可以通過使用 `getPreviousPageParam`、`fetchPreviousPage`、`hasPreviousPage` 和 `isFetchingPreviousPage` 屬性和函數來實現。 + +[//]: # '範例3' + +```tsx +useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, +}) +``` + +[//]: # '範例3' + +## 如果想以反向順序顯示頁面該怎麼辦? + +有時你可能希望以反向順序顯示頁面。如果是這種情況,可以使用 `select` 選項: + +[//]: # '範例4' + +```tsx +useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), +}) +``` + +[//]: # '範例4' + +## 如果想手動更新無限查詢該怎麼辦? + +### 手動移除第一頁: + +[//]: # '範例5' + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1), +})) +``` + +[//]: # '範例5' + +### 手動從單個頁面中移除單個值: + +[//]: # '範例6' + +```tsx +const newPagesArray = + oldPagesArray?.pages.map((page) => + page.filter((val) => val.id !== updatedId), + ) ?? [] + +queryClient.setQueryData(['projects'], (data) => ({ + pages: newPagesArray, + pageParams: data.pageParams, +})) +``` + +[//]: # '範例6' + +### 僅保留第一頁: + +[//]: # '範例7' + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(0, 1), + pageParams: data.pageParams.slice(0, 1), +})) +``` + +[//]: # '範例7' + +確保始終保持 pages 和 pageParams 的相同資料結構! + +## 如果想限制頁面數量該怎麼辦? + +在某些使用場景中,你可能希望限制查詢資料中儲存的頁面數量,以提高效能和使用者體驗: + +- 當使用者可以載入大量頁面時(記憶體使用) +- 當需要重新獲取包含數十頁的無限查詢時(網路使用:所有頁面會依序獲取) + +解決方案是使用「有限無限查詢」。這可以通過將 `maxPages` 選項與 `getNextPageParam` 和 `getPreviousPageParam` 結合使用來實現,以便在需要時雙向獲取頁面。 + +在以下範例中,查詢資料 pages 陣列中僅保留 3 頁。如果需要重新獲取,僅會依序重新獲取 3 頁。 + +[//]: # '範例8' + +```tsx +useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, + maxPages: 3, +}) +``` + +[//]: # '範例8' + +## 如果 API 沒有返回游標該怎麼辦? + +如果你的 API 沒有返回游標,可以將 `pageParam` 用作游標。因為 `getNextPageParam` 和 `getPreviousPageParam` 也會獲取當前頁面的 `pageParam`,你可以用它來計算下一頁/上一頁的參數。 + +[//]: # '範例9' + +```tsx +return useInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, allPages, lastPageParam) => { + if (lastPage.length === 0) { + return undefined + } + return lastPageParam + 1 + }, + getPreviousPageParam: (firstPage, allPages, firstPageParam) => { + if (firstPageParam <= 1) { + return undefined + } + return firstPageParam - 1 + }, +}) +``` + +[//]: # '範例9' diff --git a/docs/zh-hant/framework/react/guides/initial-query-data.md b/docs/zh-hant/framework/react/guides/initial-query-data.md new file mode 100644 index 00000000000..de146b3ad4b --- /dev/null +++ b/docs/zh-hant/framework/react/guides/initial-query-data.md @@ -0,0 +1,177 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:24:16.634Z' +id: initial-query-data +title: 初始查詢資料 +--- + +在需要之前,有多種方式可以為快取中的查詢提供初始資料: + +- 宣告式: + - 提供 `initialData` 給查詢,以便在快取為空時預先填充 +- 命令式: + - [使用 `queryClient.prefetchQuery` 預先取得資料](./prefetching.md) + - [使用 `queryClient.setQueryData` 手動將資料放入快取](./prefetching.md) + +## 使用 `initialData` 預先填充查詢 + +有時您可能已經在應用程式中擁有查詢的初始資料,並可以直接提供給查詢。在這種情況下,您可以使用 `config.initialData` 選項來設定查詢的初始資料,並跳過初始載入狀態! + +> 重要提示:`initialData` 會被持久化到快取中,因此不建議在此選項中提供佔位、部分或不完整的資料,而應使用 `placeholderData` + +[//]: # '範例' + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, +}) +``` + +[//]: # '範例' + +### `staleTime` 與 `initialDataUpdatedAt` + +預設情況下,`initialData` 會被視為完全新鮮的資料,就像剛剛取得的一樣。這也意味著它會影響 `staleTime` 選項的解讀方式。 + +- 如果您使用 `initialData` 配置查詢觀察器,並且沒有設定 `staleTime`(預設 `staleTime: 0`),查詢會在掛載時立即重新取得資料: + + [//]: # '範例2' + + ```tsx + // 會立即顯示 initialTodos,但掛載後也會立即重新取得 todos + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + }) + ``` + + [//]: # '範例2' + +- 如果您使用 `initialData` 和 `staleTime` 為 `1000` 毫秒來配置查詢觀察器,資料將在這段時間內被視為新鮮的,就像剛從查詢函式中取得的一樣。 + + [//]: # '範例3' + + ```tsx + // 立即顯示 initialTodos,但在 1000 毫秒後遇到另一個互動事件之前不會重新取得資料 + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 1000, + }) + ``` + + [//]: # '範例3' + +- 那麼如果您的 `initialData` 不是完全新鮮的呢?這就引出了最後一種配置,它實際上是最準確的,並使用一個名為 `initialDataUpdatedAt` 的選項。此選項允許您傳遞一個以毫秒為單位的 JS 時間戳,表示 `initialData` 本身最後更新的時間,例如 `Date.now()` 提供的值。請注意,如果您有一個 unix 時間戳,則需要將其乘以 `1000` 轉換為 JS 時間戳。 + + [//]: # '範例4' + + ```tsx + // 立即顯示 initialTodos,但在 1 分鐘後遇到另一個互動事件之前不會重新取得資料 + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: initialTodos, + staleTime: 60 * 1000, // 1 分鐘 + // 這可能是 10 秒前或 10 分鐘前 + initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052 + }) + ``` + + [//]: # '範例4' + + 此選項允許 `staleTime` 用於其原始目的,即確定資料需要多新鮮,同時也允許在掛載時重新取得資料,如果 `initialData` 比 `staleTime` 舊的話。在上面的範例中,我們的資料需要在 1 分鐘內保持新鮮,並且我們可以向查詢提示 `initialData` 最後更新的時間,以便查詢自行決定是否需要重新取得資料。 + + > 如果您更希望將資料視為**預先取得的資料**,建議您使用 `prefetchQuery` 或 `fetchQuery` API 事先填充快取,從而讓您可以獨立於 `initialData` 配置 `staleTime` + +### 初始資料函式 + +如果取得查詢初始資料的過程很耗資源,或者您不想在每次渲染時執行,可以將一個函式作為 `initialData` 的值傳遞。此函式僅在查詢初始化時執行一次,從而節省寶貴的記憶體和/或 CPU: + +[//]: # '範例5' + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + initialData: () => getExpensiveTodos(), +}) +``` + +[//]: # '範例5' + +### 從快取取得初始資料 + +在某些情況下,您可以從另一個查詢的快取結果中提供查詢的初始資料。一個很好的例子是從 todos 列表查詢的快取資料中搜尋單個 todo 項目,然後將其用作單個 todo 查詢的初始資料: + +[//]: # '範例6' + +```tsx +const result = useQuery({ + queryKey: ['todo', todoId], + queryFn: () => fetch('/todos'), + initialData: () => { + // 使用 'todos' 查詢中的一個 todo 作為此 todo 查詢的初始資料 + return queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId) + }, +}) +``` + +[//]: # '範例6' + +### 從快取取得初始資料並使用 `initialDataUpdatedAt` + +從快取取得初始資料意味著您用來查找初始資料的來源查詢可能已經過時。與其使用人為的 `staleTime` 來防止查詢立即重新取得資料,建議您將來源查詢的 `dataUpdatedAt` 傳遞給 `initialDataUpdatedAt`。這為查詢實例提供了所需的所有資訊,以便判斷是否需要重新取得資料以及何時重新取得,無論是否提供了初始資料。 + +[//]: # '範例7' + +```tsx +const result = useQuery({ + queryKey: ['todos', todoId], + queryFn: () => fetch(`/todos/${todoId}`), + initialData: () => + queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId), + initialDataUpdatedAt: () => + queryClient.getQueryState(['todos'])?.dataUpdatedAt, +}) +``` + +[//]: # '範例7' + +### 從快取條件式取得初始資料 + +如果您用來查找初始資料的來源查詢已經過時,您可能根本不想使用快取資料,而是直接從伺服器取得。為了更容易做出這個決定,您可以使用 `queryClient.getQueryState` 方法來獲取有關來源查詢的更多資訊,包括一個 `state.dataUpdatedAt` 時間戳,您可以用來判斷查詢是否足夠「新鮮」以滿足您的需求: + +[//]: # '範例8' + +```tsx +const result = useQuery({ + queryKey: ['todo', todoId], + queryFn: () => fetch(`/todos/${todoId}`), + initialData: () => { + // 取得查詢狀態 + const state = queryClient.getQueryState(['todos']) + + // 如果查詢存在且資料不超過 10 秒... + if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) { + // 回傳單個 todo + return state.data.find((d) => d.id === todoId) + } + + // 否則,回傳 undefined 並讓它從硬載入狀態取得資料! + }, +}) +``` + +[//]: # '範例8' +[//]: # '材料' + +## 延伸閱讀 + +如需比較 `Initial Data` 與 `Placeholder Data`,請參閱 [社群資源](../community/tkdodos-blog.md#9-placeholder-and-initial-data-in-react-query)。 + +[//]: # '材料' diff --git a/docs/zh-hant/framework/react/guides/invalidations-from-mutations.md b/docs/zh-hant/framework/react/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..97fb754961d --- /dev/null +++ b/docs/zh-hant/framework/react/guides/invalidations-from-mutations.md @@ -0,0 +1,41 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:22:57.592Z' +id: invalidations-from-mutations +title: 來自變更的失效 +--- + +使查詢失效只是成功的一半,知道**何時**使其失效才是另一半。通常當應用程式中的一個異動 (mutation) 成功時,很可能會有相關的查詢需要失效,甚至可能需要重新獲取資料以反映異動帶來的新變更。 + +舉例來說,假設我們有一個新增待辦事項的異動: + +[//]: # 'Example' + +```tsx +const mutation = useMutation({ mutationFn: postTodo }) +``` + +[//]: # 'Example' + +當 `postTodo` 異動成功時,我們很可能會希望所有 `todos` 查詢都失效,並可能重新獲取資料以顯示新的待辦事項。要做到這一點,你可以使用 `useMutation` 的 `onSuccess` 選項和 `client` 的 `invalidateQueries` 函式: + +[//]: # 'Example2' + +```tsx +import { useMutation, useQueryClient } from '@tanstack/react-query' + +const queryClient = useQueryClient() + +// 當此異動成功時,使所有帶有 `todos` 或 `reminders` 查詢鍵 (query key) 的查詢失效 +const mutation = useMutation({ + mutationFn: addTodo, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['todos'] }) + queryClient.invalidateQueries({ queryKey: ['reminders'] }) + }, +}) +``` + +[//]: # 'Example2' + +你可以使用 [`useMutation` 鉤子 (hook)](./mutations.md) 中提供的任何回調函式來設定失效的時機。 diff --git a/docs/zh-hant/framework/react/guides/migrating-to-react-query-3.md b/docs/zh-hant/framework/react/guides/migrating-to-react-query-3.md new file mode 100644 index 00000000000..7793c2fa08d --- /dev/null +++ b/docs/zh-hant/framework/react/guides/migrating-to-react-query-3.md @@ -0,0 +1,478 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:25:09.511Z' +id: migrating-to-react-query-3 +title: 遷移至 v3 +--- + +React Query 的先前版本已經非常出色,為函式庫帶來了一些令人驚豔的新功能、更多魔法般的體驗,以及整體更優質的使用感受。這些版本也帶來了大量的採用,同時也為函式庫帶來了許多精煉的考驗(問題/貢獻),並凸顯了一些需要進一步打磨的地方,以讓函式庫變得更好。v3 版本正是這些打磨的成果。 + +## 概覽 + +- 更具擴展性和可測試性的快取配置 +- 更好的伺服器渲染 (SSR) 支援 +- 資料延遲(先前稱為 `usePaginatedQuery`)功能隨處可用! +- 雙向無限查詢 (Infinite Queries) +- 查詢資料選擇器 (Query data selectors)! +- 可在使用前完全配置查詢和/或變更 (mutations) 的預設值 +- 更細緻的選擇性渲染優化 +- 新的 `useQueries` 鉤子!(可變長度的平行查詢執行) +- `useIsFetching()` 鉤子支援查詢過濾器! +- 變更 (mutations) 的重試/離線/重播支援 +- 在 React 外部觀察查詢/變更 +- 在任何地方使用 React Query 的核心邏輯! +- 整合/同位置的開發工具,透過 `react-query/devtools` 使用 +- 快取持久化至網頁儲存空間(實驗性功能,透過 `react-query/persistQueryClient-experimental` 和 `react-query/createWebStoragePersistor-experimental` 使用) + +## 重大變更 + +### `QueryCache` 已被拆分為 `QueryClient` 和底層的 `QueryCache` 與 `MutationCache` 實例。 + +`QueryCache` 包含所有查詢,`MutationCache` 包含所有變更,而 `QueryClient` 可用於設定配置並與它們互動。 + +這帶來了一些好處: + +- 允許不同類型的快取。 +- 具有不同配置的多個客戶端可以使用同一個快取。 +- 客戶端可用於追蹤查詢,這可用於 SSR 上的共享快取。 +- 客戶端 API 更專注於一般使用情境。 +- 更容易測試個別元件。 + +當建立 `new QueryClient()` 時,如果沒有提供,系統會自動為你建立 `QueryCache` 和 `MutationCache`。 + +```tsx +import { QueryClient } from 'react-query' + +const queryClient = new QueryClient() +``` + +### `ReactQueryConfigProvider` 和 `ReactQueryCacheProvider` 已被 `QueryClientProvider` 取代 + +現在可以在 `QueryClient` 中指定查詢和變更的預設選項: + +**請注意,現在是 `defaultOptions` 而非 `defaultConfig`** + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // 查詢選項 + }, + mutations: { + // 變更選項 + }, + }, +}) +``` + +`QueryClientProvider` 元件現在用於將 `QueryClient` 連接到你的應用程式: + +```tsx +import { QueryClient, QueryClientProvider } from 'react-query' + +const queryClient = new QueryClient() + +function App() { + return ... +} +``` + +### 預設的 `QueryCache` 已消失。**這次是真的!** + +如先前所提到的棄用通知,主套件不再建立或匯出預設的 `QueryCache`。**你必須透過 `new QueryClient()` 或 `new QueryCache()` 自行建立(然後可以傳遞給 `new QueryClient({ queryCache })`)** + +### 已棄用的 `makeQueryCache` 工具已被移除。 + +這已經醞釀很久了,但它終於消失了 :) + +### `QueryCache.prefetchQuery()` 已移至 `QueryClient.prefetchQuery()` + +新的 `QueryClient.prefetchQuery()` 函式是異步的,但**不會回傳查詢的資料**。如果需要資料,請使用新的 `QueryClient.fetchQuery()` 函式 + +```tsx +// 預先擷取查詢: +await queryClient.prefetchQuery('posts', fetchPosts) + +// 擷取查詢: +try { + const data = await queryClient.fetchQuery('posts', fetchPosts) +} catch (error) { + // 錯誤處理 +} +``` + +### `ReactQueryErrorResetBoundary` 和 `QueryCache.resetErrorBoundaries()` 已被 `QueryErrorResetBoundary` 和 `useQueryErrorResetBoundary()` 取代。 + +這些新功能提供了與之前相同的體驗,但增加了選擇要重置哪些元件樹的控制權。更多資訊請參閱: + +- [QueryErrorResetBoundary](../reference/QueryErrorResetBoundary.md) +- [useQueryErrorResetBoundary](../reference/useQueryErrorResetBoundary.md) + +### `QueryCache.getQuery()` 已被 `QueryCache.find()` 取代。 + +現在應使用 `QueryCache.find()` 從快取中查找個別查詢 + +### `QueryCache.getQueries()` 已移至 `QueryCache.findAll()`。 + +現在應使用 `QueryCache.findAll()` 從快取中查找多個查詢 + +### `QueryCache.isFetching` 已移至 `QueryClient.isFetching()`。 + +**請注意,現在是一個函式而非屬性** + +### `useQueryCache` 鉤子已被 `useQueryClient` 鉤子取代。 + +它會回傳其元件樹所提供的 `queryClient`,除了重新命名外,應該不需要太多調整。 + +### 查詢鍵 (Query key) 的部分不再自動展開傳遞給查詢函式。 + +現在建議使用內聯函式將參數傳遞給查詢函式: + +```tsx +// 舊版 +useQuery(['post', id], (_key, id) => fetchPost(id)) + +// 新版 +useQuery(['post', id], () => fetchPost(id)) +``` + +如果仍然堅持不使用內聯函式,可以使用新傳入的 `QueryFunctionContext`: + +```tsx +useQuery(['post', id], (context) => fetchPost(context.queryKey[1])) +``` + +### 無限查詢 (Infinite Query) 的頁面參數現在透過 `QueryFunctionContext.pageParam` 傳遞 + +先前它們是作為查詢函式中的最後一個查詢鍵參數加入的,但這對某些模式來說證明是困難的 + +```tsx +// 舊版 +useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam)) + +// 新版 +useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam)) +``` + +### `usePaginatedQuery()` 已被移除,改為使用 `keepPreviousData` 選項 + +新的 `keepPreviousData` 選項適用於 `useQuery` 和 `useInfiniteQuery`,並將對資料產生相同的「延遲」效果: + +```tsx +import { useQuery } from 'react-query' + +function Page({ page }) { + const { data } = useQuery(['page', page], fetchPage, { + keepPreviousData: true, + }) +} +``` + +### `useInfiniteQuery()` 現在是雙向的 + +`useInfiniteQuery()` 的介面已更改,以完全支援雙向無限列表。 + +- `options.getFetchMore` 已更名為 `options.getNextPageParam` +- `queryResult.canFetchMore` 已更名為 `queryResult.hasNextPage` +- `queryResult.fetchMore` 已更名為 `queryResult.fetchNextPage` +- `queryResult.isFetchingMore` 已更名為 `queryResult.isFetchingNextPage` +- 新增了 `options.getPreviousPageParam` 選項 +- 新增了 `queryResult.hasPreviousPage` 屬性 +- 新增了 `queryResult.fetchPreviousPage` 屬性 +- 新增了 `queryResult.isFetchingPreviousPage` +- 無限查詢的 `data` 現在是一個物件,包含 `pages` 和用於擷取這些頁面的 `pageParams`:`{ pages: [data, data, data], pageParams: [...]}` + +單一方向: + +```tsx +const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = + useInfiniteQuery( + 'projects', + ({ pageParam = 0 }) => fetchProjects(pageParam), + { + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + }, + ) +``` + +雙向: + +```tsx +const { + data, + fetchNextPage, + fetchPreviousPage, + hasNextPage, + hasPreviousPage, + isFetchingNextPage, + isFetchingPreviousPage, +} = useInfiniteQuery( + 'projects', + ({ pageParam = 0 }) => fetchProjects(pageParam), + { + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, + }, +) +``` + +單一方向反向: + +```tsx +const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = + useInfiniteQuery( + 'projects', + ({ pageParam = 0 }) => fetchProjects(pageParam), + { + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + }, + ) +``` + +### 無限查詢的資料現在包含頁面陣列和用於擷取這些頁面的 `pageParams`。 + +這使得資料和頁面參數的操作更加容易,例如移除第一頁資料及其參數: + +```tsx +queryClient.setQueryData(['projects'], (data) => ({ + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1), +})) +``` + +### `useMutation` 現在回傳一個物件而非陣列 + +雖然舊的方式讓我們回想起第一次發現 `useState` 時的溫暖感覺,但這種感覺並沒有持續太久。現在變更回傳的是一個單一物件。 + +```tsx +// 舊版: +const [mutate, { status, reset }] = useMutation() + +// 新版: +const { mutate, status, reset } = useMutation() +``` + +### `mutation.mutate` 不再回傳一個 Promise + +- `[mutate]` 變數已更改為 `mutation.mutate` 函式 +- 新增了 `mutation.mutateAsync` 函式 + +我們收到了許多關於此行為的問題,因為使用者期望 Promise 的行為像一個普通的 Promise。 + +因此,`mutate` 函式現在分為 `mutate` 和 `mutateAsync` 兩個函式。 + +`mutate` 函式可用於使用回呼時: + +```tsx +const { mutate } = useMutation({ mutationFn: addTodo }) + +mutate('todo', { + onSuccess: (data) => { + console.log(data) + }, + onError: (error) => { + console.error(error) + }, + onSettled: () => { + console.log('settled') + }, +}) +``` + +`mutateAsync` 函式可用於使用 async/await 時: + +```tsx +const { mutateAsync } = useMutation({ mutationFn: addTodo }) + +try { + const data = await mutateAsync('todo') + console.log(data) +} catch (error) { + console.error(error) +} finally { + console.log('settled') +} +``` + +### `useQuery` 的物件語法現在使用折疊配置: + +```tsx +// 舊版: +useQuery({ + queryKey: 'posts', + queryFn: fetchPosts, + config: { staleTime: Infinity }, +}) + +// 新版: +useQuery({ + queryKey: 'posts', + queryFn: fetchPosts, + staleTime: Infinity, +}) +``` + +### 如果設定了 `QueryOptions.enabled` 選項,它必須是一個布林值 (`true`/`false`) + +`enabled` 查詢選項現在僅在值為 `false` 時停用查詢。 +如果需要,可以使用 `!!userId` 或 `Boolean(userId)` 進行轉換,如果傳遞了非布林值,將會拋出一個方便的錯誤。 + +### `QueryOptions.initialStale` 選項已被移除 + +`initialStale` 查詢選項已被移除,初始資料現在被視為常規資料。 +這意味著如果提供了 `initialData`,查詢預設會在掛載時重新擷取。 +如果不希望立即重新擷取,可以定義一個 `staleTime`。 + +### `QueryOptions.forceFetchOnMount` 選項已被 `refetchOnMount: 'always'` 取代 + +老實說,我們累積了太多 `refetchOn____` 選項,所以這應該能讓事情更清晰。 + +### `QueryOptions.refetchOnMount` 選項現在僅適用於其父元件,而非所有查詢觀察者 + +當 `refetchOnMount` 設為 `false` 時,任何其他元件都被阻止在掛載時重新擷取。 +在版本 3 中,僅設定了此選項的元件不會在掛載時重新擷取。 + +### `QueryOptions.queryFnParamsFilter` 已被移除,改為使用新的 `QueryFunctionContext` 物件。 + +`queryFnParamsFilter` 選項已被移除,因為查詢函式現在接收的是 `QueryFunctionContext` 物件,而非查詢鍵。 + +由於 `QueryFunctionContext` 也包含查詢鍵,參數仍然可以在查詢函式內部過濾。 + +### `QueryOptions.notifyOnStatusChange` 選項已被新的 `notifyOnChangeProps` 和 `notifyOnChangePropsExclusions` 選項取代。 + +透過這些新選項,可以更細緻地配置元件應在何時重新渲染。 + +僅在 `data` 或 `error` 屬性變更時重新渲染: + +```tsx +import { useQuery } from 'react-query' + +function User() { + const { data } = useQuery(['user'], fetchUser, { + notifyOnChangeProps: ['data', 'error'], + }) + return
    Username: {data.username}
    +} +``` + +防止在 `isStale` 屬性變更時重新渲染: + +```tsx +import { useQuery } from 'react-query' + +function User() { + const { data } = useQuery(['user'], fetchUser, { + notifyOnChangePropsExclusions: ['isStale'], + }) + return
    Username: {data.username}
    +} +``` + +### `QueryResult.clear()` 函式已更名為 `QueryResult.remove()` + +雖然它被稱為 `clear`,但它實際上只是從快取中移除查詢。現在的名稱更符合其功能。 + +### `QueryResult.updatedAt` 屬性已被拆分為 `QueryResult.dataUpdatedAt` 和 `QueryResult.errorUpdatedAt` 屬性 + +由於資料和錯誤可以同時存在,`updatedAt` 屬性已被拆分為 `dataUpdatedAt` 和 `errorUpdatedAt`。 + +### `setConsole()` 已被新的 `setLogger()` 函式取代 + +```tsx +import { setLogger } from 'react-query' + +// 使用 Sentry 記錄 +setLogger({ + error: (error) => { + Sentry.captureException(error) + }, +}) + +// 使用 Winston 記錄 +setLogger(winston.createLogger()) +``` + +### React Native 不再需要覆寫記錄器 + +為了防止在查詢失敗時顯示錯誤畫面,在 React Native 中需要手動變更 Console: + +```tsx +import { setConsole } from 'react-query' + +setConsole({ + log: console.log, + warn: console.warn, + error: console.warn, +}) +``` + +在版本 3 中,**當 React Query 在 React Native 中使用時,這會自動完成**。 + +### TypeScript + +#### `QueryStatus` 已從 [enum](https://www.typescriptlang.org/docs/handbook/enums.html#string-enums) 更改為 [union type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) + +因此,如果你正在檢查查詢或變更的 `status` 屬性與 `QueryStatus` enum 屬性,現在必須檢查它與 enum 先前為每個屬性持有的字串字面值。 + +因此,你必須將 enum 屬性更改為其等效的字串字面值,如下所示: + +- `QueryStatus.Idle` -> `'idle'` +- `QueryStatus.Loading` -> `'loading'` +- `QueryStatus.Error` -> `'error'` +- `QueryStatus.Success` -> `'success'` + +以下是你需要進行的更改範例: + +```tsx +- import { useQuery, QueryStatus } from 'react-query'; // [!code --] ++ import { useQuery } from 'react-query'; // [!code ++] + +const { data, status } = useQuery(['post', id], () => fetchPost(id)) + +- if (status === QueryStatus.Loading) { // [!code --] ++ if (status === 'loading') { // [!code ++] + ... +} + +- if (status === QueryStatus.Error) { // [!code --] ++ if (status === 'error') { // [!code ++] + ... +} +``` + +## 新功能 + +#### 查詢資料選擇器 (Query Data Selectors) + +`useQuery` 和 `useInfiniteQuery` 鉤子現在有一個 `select` 選項,可以選擇或轉換查詢結果的部分內容。 + +```tsx +import { useQuery } from 'react-query' + +function User() { + const { data } = useQuery(['user'], fetchUser, { + select: (user) => user.username, + }) + return
    Username: {data}
    +} +``` + +將 `notifyOnChangeProps` 選項設為 `['data', 'error']`,以僅在選取的資料變更時重新渲染。 + +#### `useQueries()` 鉤子,用於可變長度的平行查詢執行 + +希望在迴圈中執行 `useQuery` 嗎?鉤子的規則說不行,但有了新的 `useQueries()` 鉤子,你可以! + +```tsx +import { useQueries } from 'react-query' + +function Overview() { + const results = useQueries([ + { queryKey: ['post', 1], queryFn: fetchPost }, + { queryKey: ['post', 2], queryFn: fetchPost }, + ] +``` diff --git a/docs/zh-hant/framework/react/guides/migrating-to-react-query-4.md b/docs/zh-hant/framework/react/guides/migrating-to-react-query-4.md new file mode 100644 index 00000000000..8d0b2d605ef --- /dev/null +++ b/docs/zh-hant/framework/react/guides/migrating-to-react-query-4.md @@ -0,0 +1,328 @@ +--- +source-updated-at: '2025-03-31T09:06:11.000Z' +translation-updated-at: '2025-05-08T20:25:07.836Z' +id: migrating-to-react-query-4 +title: 遷移至 v4 +--- + +## 重大變更 + +v4 是一個主要版本,因此需要注意以下重大變更: + +### react-query 現在改為 @tanstack/react-query + +您需要解除安裝舊依賴並安裝新依賴,同時變更導入語句: + +``` +npm uninstall react-query +npm install @tanstack/react-query +npm install @tanstack/react-query-devtools +``` + +```tsx +- import { useQuery } from 'react-query' // [!code --] +- import { ReactQueryDevtools } from 'react-query/devtools' // [!code --] + ++ import { useQuery } from '@tanstack/react-query' // [!code ++] ++ import { ReactQueryDevtools } from '@tanstack/react-query-devtools' // [!code ++] +``` + +#### 代碼轉換工具 (Codemod) + +為了簡化導入遷移,v4 提供了一個代碼轉換工具。 + +> 此代碼轉換工具會盡力協助您遷移重大變更。請徹底檢查生成的代碼!此外,有些邊緣案例可能無法被代碼轉換工具發現,因此請注意日誌輸出。 + +您可以透過以下任一命令輕鬆應用它: + +如果要針對 `.js` 或 `.jsx` 檔案執行,請使用以下命令: + +``` +npx jscodeshift ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js +``` + +如果要針對 `.ts` 或 `.tsx` 檔案執行,請使用以下命令: + +``` +npx jscodeshift ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js +``` + +請注意,在 `TypeScript` 的情況下,您需要使用 `tsx` 作為解析器,否則代碼轉換工具將無法正確應用! + +**注意:** 應用代碼轉換工具可能會破壞您的代碼格式,因此在應用後請別忘記執行 `prettier` 和/或 `eslint`! + +**注意:** 代碼轉換工具 _僅_ 會變更導入語句 — 您仍需手動安裝獨立的開發工具套件。 + +### 查詢鍵 (Query Keys) 和變異鍵 (Mutation Keys) 必須為陣列 + +在 v3 中,查詢鍵和變異鍵可以是字串或陣列。React Query 內部始終僅使用陣列鍵,有時會將其暴露給使用者。例如,在 `queryFn` 中,您始終會獲得陣列鍵,以便更容易使用[預設查詢函式](./default-query-function.md)。 + +然而,我們並未在所有 API 中貫徹這一概念。例如,當使用[查詢過濾器](./filters.md)中的 `predicate` 函式時,您會獲得原始查詢鍵。如果您混合使用陣列和字串作為查詢鍵,這將使處理此類函式變得困難。使用全域回調時也是如此。 + +為了簡化所有 API,我們決定讓所有鍵僅為陣列: + +```tsx +;-useQuery('todos', fetchTodos) + // [!code --] + useQuery(['todos'], fetchTodos) // [!code ++] +``` + +#### 代碼轉換工具 + +為了簡化遷移,我們決定提供一個代碼轉換工具。 + +> 此代碼轉換工具會盡力協助您遷移重大變更。請徹底檢查生成的代碼!此外,有些邊緣案例可能無法被代碼轉換工具發現,因此請注意日誌輸出。 + +您可以透過以下任一命令輕鬆應用它: + +如果要針對 `.js` 或 `.jsx` 檔案執行,請使用以下命令: + +``` +npx jscodeshift ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js +``` + +如果要針對 `.ts` 或 `.tsx` 檔案執行,請使用以下命令: + +``` +npx jscodeshift ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js +``` + +請注意,在 `TypeScript` 的情況下,您需要使用 `tsx` 作為解析器,否則代碼轉換工具將無法正確應用! + +**注意:** 應用代碼轉換工具可能會破壞您的代碼格式,因此在應用後請別忘記執行 `prettier` 和/或 `eslint`! + +### 已移除 idle 狀態 + +隨著新的 [fetchStatus](./queries.md#fetchstatus) 引入以提供更好的離線支援,`idle` 狀態變得無關緊要,因為 `fetchStatus: 'idle'` 能更好地捕捉相同狀態。更多資訊請參閱[為什麼有兩種不同狀態](./queries.md#why-two-different-states)。 + +這主要會影響尚未有任何 `data` 的 `disabled` 查詢,因為這些查詢之前處於 `idle` 狀態: + +```tsx +- status: 'idle' // [!code --] ++ status: 'loading' // [!code ++] ++ fetchStatus: 'idle' // [!code ++] +``` + +此外,請參閱[依賴查詢指南](./dependent-queries.md) + +#### 停用的查詢 + +由於此變更,停用的查詢(即使是暫時停用的查詢)將以 `loading` 狀態開始。為了簡化遷移,特別是為了有一個良好的標誌來判斷何時顯示載入指示器,您可以檢查 `isInitialLoading` 而非 `isLoading`: + +```tsx +;-isLoading + // [!code --] + isInitialLoading // [!code ++] +``` + +另請參閱[停用查詢指南](./disabling-queries.md#isInitialLoading) + +### `useQueries` 的新 API + +`useQueries` 鉤子現在接受一個帶有 `queries` 屬性的物件作為輸入。`queries` 屬性的值是一個查詢陣列(此陣列與 v3 中傳遞給 `useQueries` 的內容相同)。 + +```tsx +;-useQueries([ + { queryKey1, queryFn1, options1 }, + { queryKey2, queryFn2, options2 }, +]) + // [!code --] + useQueries({ + queries: [ + { queryKey1, queryFn1, options1 }, + { queryKey2, queryFn2, options2 }, + ], + }) // [!code ++] +``` + +### undefined 不再是成功查詢的有效快取值 + +為了能夠透過返回 `undefined` 來中止更新,我們必須將 `undefined` 設為無效的快取值。這與 react-query 的其他概念一致,例如從 [initialData 函式](./initial-query-data.md#initial-data-function) 返回 `undefined` 也 _不會_ 設定資料。 + +此外,在 `queryFn` 中添加日誌記錄很容易產生 `Promise`: + +```tsx +useQuery(['key'], () => + axios.get(url).then((result) => console.log(result.data)), +) +``` + +現在這在類型層面上被禁止;在運行時,`undefined` 將被轉換為 _失敗的 Promise_,這意味著您將獲得一個 `error`,並且在開發模式下也會將此錯誤記錄到控制台。 + +### 查詢和變異預設需要網路連接才能運行 + +請閱讀關於線上/離線支援的[新功能公告](#proper-offline-support),以及專門的[網路模式](./network-mode.md)頁面。 + +儘管 React Query 是一個可以用於任何產生 Promise 的異步狀態管理器,但它最常與資料獲取庫一起用於資料獲取。這就是為什麼預設情況下,如果沒有網路連接,查詢和變異將被 `paused`。如果您希望保持之前的行為,可以為查詢和變異全域設定 `networkMode: offlineFirst`: + +```tsx +new QueryClient({ + defaultOptions: { + queries: { + networkMode: 'offlineFirst', + }, + mutations: { + networkMode: 'offlineFirst', + }, + }, +}) +``` + +### `notifyOnChangeProps` 屬性不再接受 `"tracked"` 作為值 + +`notifyOnChangeProps` 選項不再接受 `"tracked"` 值。相反,`useQuery` 預設會追蹤屬性。所有使用 `notifyOnChangeProps: "tracked"` 的查詢應通過移除此選項來更新。 + +如果您希望在任何查詢中繞過此行為以模擬 v3 的預設行為(即每當查詢變更時重新渲染),`notifyOnChangeProps` 現在接受 `"all"` 值來選擇退出預設的智能追蹤優化。 + +### `notifyOnChangePropsExclusion` 已被移除 + +在 v4 中,`notifyOnChangeProps` 預設為 v3 的 `"tracked"` 行為,而非 `undefined`。現在 `"tracked"` 是 v4 的預設行為,因此不再需要包含此配置選項。 + +### `cancelRefetch` 行為一致化 + +`cancelRefetch` 選項可以傳遞給所有強制獲取查詢的函式,即: + +- `queryClient.refetchQueries` +- `queryClient.invalidateQueries` +- `queryClient.resetQueries` +- `refetch` 從 `useQuery` 返回 +- `fetchNextPage` 和 `fetchPreviousPage` 從 `useInfiniteQuery` 返回 + +除了 `fetchNextPage` 和 `fetchPreviousPage`,此標誌預設為 `false`,這是不一致且可能帶來問題的:如果在一個緩慢的獲取已在進行中時呼叫 `refetchQueries` 或 `invalidateQueries`,可能不會獲得最新結果,因為此重新獲取將被跳過。 + +我們認為,如果查詢被您編寫的代碼主動重新獲取,它應該預設重新開始獲取。 + +這就是為什麼此標誌現在對上述所有方法預設為 _true_。這也意味著如果您連續呼叫 `refetchQueries` 兩次而不等待,它現在將取消第一次獲取並用第二次重新開始: + +``` +queryClient.refetchQueries({ queryKey: ['todos'] }) +// 這將中止先前的重新獲取並開始新的獲取 +queryClient.refetchQueries({ queryKey: ['todos'] }) +``` + +您可以通過明確傳遞 `cancelRefetch:false` 來選擇退出此行為: + +``` +queryClient.refetchQueries({ queryKey: ['todos'] }) +// 這不會中止先前的重新獲取 — 它將被忽略 +queryClient.refetchQueries({ queryKey: ['todos'] }, { cancelRefetch: false }) +``` + +> 注意:對於自動觸發的獲取(例如由於查詢掛載或窗口焦點重新獲取),行為沒有變更。 + +### 查詢過濾器 + +[查詢過濾器](./filters.md) 是一個具有特定條件以匹配查詢的物件。歷史上,過濾選項主要是布林標誌的組合。然而,組合這些標誌可能導致不可能的狀態。具體來說: + +``` +active?: boolean + - 設為 true 時將匹配活動查詢。 + - 設為 false 時將匹配非活動查詢。 +inactive?: boolean + - 設為 true 時將匹配非活動查詢。 + - 設為 false 時將匹配活動查詢。 +``` + +這些標誌在一起使用時效果不佳,因為它們是互斥的。將兩個標誌都設為 `false` 可以根據描述匹配所有查詢,或者不匹配任何查詢,這沒有多大意義。 + +在 v4 中,這些過濾器已合併為單一過濾器,以更好地顯示意圖: + +```tsx +- active?: boolean // [!code --] +- inactive?: boolean // [!code --] ++ type?: 'active' | 'inactive' | 'all' // [!code ++] +``` + +過濾器預設為 `all`,您可以選擇僅匹配 `active` 或 `inactive` 查詢。 + +#### refetchActive / refetchInactive + +[queryClient.invalidateQueries](../../../reference/QueryClient.md#queryclientinvalidatequeries) 有兩個額外的類似標誌: + +``` +refetchActive: Boolean + - 預設為 true + - 設為 false 時,匹配重新獲取謂詞且正通過 useQuery 等渲染的查詢將不會在後台重新獲取,僅標記為無效。 +refetchInactive: Boolean + - 預設為 false + - 設為 true 時,匹配重新獲取謂詞且未通過 useQuery 等渲染的查詢將同時標記為無效並在後台重新獲取。 +``` + +出於相同原因,這些也已合併: + +```tsx +- refetchActive?: boolean // [!code --] +- refetchInactive?: boolean // [!code --] ++ refetchType?: 'active' | 'inactive' | 'all' | 'none' // [!code ++] +``` + +此標誌預設為 `active`,因為 `refetchActive` 預設為 `true`。這意味著我們還需要一種方法來告訴 `invalidateQueries` 完全不重新獲取,這就是為什麼這裡也允許第四個選項(`none`)。 + +### `onSuccess` 不再從 `setQueryData` 呼叫 + +這讓許多人感到困惑,並且如果從 `onSuccess` 內部呼叫 `setQueryData`,還會導致無限循環。當與 `staleTime` 結合使用時,這也經常是錯誤的來源,因為如果僅從快取讀取資料,`onSuccess` _不會_ 被呼叫。 + +與 `onError` 和 `onSettled` 類似,`onSuccess` 回調現在與發出的請求綁定。沒有請求 -> 沒有回調。 + +如果您想監聽 `data` 欄位的變更,最好使用 `useEffect`,其中 `data` 是依賴陣列的一部分。由於 React Query 通過結構共享確保資料穩定,效果不會在每次後台重新獲取時執行,而僅在資料中的某些內容變更時執行: + +``` +const { data } = useQuery({ queryKey, queryFn }) +React.useEffect(() => mySideEffectHere(data), [data]) +``` + +### `persistQueryClient` 和相應的持久化插件不再處於實驗階段並已更名 + +插件 `createWebStoragePersistor` 和 `createAsyncStoragePersistor` 已更名為 [`createSyncStoragePersister`](../plugins/createSyncStoragePersister.md) 和 [`createAsyncStoragePersister`](../plugins/createAsyncStoragePersister.md)。`persistQueryClient` 中的介面 `Persistor` 也更名為 `Persister`。查看[此 StackExchange](https://english.stackexchange.com/questions/206893/persister-or-persistor) 了解此變更的動機。 + +由於這些插件不再處於實驗階段,它們的導入路徑也已更新: + +```tsx +- import { persistQueryClient } from 'react-query/persistQueryClient-experimental' // [!code --] +- import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental' // [!code --] +- import { createAsyncStoragePersistor } from 'react-query/createAsyncStoragePersistor-experimental' // [!code --] + ++ import { persistQueryClient } from '@tanstack/react-query-persist-client' // [!code ++] ++ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' // [!code ++] ++ import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' // [!code ++] +``` + +### 不再支援 Promise 上的 `cancel` 方法 + +[舊的 `cancel` 方法](./query-cancellation.md#old-cancel-function) 允許您在 Promise 上定義一個 `cancel` 函式,然後由函式庫用於支援查詢取消,現已移除。我們建議使用[較新的 API](./query-cancellation.md)(自 v3.30.0 引入)進行查詢取消,該 API 內部使用 [`AbortController` API](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) 並為您的查詢函式提供一個 [`AbortSignal` 實例](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) 以支援查詢取消。 + +### TypeScript + +類型現在需要使用 TypeScript v4.1 或更高版本 + +### 支援的瀏覽器 + +從 v4 開始,React Query 針對現代瀏覽器進行了優化。我們已更新 browserslist 以產生更現代、性能更好且更小的套件包。您可以在[安裝說明](../../installation#requirements)中閱讀相關要求。 + +### `setLogger` 已被移除 + +過去可以通過呼叫 `setLogger` 全域變更記錄器。在 v4 中,該函式被替換為創建 `QueryClient` 時的一個可選欄位。 + +```tsx +- import { QueryClient, setLogger } from 'react-query'; // [!code --] ++ import { QueryClient } from '@tanstack/react-query'; // [!code ++] + +- setLogger(customLogger) // [!code --] +- const queryClient = new QueryClient(); // [!code --] ++ const queryClient = new QueryClient({ logger: customLogger }) // [!code ++] +``` + +### 伺服器端不再預設手動垃圾回收 + +在 v3 中,React Query 會預設快取查詢結果 5 分鐘,然後手動垃圾回收這些資料。此預設值也應用於伺服器端的 React Query。 + +這導致高記憶體消耗和掛起的進程等待此手動垃圾回收完成。在 v4 中,伺服器端的 `cacheTime` 現在預設設為 `Infinity`,有效地禁用手動垃圾回收(NodeJS 進程將在請求完成後清除所有內容)。 + +此變更僅影響使用伺服器端 React Query 的使用者,例如與 Next.js 一起使用時。如果您手動設定 `cacheTime`,這將不會影響您(儘管您可能希望鏡像 diff --git a/docs/zh-hant/framework/react/guides/migrating-to-v5.md b/docs/zh-hant/framework/react/guides/migrating-to-v5.md new file mode 100644 index 00000000000..67204d9ee61 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/migrating-to-v5.md @@ -0,0 +1,327 @@ +--- +source-updated-at: '2025-04-10T12:15:03.000Z' +translation-updated-at: '2025-05-08T20:25:00.703Z' +id: migrating-to-tanstack-query-5 +title: 遷移至 v5 +--- + +## 重大變更 + +v5 是一個主要版本,因此需要注意以下重大變更: + +### 僅支援單一簽名格式,統一使用物件形式 + +`useQuery` 及其相關函式過去在 TypeScript 中有多種重載形式:即函式可以有多種呼叫方式。這不僅在類型維護上相當困難,還需要在運行時檢查第一和第二參數的類型,以正確建立選項。 + +現在我們僅支援物件格式。 + +```tsx +useQuery(key, fn, options) // [!code --] +useQuery({ queryKey, queryFn, ...options }) // [!code ++] +useInfiniteQuery(key, fn, options) // [!code --] +useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +useMutation(fn, options) // [!code --] +useMutation({ mutationFn, ...options }) // [!code ++] +useIsFetching(key, filters) // [!code --] +useIsFetching({ queryKey, ...filters }) // [!code ++] +useIsMutating(key, filters) // [!code --] +useIsMutating({ mutationKey, ...filters }) // [!code ++] +``` + +```tsx +queryClient.isFetching(key, filters) // [!code --] +queryClient.isFetching({ queryKey, ...filters }) // [!code ++] +queryClient.ensureQueryData(key, filters) // [!code --] +queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++] +queryClient.getQueriesData(key, filters) // [!code --] +queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++] +queryClient.setQueriesData(key, updater, filters, options) // [!code --] +queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++] +queryClient.removeQueries(key, filters) // [!code --] +queryClient.removeQueries({ queryKey, ...filters }) // [!code ++] +queryClient.resetQueries(key, filters, options) // [!code --] +queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.cancelQueries(key, filters, options) // [!code --] +queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.invalidateQueries(key, filters, options) // [!code --] +queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.refetchQueries(key, filters, options) // [!code --] +queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.fetchQuery(key, fn, options) // [!code --] +queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchQuery(key, fn, options) // [!code --] +queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.fetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +``` + +```tsx +queryCache.find(key, filters) // [!code --] +queryCache.find({ queryKey, ...filters }) // [!code ++] +queryCache.findAll(key, filters) // [!code --] +queryCache.findAll({ queryKey, ...filters }) // [!code ++] +``` + +### `queryClient.getQueryData` 現在僅接受 `queryKey` 作為參數 + +`queryClient.getQueryData` 的參數變更為僅接受 `queryKey` + +```tsx +queryClient.getQueryData(queryKey, filters) // [!code --] +queryClient.getQueryData(queryKey) // [!code ++] +``` + +### `queryClient.getQueryState` 現在僅接受 `queryKey` 作為參數 + +`queryClient.getQueryState` 的參數變更為僅接受 `queryKey` + +```tsx +queryClient.getQueryState(queryKey, filters) // [!code --] +queryClient.getQueryState(queryKey) // [!code ++] +``` + +#### 代碼修改工具 (Codemod) + +為了簡化移除重載的遷移過程,v5 提供了一個代碼修改工具。 + +> 代碼修改工具會盡力協助您遷移重大變更。請徹底檢查生成的代碼!此外,有些邊緣情況無法通過代碼修改工具找到,因此請注意日誌輸出。 + +如果您想針對 `.js` 或 `.jsx` 檔案執行,請使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +如果您想針對 `.ts` 或 `.tsx` 檔案執行,請使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +請注意,在 `TypeScript` 的情況下,您需要使用 `tsx` 作為解析器;否則代碼修改工具將無法正確應用! + +**注意:** 應用代碼修改工具可能會破壞您的代碼格式,因此請在應用代碼修改工具後執行 `prettier` 和/或 `eslint`! + +關於代碼修改工具如何運作的一些說明: + +- 一般情況下,我們會尋找幸運的情況,即第一個參數是一個物件表達式,並且包含 "queryKey" 或 "mutationKey" 屬性(取決於正在轉換的鉤子/方法呼叫)。如果是這種情況,您的代碼已經符合新的簽名,因此代碼修改工具不會觸碰它。🎉 +- 如果上述條件不滿足,代碼修改工具將檢查第一個參數是否為陣列表達式或引用陣列表達式的標識符。如果是這種情況,代碼修改工具會將其放入物件表達式中,然後它將成為第一個參數。 +- 如果可以推斷出物件參數,代碼修改工具將嘗試將現有屬性複製到新創建的物件中。 +- 如果代碼修改工具無法推斷用法,則會在控制台留下訊息。訊息包含使用情況的檔案名稱和行號。在這種情況下,您需要手動進行遷移。 +- 如果轉換導致錯誤,您也會在控制台看到訊息。此訊息將通知您發生了意外情況,請手動進行遷移。 + +### 移除了 `useQuery`(和 `QueryObserver`)上的回調函式 + +`onSuccess`、`onError` 和 `onSettled` 已從查詢中移除。它們在突變中未被觸碰。請參閱 [此 RFC](https://github.com/TanStack/query/discussions/5279) 了解此變更的動機以及替代方案。 + +### `refetchInterval` 回調函式現在僅傳遞 `query` + +這簡化了回調的調用方式(`refetchOnWindowFocus`、`refetchOnMount` 和 `refetchOnReconnect` 回調也僅傳遞查詢),並修復了當回調獲取由 `select` 轉換的數據時的一些類型問題。 + +```tsx +- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --] ++ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++] +``` + +您仍然可以通過 `query.state.data` 訪問數據,但它不會是被 `select` 轉換後的數據。如果您需要訪問轉換後的數據,可以對 `query.state.data` 再次調用轉換函式。 + +### 移除了 `useQuery` 中的 `remove` 方法 + +以前,`remove` 方法用於從 `queryCache` 中移除查詢而不通知觀察者。它最適合用於命令式移除不再需要的數據,例如在用戶登出時。 + +但在查詢仍然活躍時這樣做沒有太大意義,因為它只會在下一次重新渲染時觸發硬載入狀態。 + +如果您仍然需要移除查詢,可以使用 `queryClient.removeQueries({queryKey: key})` + +```tsx +const queryClient = useQueryClient() +const query = useQuery({ queryKey, queryFn }) + +query.remove() // [!code --] +queryClient.removeQueries({ queryKey }) // [!code ++] +``` + +### 最低要求的 TypeScript 版本現在是 4.7 + +主要是因為在類型推斷方面有一個重要的修復。請參閱此 [TypeScript 問題](https://github.com/microsoft/TypeScript/issues/43371) 了解更多信息。 + +### 移除了 `useQuery` 中的 `isDataEqual` 選項 + +以前,此函式用於指示是否使用先前的 `data`(`true`)或新數據(`false`)作為查詢的解析數據。 + +您可以通過向 `structuralSharing` 傳遞一個函式來實現相同的功能: + +```tsx + import { replaceEqualDeep } from '@tanstack/react-query' + +- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --] ++ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++] +``` + +### 移除了已棄用的自定義記錄器 (logger) + +自定義記錄器在 v4 中已被棄用,並在此版本中移除。記錄僅在開發模式下有效,而在開發模式下傳遞自定義記錄器是不必要的。 + +### 支援的瀏覽器 + +我們更新了我們的 browserslist 以生成更現代、性能更好且更小的套件。您可以在此處閱讀要求 [here](../../installation#requirements)。 + +### 私有類別欄位和方法 + +TanStack Query 一直都有類別上的私有欄位和方法,但它們並不是真正的私有——它們只是在 `TypeScript` 中是私有的。我們現在使用 [ECMAScript 私有類別特性](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields),這意味著這些欄位現在在運行時是真正私有的,無法從外部訪問。 + +### 將 `cacheTime` 更名為 `gcTime` + +幾乎每個人都誤解了 `cacheTime`。它聽起來像是「數據被緩存的時間量」,但這是不正確的。 + +`cacheTime` 在查詢仍在使用時不會執行任何操作。它僅在查詢變為未使用後才會生效。時間過後,數據將被「垃圾回收」,以避免緩存增長。 + +`gc` 指的是「垃圾回收」時間。這有點技術性,但也是計算機科學中 [眾所周知的縮寫]()。 + +```tsx +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, // [!code --] ++ gcTime: 10 * MINUTE, // [!code ++] + }, + }, +}) +``` + +### 將 `useErrorBoundary` 選項更名為 `throwOnError` + +為了使 `useErrorBoundary` 選項更加框架無關,並避免與 React 鉤子的既定前綴「`use`」和「ErrorBoundary」組件名稱混淆,它已更名為 `throwOnError`,以更準確地反映其功能。 + +### TypeScript:`Error` 現在是錯誤的默認類型,而不是 `unknown` + +儘管在 JavaScript 中,您可以 `throw` 任何東西(這使得 `unknown` 是最正確的類型),但幾乎總是會拋出 `Error`(或 `Error` 的子類)。此變更使得在 TypeScript 中處理 `error` 欄位更加容易。 + +如果您想拋出不是 Error 的東西,現在必須自己設置泛型: + +```ts +useQuery({ + queryKey: ['some-query'], + queryFn: async () => { + if (Math.random() > 0.5) { + throw 'some error' + } + return 42 + }, +}) +``` + +有關全局設置不同類型錯誤的方法,請參閱 [TypeScript 指南](../typescript.md#registering-a-global-error)。 + +### 移除了 eslint `prefer-query-object-syntax` 規則 + +由於現在唯一支援的語法是物件語法,此規則不再需要 + +### 移除了 `keepPreviousData`,改用 `placeholderData` 的恆等函式 + +我們移除了 `keepPreviousData` 選項和 `isPreviousData` 標誌,因為它們的功能與 `placeholderData` 和 `isPlaceholderData` 標誌基本相同。 + +為了實現與 `keepPreviousData` 相同的功能,我們將先前的查詢 `data` 作為參數添加到 `placeholderData` 中,該參數接受一個恆等函式。因此,您只需向 `placeholderData` 提供一個恆等函式或使用 Tanstack Query 中包含的 `keepPreviousData` 函式。 + +> 需要注意的是,`useQueries` 不會在 `placeholderData` 函式中接收 `previousData` 作為參數。這是由於傳遞給陣列的查詢具有動態性質,這可能導致從佔位符和 queryFn 返回的結果形狀不同。 + +```tsx +import { + useQuery, ++ keepPreviousData // [!code ++] +} from "@tanstack/react-query"; + +const { + data, +- isPreviousData, // [!code --] ++ isPlaceholderData, // [!code ++] +} = useQuery({ + queryKey, + queryFn, +- keepPreviousData: true, // [!code --] ++ placeholderData: keepPreviousData // [!code ++] +}); +``` + +在 Tanstack Query 的上下文中,恆等函式指的是總是返回其提供的參數(即數據)而不變的函式。 + +```ts +useQuery({ + queryKey, + queryFn, + placeholderData: (previousData, previousQuery) => previousData, // 與 `keepPreviousData` 行為相同的恆等函式 +}) +``` + +然而,此變更有一些需要注意的地方: + +- `placeholderData` 總是會將您置於 `success` 狀態,而 `keepPreviousData` 會給您先前查詢的狀態。該狀態可能是 `error`,如果我們成功獲取數據後又遇到後台重新獲取錯誤。然而,錯誤本身並未共享,因此我們決定堅持 `placeholderData` 的行為。 +- `keepPreviousData` 會給您先前數據的 `dataUpdatedAt` 時間戳,而使用 `placeholderData` 時,`dataUpdatedAt` 將保持為 `0`。如果您想在屏幕上連續顯示該時間戳,這可能會很煩人。但您可以通過 `useEffect` 繞過它。 + + ```ts + const [updatedAt, setUpdatedAt] = useState(0) + + const { data, dataUpdatedAt } = useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + }) + + useEffect(() => { + if (dataUpdatedAt > updatedAt) { + setUpdatedAt(dataUpdatedAt) + } + }, [dataUpdatedAt]) + ``` + +### 窗口焦點重新獲取不再監聽 `focus` 事件 + +現在僅使用 `visibilitychange` 事件。這是可能的,因為我們僅支援支援 `visibilitychange` 事件的瀏覽器。這修復了 [此處列出](https://github.com/TanStack/query/pull/4805) 的一系列問題。 + +### 網絡狀態不再依賴 `navigator.onLine` 屬性 + +`navigator.onLine` 在基於 Chromium 的瀏覽器中效果不佳。有 [許多問題](https://bugs.chromium.org/p/chromium/issues/list?q=navigator.online) 關於假陰性,這導致查詢被錯誤地標記為 `offline`。 + +為了避免這種情況,我們現在總是從 `online: true` 開始,並且僅監聽 `online` 和 `offline` 事件來更新狀態。 + +這應該會減少假陰性的可能性,但對於通過 serviceWorkers 加載的離線應用程序,這可能意味著假陽性,這些應用程序即使沒有互聯網連接也可以工作。 + +### 移除了自定義 `context` 屬性,改用自定義 `queryClient` 實例 + +在 v4 中,我們引入了向所有 react-query 鉤子傳遞自定義 `context` 的可能性。這在使用微前端時實現了適當的隔離。 + +然而,`context` 是一個僅限於 React 的特性。`context` 所做的只是讓我們能夠訪問 `queryClient`。我們可以通過允許直接傳入自定義 `queryClient` 來實現相同的隔離。 +這反過來將使其他框架能夠以框架無關的方式擁有相同的功能。 + +```tsx +import { queryClient } from './my-client' + +const { data } = useQuery( + { + queryKey: ['users', id], + queryFn: () => fetch(...), +- context: customContext // [!code --] + }, ++ queryClient, // [!code ++] +) +``` + +### 移除了 `refetchPage`,改用 `maxPages` + +在 v4 中,我們引入了通過 `refetchPage` 函式為無限查詢定義要重新獲取的頁面的可能性。 + +然而,重新獲取所有頁面可能導致 UI 不一致。此外,此選項在例如 `queryClient.refetchQueries` 上可用,但它僅對無限查詢有效,而不是「普通」查詢。 + +v5 包括一個新的 `maxPages` 選項,用於限制無限查詢中存儲在查詢數據中和重新獲取的頁面數量。此新功能處理了最初為 `refetchPage` 頁面功能識別的用例,而沒有相關問題。 + +### 新的 `dehydrate` API + +傳遞給 `dehydrate` 的選項已簡化。查詢和突變總是會被 diff --git a/docs/zh-hant/framework/react/guides/mutations.md b/docs/zh-hant/framework/react/guides/mutations.md new file mode 100644 index 00000000000..846bf053fd1 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/mutations.md @@ -0,0 +1,415 @@ +--- +source-updated-at: '2025-04-25T12:36:10.000Z' +translation-updated-at: '2025-05-08T20:24:32.006Z' +id: mutations +title: 變更 +--- + +與查詢 (queries) 不同,突變 (mutations) 通常用於建立/更新/刪除資料或執行伺服器副作用 (side-effects)。為此,TanStack Query 導出了 `useMutation` 鉤子 (hook)。 + +以下是一個新增待辦事項到伺服器的突變範例: + +[//]: # 'Example' + +```tsx +function App() { + const mutation = useMutation({ + mutationFn: (newTodo) => { + return axios.post('/todos', newTodo) + }, + }) + + return ( +
    + {mutation.isPending ? ( + 'Adding todo...' + ) : ( + <> + {mutation.isError ? ( +
    An error occurred: {mutation.error.message}
    + ) : null} + + {mutation.isSuccess ?
    Todo added!
    : null} + + + + )} +
    + ) +} +``` + +[//]: # 'Example' + +在任何時刻,突變只能處於以下其中一種狀態: + +- `isIdle` 或 `status === 'idle'` - 突變目前處於閒置或全新/重置狀態 +- `isPending` 或 `status === 'pending'` - 突變正在執行中 +- `isError` 或 `status === 'error'` - 突變遇到錯誤 +- `isSuccess` 或 `status === 'success'` - 突變成功且突變資料可用 + +除了這些主要狀態外,根據突變的狀態還可以獲取更多資訊: + +- `error` - 如果突變處於 `error` 狀態,可以透過 `error` 屬性取得錯誤資訊。 +- `data` - 如果突變處於 `success` 狀態,可以透過 `data` 屬性取得資料。 + +在上面的範例中,你還看到可以透過呼叫 `mutate` 函式並傳入**單一變數或物件**來將變數傳遞給突變函式。 + +即使只有變數,突變也沒什麼特別的,但當與 `onSuccess` 選項、[Query Client 的 `invalidateQueries` 方法](../../../reference/QueryClient.md#queryclientinvalidatequeries) 以及 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 一起使用時,突變就變成了一個非常強大的工具。 + +[//]: # 'Info1' + +> 重要提示:`mutate` 函式是一個非同步函式,這意味著在 **React 16 及更早版本**中,你不能直接在事件回呼中使用它。如果需要在 `onSubmit` 中存取事件,你必須將 `mutate` 包裝在另一個函式中。這是由於 [React 事件池化 (event pooling)](https://reactjs.org/docs/legacy-event-pooling.html) 的緣故。 + +[//]: # 'Info1' +[//]: # 'Example2' + +```tsx +// 這在 React 16 及更早版本中無法運作 +const CreateTodo = () => { + const mutation = useMutation({ + mutationFn: (event) => { + event.preventDefault() + return fetch('/api', new FormData(event.target)) + }, + }) + + return
    ...
    +} + +// 這樣可以運作 +const CreateTodo = () => { + const mutation = useMutation({ + mutationFn: (formData) => { + return fetch('/api', formData) + }, + }) + const onSubmit = (event) => { + event.preventDefault() + mutation.mutate(new FormData(event.target)) + } + + return
    ...
    +} +``` + +[//]: # 'Example2' + +## 重置突變狀態 + +有時你需要清除突變請求的 `error` 或 `data`。為此,你可以使用 `reset` 函式來處理: + +[//]: # 'Example3' + +```tsx +const CreateTodo = () => { + const [title, setTitle] = useState('') + const mutation = useMutation({ mutationFn: createTodo }) + + const onCreateTodo = (e) => { + e.preventDefault() + mutation.mutate({ title }) + } + + return ( +
    + {mutation.error && ( +
    mutation.reset()}>{mutation.error}
    + )} + setTitle(e.target.value)} + /> +
    + +
    + ) +} +``` + +[//]: # 'Example3' + +## 突變副作用 + +`useMutation` 提供了一些輔助選項,允許在突變生命週期的任何階段快速且輕鬆地執行副作用。這些選項對於[突變後使查詢失效並重新獲取](./invalidations-from-mutations.md)甚至[樂觀更新 (optimistic updates)](./optimistic-updates.md)都非常有用。 + +[//]: # 'Example4' + +```tsx +useMutation({ + mutationFn: addTodo, + onMutate: (variables) => { + // 突變即將發生! + + // 可選返回一個包含資料的上下文,用於例如回滾操作 + return { id: 1 } + }, + onError: (error, variables, context) => { + // 發生錯誤! + console.log(`rolling back optimistic update with id ${context.id}`) + }, + onSuccess: (data, variables, context) => { + // 成功! + }, + onSettled: (data, error, variables, context) => { + // 無論錯誤或成功...都沒關係! + }, +}) +``` + +[//]: # 'Example4' + +當在任何回呼函式中返回一個 Promise 時,它會先被等待,然後才會呼叫下一個回呼: + +[//]: # 'Example5' + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: async () => { + console.log("I'm first!") + }, + onSettled: async () => { + console.log("I'm second!") + }, +}) +``` + +[//]: # 'Example5' + +你可能會發現,除了在 `useMutation` 上定義的回呼外,你還想在呼叫 `mutate` 時**觸發額外的回呼**。這可以用來觸發元件特定的副作用。為此,你可以在突變變數之後向 `mutate` 函式提供任何相同的回呼選項。支援的選項包括:`onSuccess`、`onError` 和 `onSettled`。請注意,如果你的元件在突變完成*之前*卸載,這些額外的回呼將不會執行。 + +[//]: # 'Example6' + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 我會先觸發 + }, + onError: (error, variables, context) => { + // 我會先觸發 + }, + onSettled: (data, error, variables, context) => { + // 我會先觸發 + }, +}) + +mutate(todo, { + onSuccess: (data, variables, context) => { + // 我會第二個觸發! + }, + onError: (error, variables, context) => { + // 我會第二個觸發! + }, + onSettled: (data, error, variables, context) => { + // 我會第二個觸發! + }, +}) +``` + +[//]: # 'Example6' + +### 連續突變 + +在處理連續突變時,`onSuccess`、`onError` 和 `onSettled` 回呼的處理方式略有不同。當傳遞給 `mutate` 函式時,它們只會觸發*一次*,並且只有在元件仍然掛載時才會觸發。這是因為每次呼叫 `mutate` 函式時,突變觀察者 (observer) 會被移除並重新訂閱。相反地,`useMutation` 的處理程序會針對每個 `mutate` 呼叫執行。 + +> 請注意,傳遞給 `useMutation` 的 `mutationFn` 很可能是非同步的。在這種情況下,突變完成的順序可能與 `mutate` 函式呼叫的順序不同。 + +[//]: # 'Example7' + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 會被呼叫 3 次 + }, +}) + +const todos = ['Todo 1', 'Todo 2', 'Todo 3'] +todos.forEach((todo) => { + mutate(todo, { + onSuccess: (data, variables, context) => { + // 只會執行一次,針對最後一個突變 (Todo 3), + // 無論哪個突變先完成 + }, + }) +}) +``` + +[//]: # 'Example7' + +## Promise + +使用 `mutateAsync` 而不是 `mutate` 來獲取一個 Promise,該 Promise 會在成功時解析或在錯誤時拋出。例如,這可以用於組合副作用。 + +[//]: # 'Example8' + +```tsx +const mutation = useMutation({ mutationFn: addTodo }) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('done') +} +``` + +[//]: # 'Example8' + +## 重試 + +預設情況下,TanStack Query 不會在錯誤時重試突變,但可以透過 `retry` 選項來實現: + +[//]: # 'Example9' + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + retry: 3, +}) +``` + +[//]: # 'Example9' + +如果突變因裝置離線而失敗,它們將在裝置重新連接時以相同的順序重試。 + +## 持久化突變 + +如果需要,可以將突變持久化到儲存中,並在稍後恢復。這可以透過 hydration 函式來實現: + +[//]: # 'Example10' + +```tsx +const queryClient = new QueryClient() + +// 定義 "addTodo" 突變 +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables) => { + // 取消目前 todos 列表的查詢 + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 建立樂觀待辦事項 + const optimisticTodo = { id: uuid(), title: variables.title } + + // 將樂觀待辦事項新增到 todos 列表 + queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // 返回包含樂觀待辦事項的上下文 + return { optimisticTodo } + }, + onSuccess: (result, variables, context) => { + // 將 todos 列表中的樂觀待辦事項替換為結果 + queryClient.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === context.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, context) => { + // 從 todos 列表中移除樂觀待辦事項 + queryClient.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== context.optimisticTodo.id), + ) + }, + retry: 3, +}) + +// 在某個元件中啟動突變: +const mutation = useMutation({ mutationKey: ['addTodo'] }) +mutation.mutate({ title: 'title' }) + +// 如果突變因為裝置離線等原因被暫停, +// 可以在應用程式退出時將暫停的突變脫水 (dehydrate): +const state = dehydrate(queryClient) + +// 然後在應用程式啟動時再次水合 (hydrate): +hydrate(queryClient, state) + +// 恢復暫停的突變: +queryClient.resumePausedMutations() +``` + +[//]: # 'Example10' + +### 持久化離線突變 + +如果你使用 [persistQueryClient 插件](../plugins/persistQueryClient.md) 持久化離線突變,除非你提供預設的突變函式,否則在頁面重新載入時無法恢復突變。 + +這是一個技術限制。當持久化到外部儲存時,只有突變的狀態會被持久化,因為函式無法被序列化。水合後,觸發突變的元件可能尚未掛載,因此呼叫 `resumePausedMutations` 可能會導致錯誤:`No mutationFn found`。 + +[//]: # 'Example11' + +```tsx +const persister = createSyncStoragePersister({ + storage: window.localStorage, +}) +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小時 + }, + }, +}) + +// 我們需要一個預設的突變函式,以便暫停的突變在頁面重新載入後可以恢復 +queryClient.setMutationDefaults(['todos'], { + mutationFn: ({ id, data }) => { + return api.updateTodo(id, data) + }, +}) + +export default function App() { + return ( + { + // 從 localStorage 初始恢復成功後恢復突變 + queryClient.resumePausedMutations() + }} + > + + + ) +} +``` + +[//]: # 'Example11' + +我們還有一個涵蓋查詢和突變的完整[離線範例](../examples/offline)。 + +## 突變範圍 + +預設情況下,所有突變都是並行執行的——即使你多次呼叫相同突變的 `.mutate()`。可以透過為突變指定帶有 `id` 的 `scope` 來避免這種情況。所有具有相同 `scope.id` 的突變將會序列化執行,這意味著當它們被觸發時,如果該範圍已經有一個突變正在進行中,它們將以 `isPaused: true` 狀態開始。它們會被放入佇列中,並在輪到它們時自動恢復。 + +[//]: # 'ExampleScopes' + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +}) +``` + +[//]: # 'ExampleScopes' +[//]: # 'Materials' + +## 延伸閱讀 + +有關突變的更多資訊,請查看社群資源中的 [#12: Mastering Mutations in React Query](../community/tkdodos-blog.md#12-mastering-mutations-in-react-query)。 + +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/react/guides/network-mode.md b/docs/zh-hant/framework/react/guides/network-mode.md new file mode 100644 index 00000000000..8634e105ab3 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/network-mode.md @@ -0,0 +1,48 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:23:05.538Z' +id: network-mode +title: 網路模式 +--- + +TanStack Query 提供了三種不同的網路模式,用於區分當沒有網路連線時 [查詢 (Queries)](./queries.md) 和 [變更 (Mutations)](./mutations.md) 應如何運作。此模式可以針對每個查詢/變更單獨設定,或透過查詢/變更的預設值全域設定。 + +由於 TanStack Query 最常與資料獲取函式庫搭配使用於資料獲取情境,預設的網路模式是 [線上模式 (online)](#network-mode-online)。 + +## 網路模式:線上模式 (online) + +在此模式下,除非有網路連線,否則查詢與變更將不會執行。這是預設模式。若因無網路連線導致查詢的獲取 (fetch) 無法執行,該查詢將始終保持其當前的 `狀態` (`pending`, `error`, `success`)。不過,系統會額外提供一個 [獲取狀態 (fetchStatus)](./queries.md#fetchstatus),其可能值為: + +- `fetching`: `queryFn` 正在實際執行中 — 請求正在傳輸中。 +- `paused`: 查詢未執行 — 它會處於 `暫停 (paused)` 狀態直到重新取得連線。 +- `idle`: 查詢既未獲取也未暫停。 + +為方便起見,系統會從此狀態衍生出 `isFetching` 和 `isPaused` 標記並公開。 + +> 請注意,僅檢查 `pending` 狀態可能不足以顯示載入動畫。若查詢首次掛載且無網路連線,它們可能處於 `state: 'pending'` 但 `fetchStatus: 'paused'` 的狀態。 + +若查詢因連線狀態正常而執行,但在獲取過程中斷線,TanStack Query 也會暫停重試機制。暫停的查詢將在重新取得網路連線後繼續執行。此行為與 `refetchOnReconnect` (在此模式下也預設為 `true`) 無關,因為這不是 `重新獲取 (refetch)`,而是 `繼續 (continue)`。若查詢在此期間已被 [取消 (cancelled)](./query-cancellation.md),則不會繼續執行。 + +## 網路模式:總是執行 (always) + +在此模式下,TanStack Query 會忽略線上/離線狀態並始終執行獲取。若您在不需要主動網路連線即可讓查詢運作的環境中使用 TanStack Query (例如僅從 `AsyncStorage` 讀取,或僅想從 `queryFn` 回傳 `Promise.resolve(5)`),此模式可能是您想選擇的。 + +- 查詢絕不會因無網路連線而 `暫停 (paused)`。 +- 重試也不會暫停 — 若失敗,查詢將進入 `error` 狀態。 +- 在此模式下,`refetchOnReconnect` 預設為 `false`,因為重新連線至網路不再代表應重新獲取過時查詢。您仍可手動啟用此選項。 + +## 網路模式:離線優先 (offlineFirst) + +此模式是前兩種選項的折衷方案,TanStack Query 會執行 `queryFn` 一次,但之後會暫停重試。這對於使用 [離線優先 PWA (offline-first PWA)](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers) 中攔截請求以進行快取的 Service Worker,或透過 [Cache-Control 標頭 (Cache-Control header)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#the_cache-control_header) 使用 HTTP 快取的情境非常實用。 + +在此類情境中,首次獲取可能因來自離線儲存/快取而成功。但若發生快取未命中 (cache miss),網路請求將發出並失敗,此時此模式的行為會類似 `online` 查詢 — 暫停重試。 + +## 開發者工具 (Devtools) + +[TanStack Query 開發者工具 (Devtools)](../devtools.md) 會顯示處於 `暫停 (paused)` 狀態的查詢 — 若它們本應獲取但無網路連線。工具中也提供一個 _模擬離線行為_ 的切換按鈕。請注意,此按鈕並不會實際干擾您的網路連線 (您可以在瀏覽器開發者工具中執行此操作),而是會將 [OnlineManager](../../../reference/onlineManager.md) 設定為離線狀態。 + +## 簽名 (Signature) + +- `networkMode: 'online' | 'always' | 'offlineFirst'` + - 選填 + - 預設為 `'online'` diff --git a/docs/zh-hant/framework/react/guides/optimistic-updates.md b/docs/zh-hant/framework/react/guides/optimistic-updates.md new file mode 100644 index 00000000000..6b6fd1b0f54 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/optimistic-updates.md @@ -0,0 +1,189 @@ +--- +source-updated-at: '2025-03-26T09:27:36.000Z' +translation-updated-at: '2025-05-08T20:23:22.677Z' +id: optimistic-updates +title: 樂觀更新 +--- + +React Query 提供了兩種在突變 (mutation) 完成前樂觀更新 (optimistically update) UI 的方式。你可以使用 `onMutate` 選項直接更新快取,或是利用 `useMutation` 回傳的 `variables` 來更新 UI。 + +## 透過 UI 更新 + +這是較簡單的方式,因為它不直接與快取互動。 + +[//]: # 'ExampleUI1' + +```tsx +const addTodoMutation = useMutation({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + // 確保從查詢失效 (query invalidation) _返回_ Promise + // 這樣突變會保持在 `pending` 狀態,直到重新擷取完成 + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), +}) + +const { isPending, submittedAt, variables, mutate, isError } = addTodoMutation +``` + +[//]: # 'ExampleUI1' + +接著你可以存取 `addTodoMutation.variables`,其中包含新增的待辦事項。在渲染查詢的 UI 清單中,可以在突變處於 `isPending` 狀態時,將另一個項目附加到清單中: + +[//]: # 'ExampleUI2' + +```tsx +
      + {todoQuery.items.map((todo) => ( +
    • {todo.text}
    • + ))} + {isPending &&
    • {variables}
    • } +
    +``` + +[//]: # 'ExampleUI2' + +我們在突變處於 pending 狀態時,渲染了一個帶有不同 `opacity` 的臨時項目。一旦完成,該項目將自動不再渲染。假設重新擷取成功,我們應該會在清單中看到該項目顯示為「正常項目」。 + +如果突變發生錯誤,該項目也會消失。但如果需要,我們可以透過檢查突變的 `isError` 狀態來繼續顯示它。`variables` 在突變錯誤時*不會*被清除,因此我們仍然可以存取它們,甚至可以顯示重試按鈕: + +[//]: # 'ExampleUI3' + +```tsx +{ + isError && ( +
  • + {variables} + +
  • + ) +} +``` + +[//]: # 'ExampleUI3' + +### 如果突變與查詢不在同一個元件中 + +如果突變與查詢位於同一個元件中,這種方式效果很好。不過,你也可以透過專用的 `useMutationState` Hook 在其他元件中存取所有突變。最好與 `mutationKey` 搭配使用: + +[//]: # 'ExampleUI4' + +```tsx +// 在應用程式的某處 +const { mutate } = useMutation({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + mutationKey: ['addTodo'], +}) + +// 在其他地方存取 variables +const variables = useMutationState({ + filters: { mutationKey: ['addTodo'], status: 'pending' }, + select: (mutation) => mutation.state.variables, +}) +``` + +[//]: # 'ExampleUI4' + +`variables` 會是一個 `Array`,因為可能同時有多個突變正在執行。如果我們需要項目的唯一鍵,也可以選擇 `mutation.state.submittedAt`。這甚至能讓並發的樂觀更新變得輕而易舉。 + +## 透過快取更新 + +當你在執行突變前樂觀更新狀態時,突變有可能會失敗。在大多數失敗情況下,你可以直接觸發樂觀查詢的重新擷取,將其還原為真實的伺服器狀態。但在某些情況下,重新擷取可能無法正確運作,且突變錯誤可能代表某種伺服器問題,導致無法重新擷取。此時,你可以選擇回滾更新。 + +為此,`useMutation` 的 `onMutate` 處理常式選項允許你回傳一個值,該值稍後將作為最後一個參數傳遞給 `onError` 和 `onSettled` 處理常式。在大多數情況下,傳遞回滾函式最為實用。 + +### 在新增待辦事項時更新待辦事項清單 + +[//]: # 'Example' + +```tsx +const queryClient = useQueryClient() + +useMutation({ + mutationFn: updateTodo, + // 當 mutate 被呼叫時: + onMutate: async (newTodo) => { + // 取消任何正在進行的重新擷取 + // (這樣它們就不會覆蓋我們的樂觀更新) + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 快照先前的值 + const previousTodos = queryClient.getQueryData(['todos']) + + // 樂觀更新為新值 + queryClient.setQueryData(['todos'], (old) => [...old, newTodo]) + + // 回傳帶有快照值的 context 物件 + return { previousTodos } + }, + // 如果突變失敗, + // 使用從 onMutate 回傳的 context 進行回滾 + onError: (err, newTodo, context) => { + queryClient.setQueryData(['todos'], context.previousTodos) + }, + // 無論錯誤或成功,都重新擷取: + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), +}) +``` + +[//]: # 'Example' + +### 更新單個待辦事項 + +[//]: # 'Example2' + +```tsx +useMutation({ + mutationFn: updateTodo, + // 當 mutate 被呼叫時: + onMutate: async (newTodo) => { + // 取消任何正在進行的重新擷取 + // (這樣它們就不會覆蓋我們的樂觀更新) + await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] }) + + // 快照先前的值 + const previousTodo = queryClient.getQueryData(['todos', newTodo.id]) + + // 樂觀更新為新值 + queryClient.setQueryData(['todos', newTodo.id], newTodo) + + // 回傳帶有先前和新待辦事項的 context + return { previousTodo, newTodo } + }, + // 如果突變失敗,使用上面回傳的 context + onError: (err, newTodo, context) => { + queryClient.setQueryData( + ['todos', context.newTodo.id], + context.previousTodo, + ) + }, + // 無論錯誤或成功,都重新擷取: + onSettled: (newTodo) => + queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] }), +}) +``` + +[//]: # 'Example2' + +如果你希望,也可以使用 `onSettled` 函式來取代獨立的 `onError` 和 `onSuccess` 處理常式: + +[//]: # 'Example3' + +```tsx +useMutation({ + mutationFn: updateTodo, + // ... + onSettled: async (newTodo, error, variables, context) => { + if (error) { + // 執行某些操作 + } + }, +}) +``` + +[//]: # 'Example3' + +## 何時使用哪種方式 + +如果你只有一個地方需要顯示樂觀結果,使用 `variables` 並直接更新 UI 是程式碼較少且通常更容易理解的方式。例如,你完全不需要處理回滾。 + +然而,如果畫面上有多個地方需要知道更新,直接操作快取會自動為你處理好這一切。 diff --git a/docs/zh-hant/framework/react/guides/paginated-queries.md b/docs/zh-hant/framework/react/guides/paginated-queries.md new file mode 100644 index 00000000000..72ba2cbeac1 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/paginated-queries.md @@ -0,0 +1,95 @@ +--- +source-updated-at: '2024-07-18T12:50:17.000Z' +translation-updated-at: '2025-05-08T20:22:40.353Z' +id: paginated-queries +title: 分頁查詢 +--- + +在 UI 中渲染分頁資料是非常常見的模式,而在 TanStack Query 中,只需將頁面資訊包含在查詢鍵 (query key) 中就能「直接運作」: + +[//]: # 'Example' + +```tsx +const result = useQuery({ + queryKey: ['projects', page], + queryFn: fetchProjects, +}) +``` + +[//]: # 'Example' + +然而,當你執行這個簡單範例時,可能會注意到一個奇怪的現象: + +**UI 會在 `success` 和 `pending` 狀態之間跳動,因為每個新頁面都被視為一個全新的查詢。** + +這種體驗並不理想,但不幸的是,這也是目前許多工具堅持的運作方式。不過 TanStack Query 可不一樣!如你所料,TanStack Query 提供了一個名為 `placeholderData` 的強大功能來解決這個問題。 + +## 使用 `placeholderData` 實現更好的分頁查詢 + +考慮以下範例,我們希望逐步增加查詢的頁面索引 (pageIndex) 或游標 (cursor)。如果使用 `useQuery`,**技術上雖然仍能正常運作**,但當不同頁面或游標建立和銷毀各自的查詢時,UI 會在 `success` 和 `pending` 狀態之間跳動。透過將 `placeholderData` 設為 `(previousData) => previousData` 或使用 TanStack Query 導出的 `keepPreviousData` 函數,我們可以獲得以下優勢: + +- **即使查詢鍵已改變,上次成功取得的資料仍會在新資料請求期間保持可用**。 +- 當新資料到達時,系統會無縫切換顯示新資料。 +- 可透過 `isPlaceholderData` 判斷當前查詢提供的資料類型 + +[//]: # 'Example2' + +```tsx +import { keepPreviousData, useQuery } from '@tanstack/react-query' +import React from 'react' + +function Todos() { + const [page, setPage] = React.useState(0) + + const fetchProjects = (page = 0) => + fetch('/api/projects?page=' + page).then((res) => res.json()) + + const { isPending, isError, error, data, isFetching, isPlaceholderData } = + useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + placeholderData: keepPreviousData, + }) + + return ( +
    + {isPending ? ( +
    Loading...
    + ) : isError ? ( +
    Error: {error.message}
    + ) : ( +
    + {data.projects.map((project) => ( +

    {project.name}

    + ))} +
    + )} + Current Page: {page + 1} + + + {isFetching ? Loading... : null} +
    + ) +} +``` + +[//]: # 'Example2' + +## 使用 `placeholderData` 延遲無限查詢結果 + +雖然較不常見,但 `placeholderData` 選項也能完美搭配 `useInfiniteQuery` 鉤子 (hook) 使用,讓使用者在無限查詢鍵隨時間變化的同時,仍能無縫查看快取的資料。 diff --git a/docs/zh-hant/framework/react/guides/parallel-queries.md b/docs/zh-hant/framework/react/guides/parallel-queries.md new file mode 100644 index 00000000000..5aec37112c7 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/parallel-queries.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:22:22.749Z' +id: parallel-queries +title: 平行查詢 +--- + +「平行」查詢是指同時執行多個查詢,以最大化資料獲取的並發性。 + +## 手動平行查詢 + +當平行查詢的數量固定不變時,使用平行查詢**無需額外處理**。只需並列使用多個 TanStack Query 的 `useQuery` 和 `useInfiniteQuery` 鉤子即可! + +[//]: # 'Example' + +```tsx +function App () { + // 以下查詢會同時執行 + const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers }) + const teamsQuery = useQuery({ queryKey: ['teams'], queryFn: fetchTeams }) + const projectsQuery = useQuery({ queryKey: ['projects'], queryFn: fetchProjects }) + ... +} +``` + +[//]: # 'Example' +[//]: # 'Info' + +> 在 suspense 模式中使用 React Query 時,這種平行查詢模式會失效,因為第一個查詢會在內部拋出 promise 並暫停元件,導致其他查詢無法執行。解決方法是使用 `useSuspenseQueries` 鉤子(建議做法),或為每個 `useSuspenseQuery` 實例建立獨立元件來實現平行處理。 + +[//]: # 'Info' + +## 使用 `useQueries` 實現動態平行查詢 + +[//]: # 'DynamicParallelIntro' + +若需要在每次渲染時動態調整查詢數量,手動查詢會違反 hooks 規則。此時應使用 TanStack Query 提供的 `useQueries` 鉤子,它能動態執行任意數量的平行查詢。 + +[//]: # 'DynamicParallelIntro' + +`useQueries` 接收一個包含 **queries 鍵**的**選項物件**,該鍵值為**查詢物件陣列**,並回傳**查詢結果陣列**: + +[//]: # 'Example2' + +```tsx +function App({ users }) { + const userQueries = useQueries({ + queries: users.map((user) => { + return { + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + } + }), + }) +} +``` + +[//]: # 'Example2' diff --git a/docs/zh-hant/framework/react/guides/placeholder-query-data.md b/docs/zh-hant/framework/react/guides/placeholder-query-data.md new file mode 100644 index 00000000000..1e16fd26457 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/placeholder-query-data.md @@ -0,0 +1,103 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:22:21.696Z' +id: placeholder-query-data +title: 佔位查詢資料 +--- + +## 什麼是預留位置資料 (placeholder data)? + +預留位置資料讓查詢 (query) 能表現得像已經擁有資料一樣,類似於 `initialData` 選項,但**這些資料不會被持久化到快取中**。這在以下情境特別有用:當你擁有足夠的部分(或模擬)資料可以成功渲染查詢,同時在背景中獲取實際資料時。 + +> 範例:一篇部落格文章的查詢可以從父層的部落格文章列表中提取「預覽」資料,該列表僅包含標題和文章內容的一小段摘要。你可能不希望將這部分資料持久化到個別查詢的結果中,但它在盡快顯示內容佈局方面非常有用,同時實際查詢會完成獲取完整物件。 + +有幾種方式可以在需要之前為查詢提供預留位置資料到快取中: + +- 宣告式: + - 提供 `placeholderData` 給查詢,以便在快取為空時預先填充 +- 命令式: + - [使用 `queryClient` 和 `placeholderData` 選項預取或獲取資料](./prefetching.md) + +當我們使用 `placeholderData` 時,查詢不會處於 `pending` 狀態——它會從 `success` 狀態開始,因為我們有 `data` 可以顯示——即使這些資料只是「預留位置」資料。為了區分它與「真實」資料,我們還會在查詢結果中將 `isPlaceholderData` 標記設為 `true`。 + +## 預留位置資料作為值 + +[//]: # 'ExampleValue' + +```tsx +function Todos() { + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData: placeholderTodos, + }) +} +``` + +[//]: # 'ExampleValue' +[//]: # 'Memoization' + +### 預留位置資料的記憶化 (memoization) + +如果獲取查詢的預留位置資料的過程很耗資源,或者你不想在每次渲染時都執行,可以將值記憶化: + +```tsx +function Todos() { + const placeholderData = useMemo(() => generateFakeTodos(), []) + const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData, + }) +} +``` + +[//]: # 'Memoization' + +## 預留位置資料作為函式 + +`placeholderData` 也可以是一個函式,讓你能夠存取「先前」成功查詢的資料和查詢元資訊。這在你想使用一個查詢的資料作為另一個查詢的預留位置資料時特別有用。當查詢鍵 (QueryKey) 改變時,例如從 `['todos', 1]` 變為 `['todos', 2]`,我們可以繼續顯示「舊」資料,而不必在資料從一個查詢過渡到下一個時顯示載入動畫。更多資訊請參閱[分頁查詢](./paginated-queries.md)。 + +[//]: # 'ExampleFunction' + +```tsx +const result = useQuery({ + queryKey: ['todos', id], + queryFn: () => fetch(`/todos/${id}`), + placeholderData: (previousData, previousQuery) => previousData, +}) +``` + +[//]: # 'ExampleFunction' + +### 從快取中獲取預留位置資料 + +在某些情況下,你可以從另一個查詢的快取結果中為查詢提供預留位置資料。一個很好的例子是從部落格文章列表查詢的快取資料中搜索文章的預覽版本,然後將其用作個別文章查詢的預留位置資料: + +[//]: # 'ExampleCache' + +```tsx +function Todo({ blogPostId }) { + const queryClient = useQueryClient() + const result = useQuery({ + queryKey: ['blogPost', blogPostId], + queryFn: () => fetch(`/blogPosts/${blogPostId}`), + placeholderData: () => { + // 使用來自 'blogPosts' 查詢的較小/預覽版本的部落格文章 + // 作為此部落格文章查詢的預留位置資料 + return queryClient + .getQueryData(['blogPosts']) + ?.find((d) => d.id === blogPostId) + }, + }) +} +``` + +[//]: # 'ExampleCache' +[//]: # 'Materials' + +## 延伸閱讀 + +如需比較 `預留位置資料` 和 `初始資料`,請參閱[社群資源](../community/tkdodos-blog.md#9-placeholder-and-initial-data-in-react-query)。 + +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/react/guides/prefetching.md b/docs/zh-hant/framework/react/guides/prefetching.md new file mode 100644 index 00000000000..58c6209fde7 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/prefetching.md @@ -0,0 +1,440 @@ +--- +source-updated-at: '2025-04-25T12:36:10.000Z' +translation-updated-at: '2025-05-08T20:24:08.302Z' +id: prefetching +title: 預獲取與路由整合 +--- + +當您知道或懷疑某個資料即將被使用時,可以透過預先載入 (prefetching) 提前將該資料存入快取,從而提供更快速的體驗。 + +預先載入有以下幾種常見模式: + +1. 在事件處理函式中 +2. 在元件內 +3. 透過路由整合 +4. 在伺服器渲染期間 (另一種路由整合形式) + +本指南將探討前三種模式,第四種模式將在[伺服器渲染與水合指南](./ssr.md)和[進階伺服器渲染指南](./advanced-ssr.md)中深入說明。 + +預先載入的一個具體用途是避免請求瀑布流 (Request Waterfalls),相關背景與詳細解釋請參閱[效能與請求瀑布流指南](./request-waterfalls.md)。 + +## prefetchQuery 與 prefetchInfiniteQuery + +在深入探討各種預先載入模式前,先來了解 `prefetchQuery` 和 `prefetchInfiniteQuery` 函式。以下是基本要點: + +- 預設情況下,這些函式會使用 `queryClient` 設定的預設 `staleTime` 來判斷快取中的現有資料是否新鮮或需要重新取得 +- 您也可以傳入特定的 `staleTime`,例如:`prefetchQuery({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` + - 此 `staleTime` 僅用於預先載入,您仍需為任何 `useQuery` 呼叫設定它 + - 如果想忽略 `staleTime` 並在快取中有資料時直接返回,可以使用 `ensureQueryData` 函式 + - 提示:若在伺服器端預先載入,請為該 `queryClient` 設定高於 `0` 的預設 `staleTime`,避免為每個預先載入呼叫傳入特定 `staleTime` +- 如果沒有 `useQuery` 實例使用預先載入的查詢,該查詢將在 `gcTime` 指定的時間後被刪除並進行垃圾回收 +- 這些函式返回 `Promise`,因此不會返回查詢資料。若需要查詢資料,請改用 `fetchQuery`/`fetchInfiniteQuery` +- 預先載入函式不會拋出錯誤,因為它們通常會在 `useQuery` 中再次嘗試取得資料,這是一種優雅的後備機制。若需捕捉錯誤,請改用 `fetchQuery`/`fetchInfiniteQuery` + +以下是使用 `prefetchQuery` 的範例: + +[//]: # 'ExamplePrefetchQuery' + +```tsx +const prefetchTodos = async () => { + // 此查詢結果會像普通查詢一樣被快取 + await queryClient.prefetchQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) +} +``` + +[//]: # 'ExamplePrefetchQuery' + +無限查詢 (Infinite Queries) 可以像普通查詢一樣預先載入。預設情況下,只會預先載入查詢的第一頁,並儲存在指定的 QueryKey 下。若需預先載入多頁,可使用 `pages` 選項,此時還需提供 `getNextPageParam` 函式: + +[//]: # 'ExamplePrefetchInfiniteQuery' + +```tsx +const prefetchProjects = async () => { + // 此查詢結果會像普通查詢一樣被快取 + await queryClient.prefetchInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + pages: 3, // 預先載入前 3 頁 + }) +} +``` + +[//]: # 'ExamplePrefetchInfiniteQuery' + +接下來,我們來看看如何在不同情境下使用這些方法進行預先載入。 + +## 在事件處理函式中預先載入 + +最直接的預先載入方式是在使用者與某元素互動時執行。以下範例將在 `onMouseEnter` 或 `onFocus` 事件觸發時使用 `queryClient.prefetchQuery` 開始預先載入。 + +[//]: # 'ExampleEventHandler' + +```tsx +function ShowDetailsButton() { + const queryClient = useQueryClient() + + const prefetch = () => { + queryClient.prefetchQuery({ + queryKey: ['details'], + queryFn: getDetailsData, + // 僅當資料比 staleTime 舊時才會觸發預先載入, + // 因此在此情況下務必設定一個值 + staleTime: 60000, + }) + } + + return ( + + ) +} +``` + +[//]: # 'ExampleEventHandler' + +## 在元件內預先載入 + +當我們知道某些子元件或後代元件需要特定資料,但在其他查詢完成載入前無法渲染時,在元件生命週期內預先載入就非常有用。我們借用請求瀑布流指南中的範例來說明: + +[//]: # 'ExampleComponent' + +```tsx +function Article({ id }) { + const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + if (isPending) { + return '載入文章中...' + } + + return ( + <> + + + + + ) +} + +function Comments({ id }) { + const { data, isPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + ... +} +``` + +[//]: # 'ExampleComponent' + +這會產生如下的請求瀑布流: + +``` +1. |> getArticleById() +2. |> getArticleCommentsById() +``` + +如該指南所述,一種改善效能並扁平化瀑布流的方法是將 `getArticleCommentsById` 查詢提升至父元件並將結果作為 prop 傳遞。但如果這不可行或不理想(例如元件之間無關聯且有多層級隔離),該怎麼辦? + +在這種情況下,我們可以在父元件中預先載入該查詢。最簡單的方法是使用查詢但忽略結果: + +[//]: # 'ExampleParentComponent' + +```tsx +function Article({ id }) { + const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + // 預先載入 + useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + // 避免此查詢變更時重新渲染的優化選項: + notifyOnChangeProps: [], + }) + + if (isPending) { + return '載入文章中...' + } + + return ( + <> + + + + + ) +} + +function Comments({ id }) { + const { data, isPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + ... +} +``` + +[//]: # 'ExampleParentComponent' + +這會立即開始取得 `'article-comments'` 並扁平化瀑布流: + +``` +1. |> getArticleById() +1. |> getArticleCommentsById() +``` + +[//]: # 'Suspense' + +若想與 Suspense 一起使用預先載入,做法會稍有不同。您不能使用 `useSuspenseQueries` 來預先載入,因為預先載入會阻擋元件渲染。也不能使用 `useQuery` 進行預先載入,因為這會等到 suspenseful 查詢解析後才開始預先載入。對於這種情境,您可以使用函式庫提供的 [`usePrefetchQuery`](../reference/usePrefetchQuery.md) 或 [`usePrefetchInfiniteQuery`](../reference/usePrefetchInfiniteQuery.md) 鉤子。 + +接著,您可以在實際需要資料的元件中使用 `useSuspenseQuery`。您可能希望將此元件包裹在自己的 `` 邊界中,這樣我們預先載入的「次要」查詢就不會阻擋「主要」資料的渲染。 + +```tsx +function ArticleLayout({ id }) { + usePrefetchQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + return ( + +
    + + ) +} + +function Article({ id }) { + const { data: articleData, isPending } = useSuspenseQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + ... +} +``` + +另一種方法是在查詢函式內部進行預先載入。如果您知道每次取得文章時很可能也需要評論,這種做法就很合理。我們將使用 `queryClient.prefetchQuery`: + +```tsx +const queryClient = useQueryClient() +const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: (...args) => { + queryClient.prefetchQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + return getArticleById(...args) + }, +}) +``` + +在 effect 中預先載入也有效,但請注意,若在同一個元件中使用 `useSuspenseQuery`,此 effect 會在查詢完成後才執行,這可能不符合您的預期。 + +```tsx +const queryClient = useQueryClient() + +useEffect(() => { + queryClient.prefetchQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) +}, [queryClient, id]) +``` + +總結來說,若想在元件生命週期內預先載入查詢,有以下幾種方式,請根據情況選擇最適合的: + +- 使用 `usePrefetchQuery` 或 `usePrefetchInfiniteQuery` 鉤子在 suspense 邊界前預先載入 +- 使用 `useQuery` 或 `useSuspenseQueries` 並忽略結果 +- 在查詢函式內部預先載入 +- 在 effect 中預先載入 + +接下來我們來看一個稍微進階的案例。 + +[//]: # 'Suspense' + +### 相依查詢與程式碼分割 + +有時我們希望根據另一個取得的結果來條件式地預先載入。參考[效能與請求瀑布流指南](./request-waterfalls.md)中的範例: + +[//]: # 'ExampleConditionally1' + +```tsx +// 這會延遲載入 GraphFeedItem 元件, +// 表示在渲染前不會開始載入 +const GraphFeedItem = React.lazy(() => import('./GraphFeedItem')) + +function Feed() { + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: getFeed, + }) + + if (isPending) { + return '載入動態消息中...' + } + + return ( + <> + {data.map((feedItem) => { + if (feedItem.type === 'GRAPH') { + return + } + + return + })} + + ) +} + +// GraphFeedItem.tsx +function GraphFeedItem({ feedItem }) { + const { data, isPending } = useQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + + ... +} +``` + +[//]: # 'ExampleConditionally1' + +如該指南所述,此範例會導致以下雙重請求瀑布流: + +``` +1. |> getFeed() +2. |> JS for +3. |> getGraphDataById() +``` + +如果無法重構 API 讓 `getFeed()` 在必要時也返回 `getGraphDataById()` 的資料,就無法完全消除 `getFeed->getGraphDataById` 的瀑布流。但透過條件式預先載入,我們至少可以並行載入程式碼和資料。如同上述,有多種方式可以實現,在此範例中,我們將在查詢函式中進行: + +[//]: # 'ExampleConditionally2' + +```tsx +function Feed() { + const queryClient = useQueryClient() + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: async (...args) => { + const feed = await getFeed(...args) + + for (const feedItem of feed) { + if (feedItem.type === 'GRAPH') { + queryClient.prefetchQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + } + } + + return feed + } + }) + + ... +} +``` + +[//]: # 'ExampleConditionally2' + +這樣會並行載入程式碼和資料: + +``` +1. |> getFeed() +2. |> JS for +2. |> getGraphDataById() +``` + +但這有一個權衡點,`getGraphDataById` 的程式碼現在被包含在父元件套件中,而非 `JS for ` 中。因此您需要根據具體情況決定最佳效能權衡。如果 `GraphFeedItem` 很常見,可能值得包含在父元件中;如果非常罕見,則可能不值得。 + +[//]: # 'Router' + +## 路由整合 + +由於在元件樹中直接進行資料取得容易導致請求瀑布流,且相關修復方法在應用程式中累積後可能變得繁瑣,因此在路由層級整合預先載入是一個吸引人的方式。 + +在此方法中,您為每個路由預先明確宣告該元件樹所需的資料。由於伺服器渲染傳統上需要在渲染開始前載入所有資料,這長期以來一直是 SSR 應用的主流方法。這仍然是常見做法,您可以在[伺服器渲染與水合指南](./ssr.md)中了解更多。 + +現在,我們專注於客戶端的情況,並以 [Tanstack Router](https://tanstack.com/router) 為例說明如何實現。這些範例省略了大量設定和樣板程式碼以保持簡潔,您可以在 [Tanstack Router 文件](https://tanstack.com/router/latest/docs)中查看[完整的 React Query 範例](https://tanstack.com/router/.latest/docs/framework/react/examples/basic-react-query-file-based)。 + +在路由層級整合時,您可以選擇在該路由的所有資料載入完成前阻擋渲染,或者開始預先載入但不等待結果。這樣,您可以盡快開始渲染路由。您也可以混合這兩種方法,等待某些關鍵資料,但在所有次要資料載入完成前開始渲染。在此範例中,我們將設定 `/article` 路由在文章資料載入完成前不渲染,同時盡快開始預先載入評論,但不阻擋路由渲染即使評論尚未載入完成。 + +```tsx +const queryClient = new QueryClient() +const routerContext = new RouterContext() +const rootRoute = routerContext.createRootRoute({ + component: () => { ... } +}) + +const articleRoute = new Route({ + getParentRoute: () => rootRoute, + path: 'article', + beforeLoad: () => { + return { + articleQueryOptions: { queryKey: ['article'], queryFn: fetchArticle }, + commentsQueryOptions: { queryKey: ['comments'], queryFn: fetchComments }, + } + }, + loader: async ({ + context: { queryClient }, + routeContext: { articleQueryOptions, commentsQueryOptions }, + }) => { + // 盡快取得評論,但不阻擋 + queryClient.prefetchQuery(commentsQueryOptions) + + // 在文章載入完成前完全不渲染路由 + await queryClient.prefetchQuery(articleQueryOptions) + }, + component: ({ useRouteContext }) => { + const { articleQueryOptions, commentsQueryOptions } = useRouteContext() + const articleQuery = useQuery(articleQueryOptions) + const commentsQuery = useQuery(commentsQueryOptions) + + return ( + ... + ) + }, + errorComponent: () => '糟糕!', +}) +``` + +也可以與其他路由庫整合,請參閱 [React Router 範例](../examples/react-router)了解另一個示範。 + +[//]: # 'Router' + +## 手動初始化查詢 + +如果您已經同步擁有查詢的資料,則不需要預先載入。您可以直接使用 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 透過鍵直接新增或更新查詢的快取結果。 + +[//]: # 'ExampleManualPriming' + +```tsx +queryClient.setQueryData(['todos'], todos) +``` + +[//]: # 'ExampleManualPriming' +[//]: # 'Materials' + +## 延伸閱讀 + +若想深入了解如何在取得前將資料存入查詢快取,請參閱社群資源中的 [#17: 初始化查詢快取](../community/tkdodos-blog.md#17-seeding-the-query-cache)。 + +與伺服器端路由和框架的整合與我們剛才看到的非常相似,只是需要將資料從伺服器傳遞到客戶端以進行水合。要了解如何實現,請繼續閱讀[伺服器渲染與水合指南](./ssr.md)。 + +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/react/guides/queries.md b/docs/zh-hant/framework/react/guides/queries.md new file mode 100644 index 00000000000..8e99e0b3197 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/queries.md @@ -0,0 +1,147 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:22:26.557Z' +id: queries +title: 查詢 +--- + +## 查詢基礎 + +查詢 (Query) 是基於非同步資料來源的宣告式依賴,並與一個**唯一鍵**綁定。查詢可與任何基於 Promise 的方法(包括 GET 和 POST 方法)一起使用,從伺服器獲取資料。若您的方法會修改伺服器上的資料,建議改用[變更 (Mutations)](./mutations.md)。 + +要在元件或自訂 Hook 中訂閱查詢,請呼叫 `useQuery` Hook 並至少提供: + +- **查詢的唯一鍵** +- 一個回傳 Promise 的函式,該 Promise 會: + - 解析資料,或 + - 拋出錯誤 + +[//]: # '範例' + +```tsx +import { useQuery } from '@tanstack/react-query' + +function App() { + const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +} +``` + +[//]: # '範例' + +您提供的**唯一鍵**會在內部用於重新獲取、快取及在應用程式中共享查詢。 + +`useQuery` 回傳的查詢結果包含所有與查詢相關的資訊,可供模板渲染或其他資料使用: + +[//]: # '範例2' + +```tsx +const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +``` + +[//]: # '範例2' + +`result` 物件包含幾個非常重要的狀態,您需要了解這些狀態才能有效使用。查詢在任何時刻只能處於以下其中一種狀態: + +- `isPending` 或 `status === 'pending'` - 查詢尚未取得資料 +- `isError` 或 `status === 'error'` - 查詢遇到錯誤 +- `isSuccess` 或 `status === 'success'` - 查詢成功且資料可用 + +除了這些主要狀態外,根據查詢的狀態還可取得更多資訊: + +- `error` - 若查詢處於 `isError` 狀態,可透過 `error` 屬性取得錯誤資訊。 +- `data` - 若查詢處於 `isSuccess` 狀態,可透過 `data` 屬性取得資料。 +- `isFetching` - 在任何狀態下,若查詢正在獲取資料(包括背景重新獲取),`isFetching` 會為 `true`。 + +對於**大多數**查詢,通常只需檢查 `isPending` 狀態,接著檢查 `isError` 狀態,最後假設資料可用並渲染成功狀態: + +[//]: # '範例3' + +```tsx +function Todos() { + const { isPending, isError, data, error } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + }) + + if (isPending) { + return 載入中... + } + + if (isError) { + return 錯誤:{error.message} + } + + // 此時可假設 `isSuccess === true` + return ( +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + ) +} +``` + +[//]: # '範例3' + +若不喜歡使用布林值,也可以使用 `status` 狀態: + +[//]: # '範例4' + +```tsx +function Todos() { + const { status, data, error } = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + }) + + if (status === 'pending') { + return 載入中... + } + + if (status === 'error') { + return 錯誤:{error.message} + } + + // 同樣地,此時 status === 'success',但使用 "else" 邏輯也適用 + return ( +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + ) +} +``` + +[//]: # '範例4' + +若您在存取 `data` 前已檢查過 `pending` 和 `error`,TypeScript 也會正確縮小 `data` 的型別。 + +### 獲取狀態 (FetchStatus) + +除了 `status` 欄位外,您還會獲得一個額外的 `fetchStatus` 屬性,其選項如下: + +- `fetchStatus === 'fetching'` - 查詢正在獲取資料。 +- `fetchStatus === 'paused'` - 查詢希望獲取資料,但已暫停。詳情請參閱[網路模式 (Network Mode)](./network-mode.md) 指南。 +- `fetchStatus === 'idle'` - 查詢目前未進行任何操作。 + +### 為何需要兩種不同狀態? + +背景重新獲取與「過期但可用」(stale-while-revalidate) 邏輯使得 `status` 和 `fetchStatus` 的所有組合都有可能出現。例如: + +- 處於 `success` 狀態的查詢通常會處於 `idle` 獲取狀態,但若正在進行背景重新獲取,也可能處於 `fetching` 狀態。 +- 剛掛載且無資料的查詢通常會處於 `pending` 狀態和 `fetching` 獲取狀態,但若無網路連線,也可能處於 `paused` 狀態。 + +因此請記住,查詢可能處於 `pending` 狀態但實際上並未獲取資料。作為經驗法則: + +- `status` 提供關於 `data` 的資訊:我們是否有資料? +- `fetchStatus` 提供關於 `queryFn` 的資訊:它是否正在執行? + +[//]: # '延伸閱讀' + +## 延伸閱讀 + +若想了解其他執行狀態檢查的方式,請參閱[社群資源 (Community Resources)](../community/tkdodos-blog.md#4-status-checks-in-react-query)。 + +[//]: # '延伸閱讀' diff --git a/docs/zh-hant/framework/react/guides/query-cancellation.md b/docs/zh-hant/framework/react/guides/query-cancellation.md new file mode 100644 index 00000000000..bb214676525 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/query-cancellation.md @@ -0,0 +1,195 @@ +--- +source-updated-at: '2025-03-31T09:10:06.000Z' +translation-updated-at: '2025-05-08T20:22:27.442Z' +id: query-cancellation +title: 查詢取消 +--- + +TanStack Query 會為每個查詢函數提供一個 [`AbortSignal` 實例](https://developer.mozilla.org/docs/Web/API/AbortSignal)。當查詢過時或變為非活動狀態時,這個 `signal` 將會被中止。這意味著所有查詢都是可取消的,並且您可以根據需求在查詢函數中響應取消操作。最棒的是,這讓您可以繼續使用普通的 async/await 語法,同時獲得自動取消的所有好處。 + +`AbortController` API 在[大多數運行環境](https://developer.mozilla.org/docs/Web/API/AbortController#browser_compatibility)中都可用,但如果您的運行環境不支援它,則需要提供一個 polyfill。這裡有[幾個可用的選項](https://www.npmjs.com/search?q=abortcontroller%20polyfill)。 + +## 預設行為 + +預設情況下,查詢在卸載或變為未使用時(在其 Promise 解析之前)*不會*被取消。這意味著在 Promise 解析後,結果資料將會保留在快取中。這對於您已經開始接收查詢,但在完成之前卸載元件的情況很有幫助。如果您再次掛載元件且查詢尚未被垃圾回收,資料將會可用。 + +然而,如果您使用了 `AbortSignal`,Promise 將會被取消(例如中止 fetch 請求),因此查詢也必須被取消。取消查詢將導致其狀態*恢復*到先前的狀態。 + +## 使用 `fetch` + +[//]: # '範例' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const todosResponse = await fetch('/todos', { + // 將 signal 傳遞給 fetch + signal, + }) + const todos = await todosResponse.json() + + const todoDetails = todos.map(async ({ details }) => { + const response = await fetch(details, { + // 或傳遞給多個請求 + signal, + }) + return response.json() + }) + + return Promise.all(todoDetails) + }, +}) +``` + +[//]: # '範例' + +## 使用 `axios` [v0.22.0+](https://github.com/axios/axios/releases/tag/v0.22.0) + +[//]: # '範例2' + +```tsx +import axios from 'axios' + +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => + axios.get('/todos', { + // 將 signal 傳遞給 `axios` + signal, + }), +}) +``` + +[//]: # '範例2' + +### 使用 `axios` v0.22.0 以下版本 + +[//]: # '範例3' + +```tsx +import axios from 'axios' + +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + // 為此請求建立一個新的 CancelToken 來源 + const CancelToken = axios.CancelToken + const source = CancelToken.source() + + const promise = axios.get('/todos', { + // 將來源 token 傳遞給您的請求 + cancelToken: source.token, + }) + + // 如果 TanStack Query 發出中止信號,則取消請求 + signal?.addEventListener('abort', () => { + source.cancel('查詢已被 TanStack Query 取消') + }) + + return promise + }, +}) +``` + +[//]: # '範例3' + +## 使用 `XMLHttpRequest` + +[//]: # '範例4' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + return new Promise((resolve, reject) => { + var oReq = new XMLHttpRequest() + oReq.addEventListener('load', () => { + resolve(JSON.parse(oReq.responseText)) + }) + signal?.addEventListener('abort', () => { + oReq.abort() + reject() + }) + oReq.open('GET', '/todos') + oReq.send() + }) + }, +}) +``` + +[//]: # '範例4' + +## 使用 `graphql-request` + +可以在客戶端的 `request` 方法中設定 `AbortSignal`。 + +[//]: # '範例5' + +```tsx +const client = new GraphQLClient(endpoint) + +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + client.request({ document: query, signal }) + }, +}) +``` + +[//]: # '範例5' + +## 使用 `graphql-request` v4.0.0 以下版本 + +可以在 `GraphQLClient` 的建構函式中設定 `AbortSignal`。 + +[//]: # '範例6' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + const client = new GraphQLClient(endpoint, { + signal, + }) + return client.request(query, variables) + }, +}) +``` + +[//]: # '範例6' + +## 手動取消 + +您可能需要手動取消查詢。例如,如果請求需要很長時間才能完成,您可以允許使用者點擊取消按鈕來停止請求。為此,您只需要呼叫 `queryClient.cancelQueries({ queryKey })`,這將取消查詢並將其恢復到先前的狀態。如果您已經使用了傳遞給查詢函數的 `signal`,TanStack Query 還會額外取消 Promise。 + +[//]: # '範例7' + +```tsx +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, +}) + +const queryClient = useQueryClient() + +return ( + +) +``` + +[//]: # '範例7' + +## 限制 + +當使用 `Suspense` 鉤子時,取消功能無法運作:`useSuspenseQuery`、`useSuspenseQueries` 和 `useSuspenseInfiniteQuery`。 diff --git a/docs/zh-hant/framework/react/guides/query-functions.md b/docs/zh-hant/framework/react/guides/query-functions.md new file mode 100644 index 00000000000..4971bbe4d1d --- /dev/null +++ b/docs/zh-hant/framework/react/guides/query-functions.md @@ -0,0 +1,119 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:22:12.592Z' +id: query-functions +title: 查詢函數 +--- + +查詢函式 (query function) 可以是任何**回傳 Promise**的函式。該 Promise 應該要能**解析資料 (resolve the data)** 或**拋出錯誤 (throw an error)**。 + +以下都是有效的查詢函式配置: + +[//]: # 'Example' + +```tsx +useQuery({ queryKey: ['todos'], queryFn: fetchAllTodos }) +useQuery({ queryKey: ['todos', todoId], queryFn: () => fetchTodoById(todoId) }) +useQuery({ + queryKey: ['todos', todoId], + queryFn: async () => { + const data = await fetchTodoById(todoId) + return data + }, +}) +useQuery({ + queryKey: ['todos', todoId], + queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]), +}) +``` + +[//]: # 'Example' + +## 處理與拋出錯誤 + +為了讓 TanStack Query 判斷查詢是否出錯,查詢函式**必須拋出錯誤**或回傳**被拒絕的 Promise (rejected Promise)**。任何在查詢函式中拋出的錯誤都會被保存在查詢的 `error` 狀態中。 + +[//]: # 'Example2' + +```tsx +const { error } = useQuery({ + queryKey: ['todos', todoId], + queryFn: async () => { + if (somethingGoesWrong) { + throw new Error('Oh no!') + } + if (somethingElseGoesWrong) { + return Promise.reject(new Error('Oh no!')) + } + + return data + }, +}) +``` + +[//]: # 'Example2' + +## 與預設不拋出錯誤的 `fetch` 及其他客戶端搭配使用 + +雖然大多數工具如 `axios` 或 `graphql-request` 會自動針對失敗的 HTTP 呼叫拋出錯誤,但像 `fetch` 這類工具預設不會拋出錯誤。在這種情況下,你需要自行拋出錯誤。以下是使用常見的 `fetch` API 實現此功能的簡單方式: + +[//]: # 'Example3' + +```tsx +useQuery({ + queryKey: ['todos', todoId], + queryFn: async () => { + const response = await fetch('/todos/' + todoId) + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() + }, +}) +``` + +[//]: # 'Example3' + +## 查詢函式變數 (Query Function Variables) + +查詢鍵 (query key) 不僅用於唯一識別要取得的資料,還會以便利的方式作為 QueryFunctionContext 的一部分傳入查詢函式。雖然並非總是必要,但這讓你在需要時能夠提取查詢函式: + +[//]: # 'Example4' + +```tsx +function Todos({ status, page }) { + const result = useQuery({ + queryKey: ['todos', { status, page }], + queryFn: fetchTodoList, + }) +} + +// 在查詢函式中存取鍵 (key)、狀態 (status) 和頁碼 (page) 變數! +function fetchTodoList({ queryKey }) { + const [_key, { status, page }] = queryKey + return new Promise() +} +``` + +[//]: # 'Example4' + +### 查詢函式上下文 (QueryFunctionContext) + +`QueryFunctionContext` 是傳遞給每個查詢函式的物件,包含以下內容: + +- `queryKey: QueryKey`: [查詢鍵 (Query Keys)](./query-keys.md) +- `client: QueryClient`: [查詢客戶端 (QueryClient)](../../../reference/QueryClient.md) +- `signal?: AbortSignal` + - 由 TanStack Query 提供的 [中止訊號 (AbortSignal)](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) 實例 + - 可用於 [查詢取消 (Query Cancellation)](./query-cancellation.md) +- `meta: Record | undefined` + - 可選欄位,可用於填入查詢的額外資訊 + +此外,[無限查詢 (Infinite Queries)](./infinite-queries.md) 還會傳入以下選項: + +- `pageParam: TPageParam` + - 用於取得當前頁面的頁面參數 +- `direction: 'forward' | 'backward'` + - **已棄用** + - 當前頁面取得的方向 + - 若要取得當前頁面取得的方向,請從 `getNextPageParam` 和 `getPreviousPageParam` 中為 `pageParam` 添加方向資訊。 diff --git a/docs/zh-hant/framework/react/guides/query-invalidation.md b/docs/zh-hant/framework/react/guides/query-invalidation.md new file mode 100644 index 00000000000..4038aa7a898 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/query-invalidation.md @@ -0,0 +1,137 @@ +--- +source-updated-at: '2025-03-25T15:32:47.000Z' +translation-updated-at: '2025-05-08T20:22:20.302Z' +id: query-invalidation +title: 查詢失效 +--- + +## 查詢失效 (Query Invalidation) + +單純等待查詢變為過時 (stale) 狀態後再重新獲取資料,這種方式並不總是適用,尤其是當你明確知道使用者的某些操作導致某個查詢的資料已經過時時。為此,`QueryClient` 提供了一個 `invalidateQueries` 方法,讓你能智慧地標記查詢為過時狀態,並可能觸發重新獲取! + +[//]: # 'Example' + +```tsx +// 使快取中的所有查詢失效 +queryClient.invalidateQueries() +// 使所有以 `todos` 開頭的查詢失效 +queryClient.invalidateQueries({ queryKey: ['todos'] }) +``` + +[//]: # 'Example' + +> 注意:其他使用正規化快取 (normalized caches) 的函式庫可能會嘗試透過命令式或模式推斷 (schema inference) 來更新本地查詢的新資料,而 TanStack Query 則提供工具讓你避免維護正規化快取的手動操作,轉而採用**針對性失效、背景重新獲取,最終實現原子更新**的策略。 + +當使用 `invalidateQueries` 使查詢失效時,會發生兩件事: + +- 該查詢被標記為過時狀態。此過時狀態會覆寫 `useQuery` 或相關鉤子中設定的任何 `staleTime` 配置 +- 如果該查詢目前正透過 `useQuery` 或相關鉤子渲染,它也會在背景重新獲取資料 + +## 使用 `invalidateQueries` 進行查詢匹配 + +在使用 `invalidateQueries` 和 `removeQueries` 等 API(以及其他支援部分查詢匹配的功能)時,你可以透過查詢鍵 (query key) 的前綴來匹配多個查詢,或者非常精確地匹配特定查詢。有關可使用的篩選器類型,請參閱[查詢篩選器](./filters.md#query-filters)。 + +在這個範例中,我們可以使用 `todos` 前綴來使任何查詢鍵以 `todos` 開頭的查詢失效: + +[//]: # 'Example2' + +```tsx +import { useQuery, useQueryClient } from '@tanstack/react-query' + +// 從上下文中取得 QueryClient +const queryClient = useQueryClient() + +queryClient.invalidateQueries({ queryKey: ['todos'] }) + +// 以下兩個查詢都會失效 +const todoListQuery = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, +}) +const todoListQuery = useQuery({ + queryKey: ['todos', { page: 1 }], + queryFn: fetchTodoList, +}) +``` + +[//]: # 'Example2' + +你甚至可以透過傳遞更具體的查詢鍵給 `invalidateQueries` 方法,來使帶有特定變數的查詢失效: + +[//]: # 'Example3' + +```tsx +queryClient.invalidateQueries({ + queryKey: ['todos', { type: 'done' }], +}) + +// 以下查詢會失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +}) + +// 但以下查詢不會失效 +const todoListQuery = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, +}) +``` + +[//]: # 'Example3' + +`invalidateQueries` API 非常靈活,因此即使你只想使**不帶任何其他變數或子鍵**的 `todos` 查詢失效,也可以傳遞 `exact: true` 選項給 `invalidateQueries` 方法: + +[//]: # 'Example4' + +```tsx +queryClient.invalidateQueries({ + queryKey: ['todos'], + exact: true, +}) + +// 以下查詢會失效 +const todoListQuery = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, +}) + +// 但以下查詢不會失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { type: 'done' }], + queryFn: fetchTodoList, +}) +``` + +[//]: # 'Example4' + +如果你需要**更細緻**的控制,可以傳遞一個斷言函式 (predicate function) 給 `invalidateQueries` 方法。這個函式會接收查詢快取中的每個 `Query` 實例,並讓你決定是否要使該查詢失效(返回 `true` 或 `false`): + +[//]: # 'Example5' + +```tsx +queryClient.invalidateQueries({ + predicate: (query) => + query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10, +}) + +// 以下查詢會失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { version: 20 }], + queryFn: fetchTodoList, +}) + +// 以下查詢會失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { version: 10 }], + queryFn: fetchTodoList, +}) + +// 但以下查詢不會失效 +const todoListQuery = useQuery({ + queryKey: ['todos', { version: 5 }], + queryFn: fetchTodoList, +}) +``` + +[//]: # 'Example5' diff --git a/docs/zh-hant/framework/react/guides/query-keys.md b/docs/zh-hant/framework/react/guides/query-keys.md new file mode 100644 index 00000000000..35ea1599560 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/query-keys.md @@ -0,0 +1,104 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:22:04.442Z' +id: query-keys +title: 查詢鍵 +--- + +TanStack Query 的核心是基於查詢鍵 (query keys) 來為你管理查詢快取。查詢鍵在頂層必須是一個陣列 (Array),可以簡單到只包含單一字串的陣列,也可以複雜到包含多個字串和巢狀物件。只要查詢鍵是可序列化的 (serializable),並且**對查詢資料具有唯一性**,你就可以使用它! + +## 簡單查詢鍵 + +最簡單的鍵形式是由常數值組成的陣列。這種格式適用於: + +- 通用列表/索引資源 +- 非階層式資源 + +[//]: # 'Example' + +```tsx +// 待辦事項列表 +useQuery({ queryKey: ['todos'], ... }) + +// 其他任何東西! +useQuery({ queryKey: ['something', 'special'], ... }) +``` + +[//]: # 'Example' + +## 包含變數的陣列鍵 + +當查詢需要更多資訊來唯一描述其資料時,你可以使用包含字串和任意數量的可序列化物件的陣列。這適用於: + +- 階層式或巢狀資源 + - 通常會傳遞 ID、索引或其他基本型別來唯一識別項目 +- 帶有額外參數的查詢 + - 通常會傳遞包含額外選項的物件 + +[//]: # 'Example2' + +```tsx +// 單一待辦事項 +useQuery({ queryKey: ['todo', 5], ... }) + +// 以「預覽」格式顯示的單一待辦事項 +useQuery({ queryKey: ['todo', 5, { preview: true }], ...}) + +// 已完成待辦事項列表 +useQuery({ queryKey: ['todos', { type: 'done' }], ... }) +``` + +[//]: # 'Example2' + +## 查詢鍵會以確定性方式雜湊! + +這意味著無論物件中鍵的順序如何,以下所有查詢都被視為相等: + +[//]: # 'Example3' + +```tsx +useQuery({ queryKey: ['todos', { status, page }], ... }) +useQuery({ queryKey: ['todos', { page, status }], ...}) +useQuery({ queryKey: ['todos', { page, status, other: undefined }], ... }) +``` + +[//]: # 'Example3' + +然而,以下查詢鍵並不相容。陣列項目的順序很重要! + +[//]: # 'Example4' + +```tsx +useQuery({ queryKey: ['todos', status, page], ... }) +useQuery({ queryKey: ['todos', page, status], ...}) +useQuery({ queryKey: ['todos', undefined, page, status], ...}) +``` + +[//]: # 'Example4' + +## 若查詢函式依賴於變數,請將其包含在查詢鍵中 + +由於查詢鍵唯一描述了它們所獲取的資料,因此應包含查詢函式中使用的任何**會變化**的變數。例如: + +[//]: # 'Example5' + +```tsx +function Todos({ todoId }) { + const result = useQuery({ + queryKey: ['todos', todoId], + queryFn: () => fetchTodoById(todoId), + }) +} +``` + +[//]: # 'Example5' + +請注意,查詢鍵會作為查詢函式的依賴項。將依賴變數加入查詢鍵可確保查詢被獨立快取,並且每當變數改變時,_查詢會自動重新獲取_(取決於你的 `staleTime` 設定)。更多資訊和範例請參閱 [exhaustive-deps](../../../eslint/exhaustive-deps.md) 章節。 + +[//]: # 'Materials' + +## 延伸閱讀 + +若想了解在大型應用中組織查詢鍵的技巧,請參考 [Effective React Query Keys](../community/tkdodos-blog.md#8-effective-react-query-keys),並查看社群資源中的 [Query Key Factory Package](../community/community-projects.md#query-key-factory)。 + +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/react/guides/query-options.md b/docs/zh-hant/framework/react/guides/query-options.md new file mode 100644 index 00000000000..b00ce10f970 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/query-options.md @@ -0,0 +1,51 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:21:26.057Z' +id: query-options +title: 查詢選項 +--- + +在多重使用場景間共享 `queryKey` 和 `queryFn`,同時保持它們彼此關聯的最佳方式之一,就是使用 `queryOptions` 輔助工具。在運行時,這個輔助工具僅會回傳你傳入的內容,但在[搭配 TypeScript 使用](../typescript.md#typing-query-options)時,它能帶來許多優勢。你可以在一個地方定義查詢的所有可能選項,並獲得完整的型別推論與型別安全。 + +[//]: # 'Example1' + +```ts +import { queryOptions } from '@tanstack/react-query' + +function groupOptions(id: number) { + return queryOptions({ + queryKey: ['groups', id], + queryFn: () => fetchGroups(id), + staleTime: 5 * 1000, + }) +} + +// 使用方式: + +useQuery(groupOptions(1)) +useSuspenseQuery(groupOptions(5)) +useQueries({ + queries: [groupOptions(1), groupOptions(2)], +}) +queryClient.prefetchQuery(groupOptions(23)) +queryClient.setQueryData(groupOptions(42).queryKey, newGroups) +``` + +[//]: # 'Example1' + +針對無限查詢 (Infinite Queries),另有獨立的 [`infiniteQueryOptions`](../reference/infiniteQueryOptions.md) 輔助工具可供使用。 + +你仍可在元件層級覆寫部分選項。一個非常常見且實用的模式是為每個元件建立專屬的 [`select`](./render-optimizations.md#select) 函式: + +[//]: # 'Example2' + +```ts +// 型別推論依然有效,因此 query.data 會是 select 的回傳型別,而非 queryFn 的 + +const query = useQuery({ + ...groupOptions(1), + select: (data) => data.groupName, +}) +``` + +[//]: # 'Example2' diff --git a/docs/zh-hant/framework/react/guides/query-retries.md b/docs/zh-hant/framework/react/guides/query-retries.md new file mode 100644 index 00000000000..5bfe04520e0 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/query-retries.md @@ -0,0 +1,82 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:21:34.110Z' +id: query-retries +title: 查詢重試 +--- + +當 `useQuery` 查詢失敗(查詢函式拋出錯誤)時,TanStack Query 會自動重試該查詢,前提是該查詢的請求尚未達到最大連續重試次數(預設為 `3` 次)或提供了決定是否允許重試的函式。 + +您可以在全域層級和個別查詢層級上配置重試行為: + +- 設定 `retry = false` 將停用重試功能。 +- 設定 `retry = 6` 會在顯示函式拋出的最終錯誤前重試失敗的請求 6 次。 +- 設定 `retry = true` 會無限次重試失敗的請求。 +- 設定 `retry = (failureCount, error) => ...` 可根據請求失敗原因自訂重試邏輯。 + +[//]: # 'Info' + +> 在伺服器端,重試次數預設為 `0`,以確保伺服器渲染 (server rendering) 速度最快。 + +[//]: # 'Info' +[//]: # 'Example' + +```tsx +import { useQuery } from '@tanstack/react-query' + +// 讓特定查詢重試特定次數 +const result = useQuery({ + queryKey: ['todos', 1], + queryFn: fetchTodoListPage, + retry: 10, // 會在顯示錯誤前重試失敗的請求 10 次 +}) +``` + +[//]: # 'Example' + +> 資訊:在最後一次重試嘗試之前,`error` 屬性的內容將作為 `failureReason` 回應屬性的一部分存在於 `useQuery` 中。因此,在上述範例中,任何錯誤內容在前 9 次重試嘗試(總共 10 次嘗試)中都會是 `failureReason` 屬性的一部分,最終如果所有重試嘗試後錯誤仍然存在,這些內容將在最後一次嘗試後成為 `error` 的一部分。 + +## 重試延遲 (Retry Delay) + +預設情況下,TanStack Query 不會在請求失敗後立即進行重試。按照標準做法,每次重試嘗試會逐步套用退避延遲 (back-off delay)。 + +預設的 `retryDelay` 設定為每次嘗試加倍(從 `1000` 毫秒開始),但不超過 30 秒: + +[//]: # 'Example2' + +```tsx +// 為所有查詢進行配置 +import { + QueryCache, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, + }, +}) + +function App() { + return ... +} +``` + +[//]: # 'Example2' + +雖然不建議這樣做,但您顯然可以在 Provider 和個別查詢選項中覆寫 `retryDelay` 函式/整數。如果設定為整數而非函式,延遲時間將始終保持相同: + +[//]: # 'Example3' + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + retryDelay: 1000, // 無論重試多少次,總是等待 1000 毫秒後重試 +}) +``` + +[//]: # 'Example3' diff --git a/docs/zh-hant/framework/react/guides/render-optimizations.md b/docs/zh-hant/framework/react/guides/render-optimizations.md new file mode 100644 index 00000000000..c1d7a9c0ecd --- /dev/null +++ b/docs/zh-hant/framework/react/guides/render-optimizations.md @@ -0,0 +1,77 @@ +--- +source-updated-at: '2025-04-29T10:48:29.000Z' +translation-updated-at: '2025-05-08T20:21:38.387Z' +id: render-optimizations +title: 渲染優化 +--- + +React Query 會自動應用幾項優化措施,確保元件只在真正需要時才重新渲染。以下是實現此目標的方式: + +## 結構共享 (structural sharing) + +React Query 使用稱為「結構共享」的技術,確保在重新渲染時盡可能保持最多的引用不變。如果資料是透過網路請求取得,通常透過 JSON 解析回應會得到一個全新的引用。然而,如果資料中的內容**沒有任何變化**,React Query 會保持原始引用不變。如果只有部分資料變更,React Query 會保留未變更的部分,僅替換變更的部分。 + +> 注意:此優化僅在 `queryFn` 回傳 JSON 相容資料時有效。你可以透過全域設定 `structuralSharing: false` 或在單一查詢中關閉此功能,也可以透過傳入自訂函數來實作自己的結構共享邏輯。 + +### 引用一致性 (referential identity) + +從 `useQuery`、`useInfiniteQuery`、`useMutation` 回傳的頂層物件,以及 `useQueries` 回傳的陣列,**並不具備引用穩定性**。每次渲染時都會產生新的引用。然而,這些 Hook 回傳的 `data` 屬性會盡可能保持穩定。 + +## 追蹤屬性 (tracked properties) + +React Query 只會在 `useQuery` 回傳的屬性被實際「使用」時觸發重新渲染。這是透過 [Proxy 物件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) 實現的。此機制避免了許多不必要的重新渲染,例如 `isFetching` 或 `isStale` 等屬性可能頻繁變更,但元件中並未使用這些屬性時。 + +你可以透過全域設定或單一查詢中手動設定 `notifyOnChangeProps` 來自訂此功能。若要完全關閉此功能,可設定 `notifyOnChangeProps: 'all'`。 + +> 注意:Proxy 的 get trap 會在存取屬性時觸發,無論是透過解構賦值還是直接存取。如果使用物件剩餘解構 (object rest destructuring),將會停用此優化。我們提供了 [lint 規則](../../../eslint/no-rest-destructuring.md) 來防止此問題。 + +## select 選項 + +你可以使用 `select` 選項來選擇元件應訂閱的資料子集。這對於高度優化的資料轉換或避免不必要的重新渲染非常有用。 + +```js +export const useTodos = (select) => { + return useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + select, + }) +} + +export const useTodoCount = () => { + return useTodos((data) => data.length) +} +``` + +使用 `useTodoCount` 自訂 Hook 的元件只會在待辦事項的數量變更時重新渲染。即使某個待辦事項的名稱變更,元件也**不會**重新渲染。 + +> 注意:`select` 僅作用於已成功快取的資料,不適合在此處拋出錯誤。錯誤的來源應是 `queryFn`,若 `select` 函數回傳錯誤,會導致 `data` 為 `undefined` 且 `isSuccess` 為 `true`。建議在 `queryFn` 中處理錯誤,或將與快取無關的錯誤處理邏輯放在查詢 Hook 之外。 + +### 記憶化 (memoization) + +`select` 函數只會在以下情況重新執行: + +- `select` 函數本身的引用發生變化 +- `data` 發生變更 + +這表示像上面範例中直接內聯的 `select` 函數會在每次渲染時執行。若要避免此情況,可以將 `select` 函數用 `useCallback` 包裹,或將其提取為無依賴的穩定函數引用: + +```js +// 使用 useCallback 包裹 +export const useTodoCount = () => { + return useTodos(useCallback((data) => data.length, [])) +} +``` + +```js +// 提取為穩定的函數引用 +const selectTodoCount = (data) => data.length + +export const useTodoCount = () => { + return useTodos(selectTodoCount) +} +``` + +## 延伸閱讀 + +關於這些主題的深入指南,請參閱社群資源中的 [React Query 渲染優化](../community/tkdodos-blog.md#3-react-query-render-optimizations)。 diff --git a/docs/zh-hant/framework/react/guides/request-waterfalls.md b/docs/zh-hant/framework/react/guides/request-waterfalls.md new file mode 100644 index 00000000000..26940e4367f --- /dev/null +++ b/docs/zh-hant/framework/react/guides/request-waterfalls.md @@ -0,0 +1,341 @@ +--- +source-updated-at: '2025-04-02T06:46:03.000Z' +translation-updated-at: '2025-05-08T20:23:07.162Z' +id: request-waterfalls +title: 效能與請求瀑布 +--- + +應用程式效能是一個廣泛且複雜的領域,雖然 React Query 無法讓你的 API 變得更快,但在使用 React Query 時仍有許多需要注意的事項,以確保最佳效能。 + +使用 React Query 或任何允許你在元件內部獲取資料的資料獲取函式庫時,最大的效能陷阱就是請求瀑布 (request waterfalls)。本頁的其餘部分將解釋什麼是請求瀑布、如何發現它們,以及如何重構你的應用程式或 API 來避免它們。 + +[預取與路由整合指南](./prefetching.md) 在此基礎上進一步說明,教你如何在無法或不適合重構應用程式或 API 時提前預取資料。 + +[伺服器渲染與水合指南](./ssr.md) 教你如何在伺服器上預取資料並將這些資料傳遞給客戶端,這樣你就不需要再次獲取。 + +[進階伺服器渲染指南](./advanced-ssr.md) 進一步教你如何將這些模式應用於伺服器元件 (Server Components) 和串流伺服器渲染 (Streaming Server Rendering)。 + +## 什麼是請求瀑布? + +請求瀑布是指對資源(程式碼、CSS、圖片、資料)的請求在另一個資源請求完成後才開始的情況。 + +以一個網頁為例。在載入 CSS、JS 等內容之前,瀏覽器首先需要載入標記 (markup)。這就是一個請求瀑布。 + +``` +1. |-> 標記 (Markup) +2. |-> CSS +2. |-> JS +2. |-> 圖片 (Image) +``` + +如果你在 JS 檔案中獲取 CSS,現在就會有一個雙重瀑布: + +``` +1. |-> 標記 (Markup) +2. |-> JS +3. |-> CSS +``` + +如果該 CSS 使用了背景圖片,就會形成三重瀑布: + +``` +1. |-> 標記 (Markup) +2. |-> JS +3. |-> CSS +4. |-> 圖片 (Image) +``` + +發現和分析請求瀑布的最佳方法通常是打開瀏覽器的開發者工具中的「網路」(Network) 標籤頁。 + +每個瀑布至少代表一次與伺服器的往返,除非資源是本地快取的(實際上,其中一些瀑布可能代表多次往返,因為瀏覽器需要建立連接,這需要一些來回通信,但我們在這裡忽略這一點)。因此,請求瀑布的負面影響高度依賴於用戶的延遲。以三重瀑布為例,它實際上代表了 4 次伺服器往返。在 250ms 的延遲下(這在 3G 網路或不良網路條件下並不罕見),我們最終的總時間為 4\*250=1000ms,這還僅僅是延遲時間。如果我們能將其扁平化為第一個只有 2 次往返的例子,我們就能將時間縮短到 500ms,可能將背景圖片的載入時間縮短一半! + +## 請求瀑布與 React Query + +現在讓我們來看看 React Query。我們首先關注不使用伺服器渲染 (Server Rendering) 的情況。在我們甚至開始進行查詢 (query) 之前,我們需要載入 JS,因此在我們能在螢幕上顯示該資料之前,我們已經有一個雙重瀑布: + +``` +1. |-> 標記 (Markup) +2. |-> JS +3. |-> 查詢 (Query) +``` + +以此為基礎,讓我們看看幾種可能導致 React Query 中出現請求瀑布的模式,以及如何避免它們。 + +- 單一元件瀑布 / 串行查詢 (Serial Queries) +- 嵌套元件瀑布 +- 程式碼分割 (Code Splitting) + +### 單一元件瀑布 / 串行查詢 + +當一個元件首先獲取一個查詢,然後再獲取另一個查詢時,就會形成請求瀑布。這種情況可能發生在第二個查詢是一個[依賴查詢 (Dependent Query)](./dependent-queries.md) 時,即它在獲取時依賴於第一個查詢的資料: + +```tsx +// 獲取用戶 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, +}) + +const userId = user?.id + +// 然後獲取用戶的專案 +const { + status, + fetchStatus, + data: projects, +} = useQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + // 查詢不會執行,直到 userId 存在 + enabled: !!userId, +}) +``` + +雖然並非總是可行,但為了最佳效能,最好重構你的 API,以便你可以在單一查詢中獲取這兩者。在上面的例子中,與其先獲取 `getUserByEmail` 以便能夠 `getProjectsByUser`,引入一個新的 `getProjectsByUserEmail` 查詢可以扁平化瀑布。 + +> 另一種在不重構 API 的情況下緩解依賴查詢的方法是將瀑布移到延遲較低的伺服器上。這就是伺服器元件 (Server Components) 背後的想法,詳見[進階伺服器渲染指南](./advanced-ssr.md)。 + +串行查詢的另一個例子是當你將 React Query 與 Suspense 一起使用時: + +```tsx +function App () { + // 以下查詢將串行執行,導致與伺服器的多次往返: + const usersQuery = useSuspenseQuery({ queryKey: ['users'], queryFn: fetchUsers }) + const teamsQuery = useSuspenseQuery({ queryKey: ['teams'], queryFn: fetchTeams }) + const projectsQuery = useSuspenseQuery({ queryKey: ['projects'], queryFn: fetchProjects }) + + // 注意,由於上面的查詢會暫停渲染,因此所有查詢完成前不會渲染任何資料 + ... +} +``` + +請注意,使用常規的 `useQuery` 時,這些查詢會並行執行。 + +幸運的是,這很容易修復,只需在元件中包含多個 suspenseful 查詢時始終使用 `useSuspenseQueries` 鉤子即可。 + +```tsx +const [usersQuery, teamsQuery, projectsQuery] = useSuspenseQueries({ + queries: [ + { queryKey: ['users'], queryFn: fetchUsers }, + { queryKey: ['teams'], queryFn: fetchTeams }, + { queryKey: ['projects'], queryFn: fetchProjects }, + ], +}) +``` + +### 嵌套元件瀑布 + +嵌套元件瀑布是指父元件和子元件都包含查詢,且父元件在其查詢完成前不會渲染子元件。這種情況可能發生在使用 `useQuery` 或 `useSuspenseQuery` 時。 + +如果子元件的渲染條件基於父元件中的資料,或者子元件依賴於父元件傳遞的某些結果作為 prop 來進行查詢,我們就有一個依賴性嵌套元件瀑布。 + +讓我們先看一個子元件不依賴於父元件的例子。 + +```tsx +function Article({ id }) { + const { data: articleData, isPending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + if (isPending) { + return '載入文章中...' + } + + return ( + <> + + + + + ) + +} + +function Comments({ id }) { + const { data, isPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + ... +} +``` + +請注意,雖然 `` 從父元件接收了一個 prop `id`,但該 id 在 `
    ` 渲染時已經可用,因此我們沒有理由不能同時獲取評論和文章。在實際應用中,子元件可能嵌套在父元件下方很深的層級,這類瀑布通常更難發現和修復。但在我們的例子中,扁平化瀑布的一種方法是將評論查詢提升到父元件: + +```tsx +function Article({ id }) { + const { data: articleData, isPending: articlePending } = useQuery({ + queryKey: ['article', id], + queryFn: getArticleById, + }) + + const { data: commentsData, isPending: commentsPending } = useQuery({ + queryKey: ['article-comments', id], + queryFn: getArticleCommentsById, + }) + + if (articlePending) { + return '載入文章中...' + } + + return ( + <> + + + {commentsPending ? ( + '載入評論中...' + ) : ( + + )} + + ) +} +``` + +現在兩個查詢將並行獲取。請注意,如果你使用 Suspense,你會希望將這兩個查詢合併為一個 `useSuspenseQueries`。 + +另一種扁平化瀑布的方法是在 `
    ` 元件中預取評論,或者在頁面載入或導航時在路由層級預取這兩個查詢。更多關於這部分的內容,請參閱[預取與路由整合指南](./prefetching.md)。 + +接下來,讓我們看一個依賴性嵌套元件瀑布的例子。 + +```tsx +function Feed() { + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: getFeed, + }) + + if (isPending) { + return '載入動態中...' + } + + return ( + <> + {data.map((feedItem) => { + if (feedItem.type === 'GRAPH') { + return + } + + return + })} + + ) +} + +function GraphFeedItem({ feedItem }) { + const { data, isPending } = useQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + + ... +} +``` + +第二個查詢 `getGraphDataById` 在兩個方面依賴於其父元件。首先,除非 `feedItem` 是一個圖表,否則它不會發生;其次,它需要父元件提供一個 `id`。 + +``` +1. |> getFeed() +2. |> getGraphDataById() +``` + +在這個例子中,我們無法簡單地通過將查詢提升到父元件或甚至添加預取來扁平化瀑布。就像本指南開頭的依賴查詢例子一樣,一個選擇是重構我們的 API,將圖表資料包含在 `getFeed` 查詢中。另一個更進階的解決方案是利用伺服器元件 (Server Components) 將瀑布移到延遲較低的伺服器上(更多關於這部分的內容,請參閱[進階伺服器渲染指南](./advanced-ssr.md)),但請注意,這可能是一個非常大的架構變更。 + +即使有一些查詢瀑布,你仍然可以擁有良好的效能,只需知道它們是一個常見的效能問題並注意它們即可。一個特別隱蔽的情況是涉及程式碼分割 (Code Splitting) 時,讓我們接下來看看這個。 + +### 程式碼分割 + +將應用程式的 JS 程式碼分割成較小的塊並僅載入必要的部分通常是實現良好效能的關鍵步驟。然而,它有一個缺點,就是經常會引入請求瀑布。當這些分割後的程式碼中也包含查詢時,這個問題會進一步惡化。 + +考慮這個稍微修改過的 Feed 例子。 + +```tsx +// 這會懶載入 GraphFeedItem 元件,意味著 +// 它不會開始載入,直到有東西渲染它 +const GraphFeedItem = React.lazy(() => import('./GraphFeedItem')) + +function Feed() { + const { data, isPending } = useQuery({ + queryKey: ['feed'], + queryFn: getFeed, + }) + + if (isPending) { + return '載入動態中...' + } + + return ( + <> + {data.map((feedItem) => { + if (feedItem.type === 'GRAPH') { + return + } + + return + })} + + ) +} + +// GraphFeedItem.tsx +function GraphFeedItem({ feedItem }) { + const { data, isPending } = useQuery({ + queryKey: ['graph', feedItem.id], + queryFn: getGraphDataById, + }) + + ... +} +``` + +這個例子有一個雙重瀑布,看起來像這樣: + +``` +1. |> getFeed() +2. |> 的 JS +3. |> getGraphDataById() +``` + +但這只是從例子中的程式碼來看,如果我們考慮這個頁面的首次載入情況,實際上我們需要完成 5 次伺服器往返才能渲染圖表! + +``` +1. |> 標記 (Markup) +2. |> 的 JS +3. |> getFeed() +4. |> 的 JS +5. |> getGraphDataById() +``` + +請注意,這在伺服器渲染時看起來會有些不同,我們將在[伺服器渲染與水合指南](./ssr.md)中進一步探討。還請注意,包含 `` 的路由也經常會被程式碼分割,這可能會增加另一個跳躍 (hop)。 + +在程式碼分割的情況下,將 `getGraphDataById` 查詢提升到 `` 元件並使其成為條件式,或添加條件式預取可能會有幫助。這樣該查詢就可以與程式碼並行獲取,將例子部分變成這樣: + +``` +1. |> getFeed() +2. |> getGraphDataById() +2. |> 的 JS +``` + +然而,這是一個非常明顯的權衡。你現在將 `getGraphDataById` 的資料獲取程式碼包含在與 `` 相同的套件 (bundle) 中,因此請評估哪種方式最適合你的情況。更多關於如何實現這部分的內容,請參閱[預取與路由整合指南](./prefetching.md)。 + +> 以下兩者之間的權衡: +> +> - 將所有資料獲取程式碼包含在主套件中,即使我們很少使用它 +> - 將資料獲取程式碼放在程式碼分割後的套件中,但會有請求瀑布 +> +> 並不理想,這也是伺服器元件 (Server Components) 的動機之一。使用伺服器元件,可以同時避免這兩者,更多關於這如何應用於 React Query 的內容,請參閱[進階伺服器渲染指南](./advanced-ssr.md)。 + +## 總結與要點 + +請求瀑布是一個非常常見且複雜的效能問題,涉及許多權衡。有許多方式可能意外地將其引入你的應用程式: + +- 在子元件中添加查詢,沒有意識到父元件已經有一個查詢 +- 在父元件中添加查詢,沒有意識到子元件已經有一個查詢 +- 將一個包含有查詢的子元件的元件移動到一個新的、有查詢的父元件下 +- 等等... + +由於這種意外複雜性,注意瀑布並定期檢查你的應用程式以尋找它們(一個好的方法是時不時檢查「網路」標籤頁!)是非常值得的。你不一定需要扁平化所有瀑布才能擁有良好的效能,但要留意那些影響較大的瀑布。 + +在下一指南中,我們將探討更多扁平化瀑布的方法,通過利用[預取與路由整合](./prefetching.md)。 diff --git a/docs/zh-hant/framework/react/guides/scroll-restoration.md b/docs/zh-hant/framework/react/guides/scroll-restoration.md new file mode 100644 index 00000000000..e2690f967fc --- /dev/null +++ b/docs/zh-hant/framework/react/guides/scroll-restoration.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:21:05.614Z' +id: scroll-restoration +title: 滾動恢復 +--- + +# 滾動恢復 (Scroll Restoration) + +傳統上,當你在網頁瀏覽器中導航至先前造訪過的頁面時,會發現頁面會自動滾動到你上次離開時的相同位置。這項功能稱為**滾動恢復 (scroll restoration)**,但隨著網頁應用程式逐漸轉向客戶端資料獲取 (client side data fetching),這項功能反而有所退步。然而,有了 TanStack Query,情況就不同了。 + +在 TanStack Query 中,所有查詢(包括分頁查詢和無限滾動查詢)的「滾動恢復」功能開箱即用™️。這是因為查詢結果會被緩存,並能在查詢渲染時同步獲取。只要你的查詢緩存時間足夠長(預設為 5 分鐘)且未被垃圾回收機制清除,滾動恢復功能就能始終完美運作。 diff --git a/docs/zh-hant/framework/react/guides/ssr.md b/docs/zh-hant/framework/react/guides/ssr.md new file mode 100644 index 00000000000..a82edd387ec --- /dev/null +++ b/docs/zh-hant/framework/react/guides/ssr.md @@ -0,0 +1,470 @@ +--- +source-updated-at: '2025-04-02T06:46:03.000Z' +translation-updated-at: '2025-05-08T20:23:21.855Z' +id: ssr +title: 伺服器渲染與水合 +--- + +在本指南中,您將學習如何在伺服器渲染 (Server Rendering) 中使用 React Query。 + +建議先閱讀 [預取與路由整合](./prefetching.md) 指南了解背景知識。在此之前,您可能還想查看 [效能與請求瀑布流指南](./request-waterfalls.md)。 + +如需進階的伺服器渲染模式,例如串流 (Streaming)、伺服器元件 (Server Components) 和 Next.js 的新應用路由 (app router),請參閱 [進階伺服器渲染指南](./advanced-ssr.md)。 + +若只想查看程式碼範例,可直接跳至下方的 [完整 Next.js pages router 範例](#full-nextjs-pages-router-example) 或 [完整 Remix 範例](#full-remix-example)。 + +## 伺服器渲染與 React Query + +究竟什麼是伺服器渲染?本指南後續將假設您已熟悉此概念,但讓我們花些時間探討它與 React Query 的關聯。伺服器渲染是指在伺服器端生成初始 HTML,讓使用者在頁面載入時立即看到內容。這可能在頁面請求時即時發生 (SSR),也可能因先前請求被快取或於建置時預先發生 (SSG)。 + +若您閱讀過請求瀑布流指南,可能記得這個流程: + +``` +1. |-> 標記 (無內容) +2. |-> JavaScript +3. |-> 查詢 +``` + +在客戶端渲染 (Client Rendered) 的應用中,這是讓使用者看到螢幕內容前至少需要的 3 次伺服器往返。伺服器渲染可將上述流程轉變為: + +``` +1. |-> 標記 (包含內容與初始資料) +2. |-> JavaScript +``` + +當 **1.** 完成時,使用者即可看到內容;而當 **2.** 完成時,頁面便具備互動性且可點擊。由於標記也包含我們需要的初始資料,步驟 **3.** 完全不需要在客戶端執行,至少在您因某些原因需要重新驗證資料前是如此。 + +這全是從客戶端的角度來看。在伺服器端,我們需要在生成/渲染標記前 **預取** 資料,將這些資料 **脫水 (dehydrate)** 成可序列化的格式並嵌入標記中,然後在客戶端將資料 **水合 (hydrate)** 至 React Query 快取,以避免在客戶端重新獲取資料。 + +繼續閱讀以了解如何透過 React Query 實作這三個步驟。 + +## 關於 Suspense 的簡要說明 + +本指南使用常規的 `useQuery` API。雖然我們不一定推薦,但也可以改用 `useSuspenseQuery`,**前提是您總是預取所有查詢**。這樣做的好處是可以在客戶端使用 `` 處理載入狀態。 + +若在使用 `useSuspenseQuery` 時忘記預取查詢,後果將取決於您使用的框架。在某些情況下,資料會暫停 (Suspend) 並在伺服器端獲取,但永遠不會水合至客戶端,導致客戶端再次獲取資料。這將導致標記水合不匹配,因為伺服器和客戶端嘗試渲染不同的內容。 + +## 初始設定 + +使用 React Query 的第一步總是建立一個 `queryClient` 並將應用程式包裹在 `` 中。進行伺服器渲染時,關鍵在於 **在應用程式內部** 建立 `queryClient` 實例,並將其置於 React 狀態中(使用實例引用也可)。**這確保不同使用者和請求間的資料不會共享**,同時每個元件生命週期僅建立一次 `queryClient`。 + +Next.js pages router 範例: + +```tsx +// _app.tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +// 切勿這樣做: +// const queryClient = new QueryClient() +// +// 在檔案根層級建立 queryClient 會使快取在所有請求間共享, +// 意味著所有資料都會傳遞給所有使用者。 +// 除了影響效能外,這還可能洩露敏感資料。 + +export default function MyApp({ Component, pageProps }) { + // 正確做法是確保每個請求有自己的快取: + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 時,通常會設定預設的 staleTime + // 大於 0,以避免客戶端立即重新獲取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +Remix 範例: + +```tsx +// app/root.tsx +import { Outlet } from '@remix-run/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +export default function MyApp() { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 時,通常會設定預設的 staleTime + // 大於 0,以避免客戶端立即重新獲取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +## 快速開始:使用 `initialData` + +最快捷的方式是完全不涉及 React Query 的預取功能,也不使用 `dehydrate`/`hydrate` API。相反地,您可以直接將原始資料作為 `initialData` 選項傳遞給 `useQuery`。以下是使用 Next.js pages router 和 `getServerSideProps` 的範例: + +```tsx +export async function getServerSideProps() { + const posts = await getPosts() + return { props: { posts } } +} + +function Posts(props) { + const { data } = useQuery({ + queryKey: ['posts'], + queryFn: getPosts, + initialData: props.posts, + }) + + // ... +} +``` + +這同樣適用於 `getStaticProps` 甚至較舊的 `getInitialProps`,相同的模式可應用於任何具有等效功能的其他框架。以下是 Remix 的相同範例: + +```tsx +export async function loader() { + const posts = await getPosts() + return json({ posts }) +} + +function Posts() { + const { posts } = useLoaderData() + + const { data } = useQuery({ + queryKey: ['posts'], + queryFn: getPosts, + initialData: posts, + }) + + // ... +} +``` + +這種設定非常簡潔,對某些情況來說是快速解決方案,但與完整方法相比,有以下 **幾點權衡需考慮**: + +- 若在元件樹深處呼叫 `useQuery`,則需要將 `initialData` 傳遞至該處 +- 若在多個位置使用相同查詢呼叫 `useQuery`,僅在其中一個傳遞 `initialData` 可能很脆弱,且當應用變更時容易出錯。若移除或移動包含帶有 `initialData` 的 `useQuery` 的元件,更深層嵌套的 `useQuery` 可能不再有任何資料。而將 `initialData` 傳遞給所有需要它的查詢也可能很繁瑣 +- 無法知道查詢在伺服器端的獲取時間,因此 `dataUpdatedAt` 和判斷查詢是否需要重新獲取將基於頁面載入時間 +- 若快取中已有查詢資料,`initialData` 將不會覆寫這些資料,**即使新資料比舊資料更新** + - 考慮上述 `getServerSideProps` 範例,若多次來回導航至頁面,每次都會呼叫 `getServerSideProps` 並獲取新資料,但由於使用 `initialData` 選項,客戶端快取和資料永遠不會更新 + +設定完整的水合解決方案並不複雜且沒有這些缺點,這將是本文檔後續的重點。 + +## 使用水合 API + +只需稍多的設定,您就可以在預載階段使用 `queryClient` 預取查詢,將該 `queryClient` 的序列化版本傳遞給應用的渲染部分並在那裡重用。這避免了上述問題。可跳至完整的 Next.js pages router 和 Remix 範例,但一般來說,這些是額外的步驟: + +- 在框架的 loader 函式中,建立 `const queryClient = new QueryClient(options)` +- 在 loader 函式中,對每個要預取的查詢執行 `await queryClient.prefetchQuery(...)` + - 盡可能使用 `await Promise.all(...)` 平行獲取查詢 + - 不預取某些查詢也沒關係,這些查詢不會被伺服器渲染,而是在應用可互動後於客戶端獲取。這對使用者互動後才顯示的內容或頁面較下方的內容非常適合,以避免阻塞更關鍵的內容 +- 從 loader 返回 `dehydrate(queryClient)`,注意返回的具體語法因框架而異 +- 使用 `` 包裹您的元件樹,其中 `dehydratedState` 來自框架的 loader。獲取 `dehydratedState` 的方式也因框架而異 + - 這可以為每個路由執行,或放在應用頂層以避免重複程式碼,參見範例 + +> 有趣的是,實際上涉及 **三個** `queryClient`。框架的 loader 是一種發生在渲染前的「預載」階段,此階段有自己的 `queryClient` 負責預取。此階段的脫水結果會傳遞給 **伺服器渲染程序** 和 **客戶端渲染程序**,它們各自有自己的 `queryClient`,確保它們從相同的資料開始,因此能返回相同的標記。 + +> 伺服器元件 (Server Components) 是另一種「預載」階段,也能「預載」(預渲染)部分 React 元件樹。詳見 [進階伺服器渲染指南](./advanced-ssr.md)。 + +### 完整 Next.js pages router 範例 + +> 有關 app router 的文檔,請參閱 [進階伺服器渲染指南](./advanced-ssr.md)。 + +初始設定: + +```tsx +// _app.tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +export default function MyApp({ Component, pageProps }) { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 時,通常會設定預設的 staleTime + // 大於 0,以避免客戶端立即重新獲取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +在每個路由中: + +```tsx +// pages/posts.tsx +import { + dehydrate, + HydrationBoundary, + QueryClient, + useQuery, +} from '@tanstack/react-query' + +// 這也可以是 getServerSideProps +export async function getStaticProps() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} + +function Posts() { + // 這個 useQuery 也可以發生在 的更深層子元件中 + // 無論如何,資料都會立即可用 + const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts }) + + // 這個查詢未在伺服器預取,將在客戶端才開始獲取 + // 兩種模式可以混合使用 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} + +export default function PostsRoute({ dehydratedState }) { + return ( + + + + ) +} +``` + +### 完整 Remix 範例 + +初始設定: + +```tsx +// app/root.tsx +import { Outlet } from '@remix-run/react' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +export default function MyApp() { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 時,通常會設定預設的 staleTime + // 大於 0,以避免客戶端立即重新獲取 + staleTime: 60 * 1000, + }, + }, + }), + ) + + return ( + + + + ) +} +``` + +在每個路由中(注意這也可以在嵌套路由中使用): + +```tsx +// app/routes/posts.tsx +import { json } from '@remix-run/node' +import { + dehydrate, + HydrationBoundary, + QueryClient, + useQuery, +} from '@tanstack/react-query' + +export async function loader() { + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: getPosts, + }) + + return json({ dehydratedState: dehydrate(queryClient) }) +} + +function Posts() { + // 這個 useQuery 也可以發生在 的更深層子元件中 + // 無論如何,資料都會立即可用 + const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts }) + + // 這個查詢未在伺服器預取,將在客戶端才開始獲取 + // 兩種模式可以混合使用 + const { data: commentsData } = useQuery({ + queryKey: ['posts-comments'], + queryFn: getComments, + }) + + // ... +} + +export default function PostsRoute() { + const { dehydratedState } = useLoaderData() + return ( + + + + ) +} +``` + +## 可選 - 減少樣板程式碼 + +在每個路由中包含以下部分可能顯得冗長: + +```tsx +export default function PostsRoute({ dehydratedState }) { + return ( + + + + ) +} +``` + +雖然這種方法沒有問題,但若想減少樣板程式碼,可以這樣修改 Next.js 的設定: + +```tsx +// _app.tsx +import { + HydrationBoundary, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' + +export default function MyApp({ Component, pageProps }) { + const [queryClient] = React.useState(() => new QueryClient()) + + return ( + + + + + + ) +} + +// pages/posts.tsx +// 移除帶有 HydrationBoundary 的 PostsRoute,直接導出 Posts: +export default function Posts() { ... } +``` + +在 Remix 中,這稍微複雜一些,建議查看 [use-dehydrated-state](https://github.com/maplegrove-io/use-dehydrated-state) 套件。 + +## 預取相依查詢 + +在預取指南中,我們學習了如何 [預取相依查詢](./prefetching.md#dependent-queries--code-splitting),但如何在框架的 loader 中實現呢?考慮以下取自 [相依查詢指南](./dependent-queries.md) 的程式碼: + +```tsx +// 獲取使用者 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, +}) + +const userId = user?.id + +// 然後獲取使用者的專案 +const { + status, + fetchStatus, + data: projects, +} = useQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + // 查詢僅在使用者 ID 存在時執行 + enabled: !!userId, +}) +``` + +如何預取這些資料以實現伺服器渲染?以下是範例: + +```tsx +// 在 Remix 中,將此函式更名為 loader +export async function getServerSideProps() { + const queryClient = new QueryClient() + + const user = await queryClient.fetchQuery({ + queryKey: ['user', email], + queryFn: getUserByEmail, + }) + + if (user?.userId) { + await queryClient.prefetchQuery({ + queryKey: ['projects', userId], + queryFn: getProjectsByUser, + }) + } + + // 在 Remix 中: + // return json({ dehydratedState: dehydrate(queryClient) }) + return { props: { dehydratedState: dehydrate(queryClient) } } +} +``` + +當然,這可能會變得更複雜,但由於這些 loader 函式只是 JavaScript,您可以使用語言的全部功能來構建邏輯。確保預取所有需要伺服器渲染的查詢。 + +## 錯誤處理 + +React Query 預設採用優雅降級策略。這意味著: + +- `queryClient.prefetchQuery(...)` 不會拋出錯誤 +- `dehydrate(...)` 僅包含成功的查詢,不包括失敗的查詢 + +這將導致任何失敗的查詢在客戶端重試,且伺服器渲染的輸出將包含載入狀態而非完整內容。 + +雖然這是良好的預設行為,但有時這並非您想要的。當關鍵內容缺失時,您可能希望根據情況回應 404 或 500 狀態碼。對於這些情況,請改用 `queryClient.fetchQuery(...)`,它會在失敗時拋出錯誤,讓您以適當的方式處理問題。 + +```tsx +let result + +try { + result = await queryClient.fetchQuery(...) +} catch (error) { + // 處理錯誤,請參考框架文檔 +} + +// 您可能還想檢查並處理任何無效的 `result` +``` + +若因某些原因希望在脫水狀態中包含失敗的查詢以避免重試,可以使用 `shouldDehydrateQuery` 選項覆寫預設函式並實作自己的邏輯: + +```tsx +dehydrate(queryClient, { + shouldDehydrateQuery: (query) => { +``` diff --git a/docs/zh-hant/framework/react/guides/suspense.md b/docs/zh-hant/framework/react/guides/suspense.md new file mode 100644 index 00000000000..8eb72f468ea --- /dev/null +++ b/docs/zh-hant/framework/react/guides/suspense.md @@ -0,0 +1,225 @@ +--- +source-updated-at: '2025-04-25T12:36:10.000Z' +translation-updated-at: '2025-05-08T20:21:58.914Z' +id: suspense +title: Suspense +--- + +React Query 也能與 React 的 Suspense for Data Fetching APIs 搭配使用。為此,我們提供了專用的鉤子 (hooks): + +- [useSuspenseQuery](../reference/useSuspenseQuery.md) +- [useSuspenseInfiniteQuery](../reference/useSuspenseInfiniteQuery.md) +- [useSuspenseQueries](../reference/useSuspenseQueries.md) +- 此外,你還可以使用 `useQuery().promise` 和 `React.use()` (實驗性功能) + +當使用 suspense 模式時,不再需要 `status` 狀態和 `error` 物件,它們會被 `React.Suspense` 元件 (包括使用 `fallback` 屬性與 React 錯誤邊界 (error boundaries) 來捕捉錯誤) 所取代。請閱讀 [重置錯誤邊界](#resetting-error-boundaries) 並查看 [Suspense 範例](../examples/suspense) 以獲取更多關於如何設定 suspense 模式的資訊。 + +如果你想讓 mutations 將錯誤傳遞到最近的錯誤邊界 (類似於 queries),你也可以將 `throwOnError` 選項設為 `true`。 + +啟用查詢的 suspense 模式: + +```tsx +import { useSuspenseQuery } from '@tanstack/react-query' + +const { data } = useSuspenseQuery({ queryKey, queryFn }) +``` + +這在 TypeScript 中運作良好,因為 `data` 保證會被定義 (因為錯誤和載入狀態由 Suspense 和 ErrorBoundaries 處理)。 + +另一方面,因此你無法有條件地啟用/停用查詢。這對於相依查詢通常不是必要的,因為使用 suspense 時,元件內的所有查詢都會被序列化獲取。 + +此查詢也不存在 `placeholderData`。為了防止 UI 在更新過程中被 fallback 替換,請將變更 QueryKey 的更新包裝在 [startTransition](https://react.dev/reference/react/Suspense#preventing-unwanted-fallbacks) 中。 + +### throwOnError 預設值 + +預設情況下,並非所有錯誤都會被拋到最近的錯誤邊界 — 我們只會在沒有其他資料可顯示時拋出錯誤。這意味著如果查詢曾經成功在快取中取得資料,元件就會渲染,即使資料是 `stale`。因此,`throwOnError` 的預設值為: + +``` +throwOnError: (error, query) => typeof query.state.data === 'undefined' +``` + +由於你無法變更 `throwOnError` (因為這會讓 `data` 可能變成 `undefined`),如果你想讓所有錯誤都由錯誤邊界處理,就必須手動拋出錯誤: + +```tsx +import { useSuspenseQuery } from '@tanstack/react-query' + +const { data, error, isFetching } = useSuspenseQuery({ queryKey, queryFn }) + +if (error && !isFetching) { + throw error +} + +// 繼續渲染資料 +``` + +## 重置錯誤邊界 + +無論你在查詢中使用 **suspense** 還是 **throwOnError**,你都需要一種方式讓查詢知道,在發生某些錯誤後重新渲染時,你想要再次嘗試。 + +查詢錯誤可以透過 `QueryErrorResetBoundary` 元件或 `useQueryErrorResetBoundary` 鉤子來重置。 + +使用元件時,它會重置元件邊界內的任何查詢錯誤: + +```tsx +import { QueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => ( + + {({ reset }) => ( + ( +
    + 發生錯誤! + +
    + )} + > + +
    + )} +
    +) +``` + +使用鉤子時,它會重置最近的 `QueryErrorResetBoundary` 內的任何查詢錯誤。如果沒有定義邊界,則會全域重置: + +```tsx +import { useQueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => { + const { reset } = useQueryErrorResetBoundary() + return ( + ( +
    + 發生錯誤! + +
    + )} + > + +
    + ) +} +``` + +## 渲染時獲取 vs 獲取時渲染 + +開箱即用,React Query 在 `suspense` 模式下作為 **渲染時獲取 (Fetch-on-render)** 解決方案運作良好,無需額外配置。這意味著當你的元件嘗試掛載時,它們會觸發查詢獲取並暫停,但只有在你導入並掛載它們之後才會發生。如果你想更進一步,實現 **獲取時渲染 (Render-as-you-fetch)** 模型,我們建議在路由回調和/或用戶互動事件上實作 [預取 (Prefetching)](./prefetching.md),以便在元件掛載前開始載入查詢,甚至希望在導入或掛載其父元件之前就開始。 + +## 伺服器上的 Suspense 與串流 + +如果你使用 `NextJs`,你可以使用我們的 **實驗性** 整合來實現伺服器上的 Suspense:`@tanstack/react-query-next-experimental`。這個套件讓你能夠在伺服器上 (在客戶端元件中) 獲取資料,只需在元件中呼叫 `useSuspenseQuery`。然後,結果會隨著 SuspenseBoundaries 的解析從伺服器串流到客戶端。 + +為實現這一點,請將你的應用程式包裝在 `ReactQueryStreamedHydration` 元件中: + +```tsx +// app/providers.tsx +'use client' + +import { + isServer, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' +import * as React from 'react' +import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental' + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // 使用 SSR 時,我們通常會設定預設的 staleTime + // 大於 0,以避免在客戶端立即重新獲取 + staleTime: 60 * 1000, + }, + }, + }) +} + +let browserQueryClient: QueryClient | undefined = undefined + +function getQueryClient() { + if (isServer) { + // 伺服器端:總是建立新的查詢客戶端 + return makeQueryClient() + } else { + // 瀏覽器端:如果還沒有查詢客戶端,則建立一個新的 + // 這非常重要,這樣在初始渲染期間 React 暫停時 + // 我們不會重新建立新的客戶端。如果我們在查詢客戶端 + // 建立下方有暫停邊界,這可能就不需要了 + if (!browserQueryClient) browserQueryClient = makeQueryClient() + return browserQueryClient + } +} + +export function Providers(props: { children: React.ReactNode }) { + // 注意:初始化查詢客戶端時避免使用 useState,如果你沒有 + // 在可能暫停的程式碼與此之間設定暫停邊界, + // 因為如果初始渲染時暫停且沒有邊界,React 會丟棄客戶端 + const queryClient = getQueryClient() + + return ( + + + {props.children} + + + ) +} +``` + +更多資訊,請查看 [NextJs Suspense 串流範例](../examples/nextjs-suspense-streaming) 和 [進階渲染與水合 (Advanced Rendering & Hydration)](./advanced-ssr.md) 指南。 + +## 使用 `useQuery().promise` 和 `React.use()` (實驗性) + +> 要啟用此功能,你需要在建立 `QueryClient` 時將 `experimental_prefetchInRender` 選項設為 `true` + +**範例程式碼:** + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + experimental_prefetchInRender: true, + }, + }, +}) +``` + +**用法:** + +```tsx +import React from 'react' +import { useQuery } from '@tanstack/react-query' +import { fetchTodos, type Todo } from './api' + +function TodoList({ query }: { query: UseQueryResult }) { + const data = React.use(query.promise) + + return ( +
      + {data.map((todo) => ( +
    • {todo.title}
    • + ))} +
    + ) +} + +export function App() { + const query = useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) + + return ( + <> +

    Todos

    + 載入中...}> + + + + ) +} +``` diff --git a/docs/zh-hant/framework/react/guides/testing.md b/docs/zh-hant/framework/react/guides/testing.md new file mode 100644 index 00000000000..3393f083adb --- /dev/null +++ b/docs/zh-hant/framework/react/guides/testing.md @@ -0,0 +1,173 @@ +--- +source-updated-at: '2025-03-24T10:00:44.000Z' +translation-updated-at: '2025-05-08T20:21:28.584Z' +id: testing +title: 測試 +--- + +# 測試 + +React Query 透過鉤子 (hooks) 運作 — 無論是我們提供的鉤子,或是封裝它們的自訂鉤子。 + +在 React 17 或更早版本中,可以使用 [React Hooks Testing Library](https://react-hooks-testing-library.com/) 函式庫來為這些自訂鉤子撰寫單元測試。 + +透過以下指令安裝: + +```sh +npm install @testing-library/react-hooks react-test-renderer --save-dev +``` + +(`react-test-renderer` 函式庫是 `@testing-library/react-hooks` 的必要相依套件,其版本需與你使用的 React 版本相對應。) + +_注意_:使用 React 18 或更新版本時,`renderHook` 可直接透過 `@testing-library/react` 套件取得,不再需要 `@testing-library/react-hooks`。 + +## 第一個測試 + +安裝完成後,即可撰寫簡單的測試。假設有以下自訂鉤子: + +```tsx +export function useCustomHook() { + return useQuery({ queryKey: ['customHook'], queryFn: () => 'Hello' }) +} +``` + +我們可以為此撰寫如下測試: + +```tsx +import { renderHook, waitFor } from '@testing-library/react' + +const queryClient = new QueryClient() +const wrapper = ({ children }) => ( + {children} +) + +const { result } = renderHook(() => useCustomHook(), { wrapper }) + +await waitFor(() => expect(result.current.isSuccess).toBe(true)) + +expect(result.current.data).toEqual('Hello') +``` + +請注意,我們提供了一個自訂的封裝器 (wrapper),用於建立 `QueryClient` 和 `QueryClientProvider`。這有助於確保我們的測試完全與其他測試隔離。 + +可以只撰寫一次此封裝器,但若如此,我們需要確保在每個測試前清除 `QueryClient`,且測試不會並行執行,否則一個測試會影響其他測試的結果。 + +## 關閉重試機制 + +函式庫預設會進行三次指數退避 (exponential backoff) 重試,這意味著如果你想測試一個錯誤查詢,測試很可能會超時。最簡單的關閉重試方式是透過 `QueryClientProvider`。讓我們擴展上述範例: + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ 關閉重試 + retry: false, + }, + }, +}) +const wrapper = ({ children }) => ( + {children} +) +``` + +這會將元件樹中所有查詢的預設值設為「不重試」。重要的是要了解,這僅在你的實際 `useQuery` 沒有明確設定重試次數時才有效。如果某個查詢設定為重試 5 次,這仍會優先,因為預設值僅作為後備選項。 + +## 在 Jest 中將 gcTime 設為 Infinity + +如果你使用 Jest,可以將 `gcTime` 設為 `Infinity` 以避免「Jest 在測試執行完成後一秒內未退出」的錯誤訊息。這是伺服器端的預設行為,僅在明確設定 `gcTime` 時才需要調整。 + +## 測試網路請求 + +React Query 的主要用途是快取網路請求,因此我們必須能夠測試程式碼是否正確發起網路請求。 + +有許多方法可以測試這點,但在這個範例中,我們將使用 [nock](https://www.npmjs.com/package/nock)。 + +假設有以下自訂鉤子: + +```tsx +function useFetchData() { + return useQuery({ + queryKey: ['fetchData'], + queryFn: () => request('/api/data'), + }) +} +``` + +我們可以為此撰寫如下測試: + +```tsx +const queryClient = new QueryClient() +const wrapper = ({ children }) => ( + {children} +) + +const expectation = nock('http://example.com').get('/api/data').reply(200, { + answer: 42, +}) + +const { result } = renderHook(() => useFetchData(), { wrapper }) + +await waitFor(() => expect(result.current.isSuccess).toBe(true)) + +expect(result.current.data).toEqual({ answer: 42 }) +``` + +這裡我們使用 `waitFor` 並等待查詢狀態顯示請求已成功。這樣我們就能確定鉤子已完成執行且應包含正確資料。_注意_:使用 React 18 時,`waitFor` 的語意已變更,如上所述。 + +## 測試載入更多 / 無限滾動 + +首先需要模擬 API 回應: + +```tsx +function generateMockedResponse(page) { + return { + page: page, + items: [...] + } +} +``` + +接著,我們的 `nock` 配置需根據頁面區分回應,並使用 `uri` 來實現。此處 `uri` 的值會類似 `"/?page=1` 或 `/?page=2`: + +```tsx +const expectation = nock('http://example.com') + .persist() + .query(true) + .get('/api/data') + .reply(200, (uri) => { + const url = new URL(`http://example.com${uri}`) + const { page } = Object.fromEntries(url.searchParams) + return generateMockedResponse(page) + }) +``` + +(注意 `.persist()`,因為我們會多次呼叫此端點) + +現在可以安全地執行測試,關鍵在於等待資料斷言通過: + +```tsx +const { result } = renderHook(() => useInfiniteQueryCustomHook(), { + wrapper, +}) + +await waitFor(() => expect(result.current.isSuccess).toBe(true)) + +expect(result.current.data.pages).toStrictEqual(generateMockedResponse(1)) + +result.current.fetchNextPage() + +await waitFor(() => + expect(result.current.data.pages).toStrictEqual([ + ...generateMockedResponse(1), + ...generateMockedResponse(2), + ]), +) + +expectation.done() +``` + +_注意_:使用 React 18 時,`waitFor` 的語意已變更,如上所述。 + +## 延伸閱讀 + +如需更多技巧及使用 `mock-service-worker` 的替代設定,請參閱社群資源中的 [Testing React Query](../community/tkdodos-blog.md#5-testing-react-query)。 diff --git a/docs/zh-hant/framework/react/guides/updates-from-mutation-responses.md b/docs/zh-hant/framework/react/guides/updates-from-mutation-responses.md new file mode 100644 index 00000000000..d39a0125f1f --- /dev/null +++ b/docs/zh-hant/framework/react/guides/updates-from-mutation-responses.md @@ -0,0 +1,84 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:20:53.985Z' +id: updates-from-mutation-responses +title: 來自變更回應的更新 +--- + +在處理會**更新**伺服器上物件的變異 (mutation) 時,新的物件通常會自動包含在變異的回應中。與其重新擷取該項目的任何查詢並浪費網路請求來取得我們已經擁有的資料,我們可以利用變異函式回傳的物件,並立即使用 [Query Client 的 `setQueryData`](../../../reference/QueryClient.md#queryclientsetquerydata) 方法來更新現有的查詢資料: + +[//]: # 'Example' + +```tsx +const queryClient = useQueryClient() + +const mutation = useMutation({ + mutationFn: editTodo, + onSuccess: (data) => { + queryClient.setQueryData(['todo', { id: 5 }], data) + }, +}) + +mutation.mutate({ + id: 5, + name: 'Do the laundry', +}) + +// 以下的查詢將會隨著成功的變異回應而更新 +const { status, data, error } = useQuery({ + queryKey: ['todo', { id: 5 }], + queryFn: fetchTodoById, +}) +``` + +[//]: # 'Example' + +你可能會希望將 `onSuccess` 邏輯封裝成可重複使用的變異,為此你可以建立一個自訂的 Hook 如下: + +[//]: # 'Example2' + +```tsx +const useMutateTodo = () => { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: editTodo, + // 注意第二個參數是 `mutate` 函式接收的變數物件 + onSuccess: (data, variables) => { + queryClient.setQueryData(['todo', { id: variables.id }], data) + }, + }) +} +``` + +[//]: # 'Example2' + +## 不可變性 (Immutability) + +透過 `setQueryData` 進行的更新必須以**不可變**的方式執行。**請勿**嘗試透過就地變更資料(從快取中取得的資料)來直接寫入快取。這樣做一開始可能有效,但可能會導致難以察覺的錯誤。 + +[//]: # 'Example3' + +```tsx +queryClient.setQueryData(['posts', { id }], (oldData) => { + if (oldData) { + // ❌ 不要這樣做 + oldData.title = 'my new post title' + } + return oldData +}) + +queryClient.setQueryData( + ['posts', { id }], + // ✅ 這才是正確的方式 + (oldData) => + oldData + ? { + ...oldData, + title: 'my new post title', + } + : oldData, +) +``` + +[//]: # 'Example3' diff --git a/docs/zh-hant/framework/react/guides/window-focus-refetching.md b/docs/zh-hant/framework/react/guides/window-focus-refetching.md new file mode 100644 index 00000000000..e4184356830 --- /dev/null +++ b/docs/zh-hant/framework/react/guides/window-focus-refetching.md @@ -0,0 +1,107 @@ +--- +source-updated-at: '2024-05-10T12:03:22.000Z' +translation-updated-at: '2025-05-08T20:20:57.518Z' +id: window-focus-refetching +title: 視窗焦點重新獲取 +--- + +如果使用者離開您的應用程式後返回,且查詢資料已過時,**TanStack Query 會自動在背景為您請求新資料**。您可以使用 `refetchOnWindowFocus` 選項全域或針對單一查詢停用此功能: + +#### 全域停用 + +[//]: # 'Example' + +```tsx +// +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, // 預設值: true + }, + }, +}) + +function App() { + return ... +} +``` + +[//]: # 'Example' + +#### 單一查詢停用 + +[//]: # 'Example2' + +```tsx +useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + refetchOnWindowFocus: false, +}) +``` + +[//]: # 'Example2' + +## 自訂視窗焦點事件 + +在極少數情況下,您可能需要自行管理觸發 TanStack Query 重新驗證的視窗焦點事件。為此,TanStack Query 提供了 `focusManager.setEventListener` 函式,該函式會提供一個在視窗獲得焦點時應觸發的回呼函式,並允許您設定自己的事件。呼叫 `focusManager.setEventListener` 時,先前設定的處理常式會被移除(在大多數情況下會是預設處理常式),並改用您的新處理常式。例如,以下是預設處理常式: + +[//]: # 'Example3' + +```tsx +focusManager.setEventListener((handleFocus) => { + // 監聽 visibilitychange 事件 + if (typeof window !== 'undefined' && window.addEventListener) { + const visibilitychangeHandler = () => { + handleFocus(document.visibilityState === 'visible') + } + window.addEventListener('visibilitychange', visibilitychangeHandler, false) + return () => { + // 確保在設定新處理常式時取消訂閱 + window.removeEventListener('visibilitychange', visibilitychangeHandler) + } + } +}) +``` + +[//]: # 'Example3' +[//]: # 'ReactNative' + +## 在 React Native 中管理焦點 + +React Native 並非透過 `window` 的事件監聽器提供焦點資訊,而是透過 [`AppState` 模組](https://reactnative.dev/docs/appstate#app-states) 提供。您可以使用 `AppState` 的 "change" 事件在應用程式狀態變更為 "active" 時觸發更新: + +```tsx +import { AppState } from 'react-native' +import { focusManager } from '@tanstack/react-query' + +function onAppStateChange(status: AppStateStatus) { + if (Platform.OS !== 'web') { + focusManager.setFocused(status === 'active') + } +} + +useEffect(() => { + const subscription = AppState.addEventListener('change', onAppStateChange) + + return () => subscription.remove() +}, []) +``` + +[//]: # 'ReactNative' + +## 管理焦點狀態 + +[//]: # 'Example4' + +```tsx +import { focusManager } from '@tanstack/react-query' + +// 覆蓋預設的焦點狀態 +focusManager.setFocused(true) + +// 回退到預設的焦點檢查 +focusManager.setFocused(undefined) +``` + +[//]: # 'Example4' diff --git a/docs/zh-hant/framework/react/installation.md b/docs/zh-hant/framework/react/installation.md new file mode 100644 index 00000000000..2ef364a17f8 --- /dev/null +++ b/docs/zh-hant/framework/react/installation.md @@ -0,0 +1,93 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:16:52.988Z' +id: installation +title: 安裝 +--- + +# 安裝 + +您可以透過 [NPM](https://npmjs.com/) 安裝 React Query,或是透過 [ESM.sh](https://esm.sh/) 使用傳統的 ` +``` + +> 您可以在 [這裡](https://react.dev/reference/react/createElement#creating-an-element-without-jsx) 找到不使用 JSX 來使用 React 的說明。 + +### 需求 + +React Query 針對現代瀏覽器進行了優化,相容於以下瀏覽器配置: + +``` +Chrome >= 91 +Firefox >= 90 +Edge >= 91 +Safari >= 15 +iOS >= 15 +Opera >= 77 +``` + +> 根據您的環境,可能需要加入 polyfill。如果您需要支援舊版瀏覽器,需自行從 `node_modules` 轉譯函式庫。 + +### 建議 + +建議同時使用我們的 [ESLint Plugin Query](../../eslint/eslint-plugin-query.md) 來幫助您在編碼時捕捉錯誤和不一致。您可以透過以下指令安裝: + +```bash +npm i -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +pnpm add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +yarn add -D @tanstack/eslint-plugin-query +``` + +或 + +```bash +bun add -D @tanstack/eslint-plugin-query +``` diff --git a/docs/zh-hant/framework/react/overview.md b/docs/zh-hant/framework/react/overview.md new file mode 100644 index 00000000000..7f3d28e448a --- /dev/null +++ b/docs/zh-hant/framework/react/overview.md @@ -0,0 +1,103 @@ +--- +source-updated-at: '2025-04-02T06:45:29.000Z' +translation-updated-at: '2025-05-08T20:17:08.397Z' +id: overview +title: 概述 +--- + +TanStack Query(前身為 React Query)常被稱為網頁應用程式中缺失的資料獲取函式庫,但用更技術性的術語來說,它能讓你在網頁應用程式中**獲取、快取、同步和更新伺服器狀態 (server state)** 變得輕而易舉。 + +## 動機 + +大多數核心網頁框架**並未**提供一種全面的方式來獲取或更新資料。因此,開發者最終要麼建立封裝了嚴格資料獲取觀點的元框架 (meta-frameworks),要麼發明自己的資料獲取方式。這通常意味著拼湊基於元件的狀態和副作用,或是使用更通用的狀態管理函式庫來儲存並在應用程式中提供非同步資料。 + +雖然大多數傳統的狀態管理函式庫非常適合處理客戶端狀態 (client state),但它們**在處理非同步或伺服器狀態 (server state) 時表現不佳**。這是因為**伺服器狀態完全不同**。首先,伺服器狀態: + +- 遠端儲存在你可能無法控制或擁有的位置 +- 需要非同步 API 來獲取和更新 +- 意味著共享所有權,其他人可以在你不知情的情況下更改它 +- 如果不小心處理,可能會在你的應用程式中變得「過時 (out of date)」 + +一旦你理解了應用程式中伺服器狀態的本質,**隨著進展還會出現更多挑戰**,例如: + +- 快取...(可能是程式設計中最難的事情) +- 將多個相同資料的請求去重 (deduping) 為單一請求 +- 在背景更新「過時」的資料 +- 知道資料何時「過時」 +- 盡快反映資料的更新 +- 效能優化,如分頁 (pagination) 和懶加載資料 (lazy loading data) +- 管理伺服器狀態的記憶體和垃圾回收 (garbage collection) +- 透過結構共享 (structural sharing) 記憶化 (memoizing) 查詢結果 + +如果這份清單沒有讓你感到不知所措,那可能意味著你已經解決了所有伺服器狀態的問題,值得獲得一個獎項。然而,如果你像大多數人一樣,要麼尚未解決所有或大部分這些挑戰,而我們才剛剛觸及表面! + +TanStack Query 無疑是管理伺服器狀態的**最佳**函式庫之一。它**開箱即用,無需配置**,並且可以根據你的喜好進行客製化,隨著應用程式的增長而調整。 + +TanStack Query 讓你能夠克服伺服器狀態的棘手挑戰和障礙,並在它開始控制你之前掌控你的應用程式資料。 + +從更技術性的角度來看,TanStack Query 可能會: + +- 幫助你從應用程式中移除**許多**複雜且難以理解的程式碼,並用少量 TanStack Query 邏輯取代 +- 使你的應用程式更易於維護,並更容易建立新功能,而無需擔心連接新的伺服器狀態資料來源 +- 通過讓你的應用程式感覺比以往更快、更響應,直接影響你的終端使用者 +- 可能幫助你節省頻寬並提高記憶體效能 + +[//]: # 'Example' + +## 說夠了,直接展示程式碼吧! + +在下面的範例中,你可以看到 TanStack Query 以最基本和簡單的形式用於獲取 TanStack Query GitHub 專案本身的 GitHub 統計資料: + +[在 StackBlitz 中開啟](https://stackblitz.com/github/TanStack/query/tree/main/examples/react/simple) + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/react-query' + +const queryClient = new QueryClient() + +export default function App() { + return ( + + + + ) +} + +function Example() { + const { isPending, error, data } = useQuery({ + queryKey: ['repoData'], + queryFn: () => + fetch('https://api.github.com/repos/TanStack/query').then((res) => + res.json(), + ), + }) + + if (isPending) return 'Loading...' + + if (error) return 'An error has occurred: ' + error.message + + return ( +
    +

    {data.name}

    +

    {data.description}

    + 👀 {data.subscribers_count}{' '} + ✨ {data.stargazers_count}{' '} + 🍴 {data.forks_count} +
    + ) +} +``` + +[//]: # 'Example' +[//]: # 'Materials' + +## 你說服我了,接下來該怎麼做? + +- 考慮參加官方的 [TanStack Query 課程](https://query.gg?s=tanstack)(或為整個團隊購買!) +- 透過我們詳盡的[逐步指南](./installation.md)和[API 參考](./reference/useQuery.md)按照自己的步調學習 TanStack Query + +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/react/plugins/broadcastQueryClient.md b/docs/zh-hant/framework/react/plugins/broadcastQueryClient.md new file mode 100644 index 00000000000..8dff68ff232 --- /dev/null +++ b/docs/zh-hant/framework/react/plugins/broadcastQueryClient.md @@ -0,0 +1,63 @@ +--- +source-updated-at: '2024-12-16T15:14:41.000Z' +translation-updated-at: '2025-05-08T20:20:13.613Z' +id: broadcastQueryClient +title: broadcastQueryClient (實驗性) +--- + +> 非常重要:此工具目前處於實驗階段。這意味著在次要版本和修補版本中可能會出現破壞性變更。使用時請自行承擔風險。如果您選擇在生產環境中依賴此實驗階段的功能,請將版本鎖定在修補版本級別,以避免意外中斷。 + +`broadcastQueryClient` 是一個用於在同源瀏覽器分頁/視窗之間廣播和同步 `queryClient` 狀態的工具。 + +## 安裝 + +此工具以獨立套件形式提供,可透過 `'@tanstack/query-broadcast-client-experimental'` 導入使用。 + +## 使用方式 + +導入 `broadcastQueryClient` 函數,並傳入您的 `QueryClient` 實例,可選擇性地設定 `broadcastChannel`。 + +```tsx +import { broadcastQueryClient } from '@tanstack/query-broadcast-client-experimental' + +const queryClient = new QueryClient() + +broadcastQueryClient({ + queryClient, + broadcastChannel: 'my-app', +}) +``` + +## API + +### `broadcastQueryClient` + +傳入一個 `QueryClient` 實例,可選擇性地設定 `broadcastChannel`。 + +```tsx +broadcastQueryClient({ queryClient, broadcastChannel }) +``` + +### `Options` + +選項物件: + +```tsx +interface BroadcastQueryClientOptions { + /** 要同步的 QueryClient */ + queryClient: QueryClient + /** 此為用於分頁與視窗間通訊的 + * 唯一頻道名稱 */ + broadcastChannel?: string + /** BroadcastChannel API 的選項 */ + options?: BroadcastChannelOptions +} +``` + +預設選項為: + +```tsx +{ + broadcastChannel = 'tanstack-query', +} +``` diff --git a/docs/zh-hant/framework/react/plugins/createAsyncStoragePersister.md b/docs/zh-hant/framework/react/plugins/createAsyncStoragePersister.md new file mode 100644 index 00000000000..13fd1d8573f --- /dev/null +++ b/docs/zh-hant/framework/react/plugins/createAsyncStoragePersister.md @@ -0,0 +1,120 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:20:25.767Z' +id: createAsyncStoragePersister +title: createAsyncStoragePersister +--- + +## 安裝 + +此工具作為獨立套件提供,可透過 `'@tanstack/query-async-storage-persister'` 導入使用。 + +```bash +npm install @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +pnpm add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +yarn add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +bun add @tanstack/query-async-storage-persister @tanstack/react-query-persist-client +``` + +## 使用方式 + +- 導入 `createAsyncStoragePersister` 函式 +- 建立一個新的 asyncStoragePersister + - 可傳入任何符合 `AsyncStorage` 介面的 `storage` - 以下範例使用 React Native 的 async-storage +- 使用 [`PersistQueryClientProvider`](./persistQueryClient.md#persistqueryclientprovider) 元件包裹你的應用程式 + +```tsx +import AsyncStorage from '@react-native-async-storage/async-storage' +import { QueryClient } from '@tanstack/react-query' +import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client' +import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小時 + }, + }, +}) + +const asyncStoragePersister = createAsyncStoragePersister({ + storage: AsyncStorage, +}) + +const Root = () => ( + + + +) + +export default Root +``` + +## 重試機制 + +重試機制與 [SyncStoragePersister](./createSyncStoragePersister.md) 相同,差別在於此處的重試可以是非同步的。你也可以使用所有預先定義的重試處理器。 + +## API + +### `createAsyncStoragePersister` + +呼叫此函式來建立一個 asyncStoragePersister,後續可與 `persistQueryClient` 搭配使用。 + +```tsx +createAsyncStoragePersister(options: CreateAsyncStoragePersisterOptions) +``` + +### `選項` + +```tsx +interface CreateAsyncStoragePersisterOptions { + /** 用於從快取設定與擷取項目的儲存端客戶端 */ + storage: AsyncStorage | undefined | null + /** 將快取儲存至 localStorage 時使用的鍵名 */ + key?: string + /** 為避免 localStorage 過度寫入, + * 可傳入毫秒數來節流將快取儲存至磁碟的頻率 */ + throttleTime?: number + /** 如何將資料序列化後儲存 */ + serialize?: (client: PersistedClient) => string + /** 如何從儲存中反序列化資料 */ + deserialize?: (cachedString: string) => PersistedClient + /** 出錯時如何重試持久化 **/ + retry?: AsyncPersistRetryer +} + +interface AsyncStorage { + getItem: (key: string) => Promise + setItem: (key: string, value: string) => Promise + removeItem: (key: string) => Promise +} +``` + +預設選項為: + +```tsx +{ + key = `REACT_QUERY_OFFLINE_CACHE`, + throttleTime = 1000, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` diff --git a/docs/zh-hant/framework/react/plugins/createPersister.md b/docs/zh-hant/framework/react/plugins/createPersister.md new file mode 100644 index 00000000000..a179f811fcc --- /dev/null +++ b/docs/zh-hant/framework/react/plugins/createPersister.md @@ -0,0 +1,137 @@ +--- +source-updated-at: '2025-04-10T12:02:45.000Z' +translation-updated-at: '2025-05-08T20:20:41.005Z' +id: createPersister +title: createPersister (實驗性) +--- + +## 安裝 + +此工具以獨立套件形式提供,可透過 `'@tanstack/query-persist-client-core'` 匯入使用。 + +```bash +npm install @tanstack/query-persist-client-core +``` + +或 + +```bash +pnpm add @tanstack/query-persist-client-core +``` + +或 + +```bash +yarn add @tanstack/query-persist-client-core +``` + +或 + +```bash +bun add @tanstack/query-persist-client-core +``` + +> 注意:此工具也包含在 `@tanstack/react-query-persist-client` 套件中,若您已使用該套件則無需另行安裝。 + +## 使用方式 + +- 匯入 `experimental_createPersister` 函式 +- 建立一個新的 `experimental_createPersister` + - 可傳入任何符合 `AsyncStorage` 或 `Storage` 介面的 `storage` - 以下範例使用 React Native 的 async-storage +- 將該 `persister` 作為選項傳遞給您的 Query。可透過傳遞至 `QueryClient` 的 `defaultOptions` 或任何 `useQuery` hook 實例來實現 + - 若將此 `persister` 設為 `defaultOptions`,所有查詢將被持久化至提供的 `storage` 中。您還可透過傳遞 `filters` 進一步縮小範圍。與 `persistClient` 插件不同,此方式不會將整個 query client 作為單一項目持久化,而是分別持久化每個查詢。查詢雜湊 (query hash) 將被用作鍵名 + - 若將此 `persister` 提供給單一 `useQuery` hook,則僅該查詢會被持久化 +- 注意:`queryClient.setQueryData()` 操作不會被持久化,這意味著若您在查詢失效前執行樂觀更新 (optimistic update) 並重新整理頁面,對查詢資料的變更將會遺失。詳見 https://github.com/TanStack/query/issues/6310 + +透過此方式,您無需儲存整個 `QueryClient`,而是可選擇應用程式中值得持久化的內容。每個查詢會以懶載入方式還原 (當查詢首次使用時) 並持久化 (在每次 `queryFn` 執行後),因此無需進行節流 (throttle)。還原查詢後仍會遵守 `staleTime`,因此若資料被視為過時 (stale),將在還原後立即重新取得。若資料為新鮮 (fresh),則 `queryFn` 不會執行。 + +從記憶體中垃圾回收 (garbage collect) 查詢**不會**影響持久化的資料。這意味著查詢可在記憶體中保留較短時間以提升**記憶體效率**。當下次使用時,它們會直接從持久化儲存中還原。 + +```tsx +import AsyncStorage from '@react-native-async-storage/async-storage' +import { QueryClient } from '@tanstack/react-query' +import { experimental_createPersister } from '@tanstack/query-persist-client-core' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 30, // 30 秒 + persister: experimental_createPersister({ + storage: AsyncStorage, + maxAge: 1000 * 60 * 60 * 12, // 12 小時 + }), + }, + }, +}) +``` + +### 調整後的預設行為 + +`createPersister` 插件在技術上會包裝 `queryFn`,因此若 `queryFn` 未執行則不會還原。在此機制下,它扮演著查詢與網路之間的快取層。因此,當使用 persister 時,`networkMode` 預設為 `'offlineFirst'`,以便即使無網路連線時也能從持久化儲存中還原。 + +## API + +### `experimental_createPersister` + +```tsx +experimental_createPersister(options: StoragePersisterOptions) +``` + +#### `選項` + +```tsx +export interface StoragePersisterOptions { + /** 用於從快取設定與取得項目的儲存客戶端。 + * 對於 SSR 請傳入 `undefined`。 + */ + storage: AsyncStorage | Storage | undefined | null + /** + * 如何將資料序列化至儲存。 + * @預設值 `JSON.stringify` + */ + serialize?: (persistedQuery: PersistedQuery) => string + /** + * 如何從儲存反序列化資料。 + * @預設值 `JSON.parse` + */ + deserialize?: (cachedString: string) => PersistedQuery + /** + * 用於強制使現有快取失效的唯一字串, + * 若它們未共用相同的 buster 字串 + */ + buster?: string + /** + * 快取允許的最大存留時間 (毫秒)。 + * 若發現持久化的快取超過此時間, + * 將被捨棄 + * @預設值 24 小時 + */ + maxAge?: number + /** + * 儲存鍵名前綴。 + * 儲存鍵名由前綴與查詢雜湊組成,格式為 `prefix-queryHash`。 + */ + prefix?: string + /** + * 用於篩選應持久化的查詢。 + */ + filters?: QueryFilters +} + +interface AsyncStorage { + getItem: (key: string) => Promise + setItem: (key: string, value: string) => Promise + removeItem: (key: string) => Promise +} +``` + +預設選項為: + +```tsx +{ + prefix = 'tanstack-query', + maxAge = 1000 * 60 * 60 * 24, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` diff --git a/docs/zh-hant/framework/react/plugins/createSyncStoragePersister.md b/docs/zh-hant/framework/react/plugins/createSyncStoragePersister.md new file mode 100644 index 00000000000..9d14537d2ba --- /dev/null +++ b/docs/zh-hant/framework/react/plugins/createSyncStoragePersister.md @@ -0,0 +1,156 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:20:29.479Z' +id: createSyncStoragePersister +title: createSyncStoragePersister +--- + +## 安裝 + +此工具作為獨立套件提供,可透過 `'@tanstack/query-sync-storage-persister'` 導入使用。 + +```bash +npm install @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +pnpm add @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +yarn add @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +或 + +```bash +bun add @tanstack/query-sync-storage-persister @tanstack/react-query-persist-client +``` + +## 使用方式 + +- 導入 `createSyncStoragePersister` 函式 +- 建立一個新的 syncStoragePersister +- 將其傳遞給 [`persistQueryClient`](./persistQueryClient.md) 函式 + +```tsx +import { persistQueryClient } from '@tanstack/react-query-persist-client' +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小時 + }, + }, +}) + +const localStoragePersister = createSyncStoragePersister({ + storage: window.localStorage, +}) +// const sessionStoragePersister = createSyncStoragePersister({ storage: window.sessionStorage }) + +persistQueryClient({ + queryClient, + persister: localStoragePersister, +}) +``` + +## 重試機制 + +持久化可能會失敗,例如當資料大小超過儲存空間時。可以透過提供 `retry` 函式給 persister 來優雅處理錯誤。 + +重試函式會接收嘗試儲存的 `persistedClient`、`error` 以及 `errorCount` 作為輸入參數。它應返回一個*新的* `PersistedClient`,並用此新資料再次嘗試持久化。若返回 _undefined_,則不會再嘗試持久化。 + +```tsx +export type PersistRetryer = (props: { + persistedClient: PersistedClient + error: Error + errorCount: number +}) => PersistedClient | undefined +``` + +### 預定義策略 + +預設情況下不會進行重試。你可以使用以下預定義策略來處理重試,這些策略可從 `'@tanstack/react-query-persist-client'` 導入: + +- `removeOldestQuery` + - 會返回一個新的 `PersistedClient`,其中已移除最舊的查詢。 + +```tsx +const localStoragePersister = createSyncStoragePersister({ + storage: window.localStorage, + retry: removeOldestQuery, +}) +``` + +## API + +### `createSyncStoragePersister` + +呼叫此函式以建立一個 syncStoragePersister,後續可與 `persistQueryClient` 搭配使用。 + +```tsx +createSyncStoragePersister(options: CreateSyncStoragePersisterOptions) +``` + +### `選項` + +```tsx +interface CreateSyncStoragePersisterOptions { + /** 用於從快取設定和擷取項目的儲存客戶端 (window.localStorage 或 window.sessionStorage) */ + storage: Storage | undefined | null + /** 儲存快取時使用的鍵名 */ + key?: string + /** 為避免頻繁寫入, + * 可傳入毫秒時間來節流儲存快取至磁碟的頻率 */ + throttleTime?: number + /** 如何將資料序列化後儲存 */ + serialize?: (client: PersistedClient) => string + /** 如何從儲存中反序列化資料 */ + deserialize?: (cachedString: string) => PersistedClient + /** 出錯時如何重試持久化 **/ + retry?: PersistRetryer +} +``` + +預設選項為: + +```tsx +{ + key = `REACT_QUERY_OFFLINE_CACHE`, + throttleTime = 1000, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` + +#### `serialize` 與 `deserialize` 選項 + +`localStorage` 能儲存的資料量有限。若需儲存更多資料,可覆寫 `serialize` 和 `deserialize` 函式,使用如 [lz-string](https://github.com/pieroxy/lz-string/) 這類函式庫來壓縮與解壓縮資料。 + +```tsx +import { QueryClient } from '@tanstack/react-query' +import { persistQueryClient } from '@tanstack/react-query-persist-client' +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' + +import { compress, decompress } from 'lz-string' + +const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: Infinity } }, +}) + +persistQueryClient({ + queryClient: queryClient, + persister: createSyncStoragePersister({ + storage: window.localStorage, + serialize: (data) => compress(JSON.stringify(data)), + deserialize: (data) => JSON.parse(decompress(data)), + }), + maxAge: Infinity, +}) +``` diff --git a/docs/zh-hant/framework/react/plugins/persistQueryClient.md b/docs/zh-hant/framework/react/plugins/persistQueryClient.md new file mode 100644 index 00000000000..1d9b8bce078 --- /dev/null +++ b/docs/zh-hant/framework/react/plugins/persistQueryClient.md @@ -0,0 +1,293 @@ +--- +source-updated-at: '2025-04-10T16:13:17.000Z' +translation-updated-at: '2025-05-08T20:21:30.489Z' +id: persistQueryClient +title: persistQueryClient +--- + +這是一組用於與「持久化工具 (persisters)」互動的實用工具,這些工具會儲存您的 `queryClient` 以供後續使用。不同的 **持久化工具** 可用於將客戶端和快取儲存到多種不同的儲存層。 + +## 建立持久化工具 + +- [createSyncStoragePersister](./createSyncStoragePersister.md) +- [createAsyncStoragePersister](./createAsyncStoragePersister.md) +- [建立自訂持久化工具](#persisters) + +## 運作原理 + +**重要提示** - 為了讓持久化功能正常運作,您可能需要在 hydration 期間傳遞 `QueryClient` 一個 `gcTime` 值來覆寫預設值(如上所示)。 + +如果在建立 `QueryClient` 實例時未設定,hydration 期間的預設值將為 `300000`(5 分鐘),且儲存的快取將在 5 分鐘不活動後被丟棄。這是預設的垃圾回收行為。 + +它應設定為與 `persistQueryClient` 的 `maxAge` 選項相同或更高的值。例如,如果 `maxAge` 為 24 小時(預設值),則 `gcTime` 應為 24 小時或更高。如果低於 `maxAge`,垃圾回收將啟動並比預期更早丟棄儲存的快取。 + +您也可以傳遞 `Infinity` 來完全停用垃圾回收行為。 + +由於 JavaScript 的限制,允許的最大 `gcTime` 約為 24 天(詳見[更多資訊](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value))。 + +```tsx +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小時 + }, + }, +}) +``` + +### 快取清除 (Cache Busting) + +有時您可能會對應用程式或資料進行更改,這些更改會立即使所有快取資料失效。如果發生這種情況,您可以傳遞一個 `buster` 字串選項。如果找到的快取沒有相同的 buster 字串,它將被丟棄。以下幾個函數接受此選項: + +```tsx +persistQueryClient({ queryClient, persister, buster: buildHash }) +persistQueryClientSave({ queryClient, persister, buster: buildHash }) +persistQueryClientRestore({ queryClient, persister, buster: buildHash }) +``` + +### 移除 + +如果發現資料符合以下任一情況: + +1. 已過期(參見 `maxAge`) +2. 已清除(參見 `buster`) +3. 錯誤(例如:`throws ...`) +4. 為空(例如:`undefined`) + +持久化工具的 `removeClient()` 將被呼叫,快取會立即被丟棄。 + +## API + +### `persistQueryClientSave` + +- 您的查詢/變異將被 [`dehydrated`](../reference/hydration.md#dehydrate) 並由您提供的持久化工具儲存。 +- `createSyncStoragePersister` 和 `createAsyncStoragePersister` 會將此操作節流為最多每秒執行一次,以避免潛在的高成本寫入。請參閱它們的文件以了解如何自訂節流時間。 + +您可以使用此功能在您選擇的時刻明確地持久化快取。 + +```tsx +persistQueryClientSave({ + queryClient, + persister, + buster = '', + dehydrateOptions = undefined, +}) +``` + +### `persistQueryClientSubscribe` + +在 `queryClient` 的快取發生變化時執行 `persistQueryClientSave`。例如:您可以在使用者登入並勾選「記住我」時啟動 `subscribe`。 + +- 它會返回一個 `unsubscribe` 函數,您可以用來停止監控;結束對持久化快取的更新。 +- 如果您想在 `unsubscribe` 後清除持久化快取,可以向 `persistQueryClientRestore` 傳遞一個新的 `buster`,這將觸發持久化工具的 `removeClient` 函數並丟棄持久化快取。 + +```tsx +persistQueryClientSubscribe({ + queryClient, + persister, + buster = '', + dehydrateOptions = undefined, +}) +``` + +### `persistQueryClientRestore` + +- 嘗試從持久化工具中 [`hydrate`](../reference/hydration.md#hydrate) 先前持久化的 dehydrated 查詢/變異快取,將其還原到傳入的 query client 的快取中。 +- 如果找到的快取比 `maxAge`(預設為 24 小時)更舊,它將被丟棄。您可以根據需要自訂此時間。 + +您可以使用此功能在您選擇的時刻還原快取。 + +```tsx +persistQueryClientRestore({ + queryClient, + persister, + maxAge = 1000 * 60 * 60 * 24, // 24 小時 + buster = '', + hydrateOptions = undefined, +}) +``` + +### `persistQueryClient` + +執行以下操作: + +1. 立即還原任何持久化的快取(參見 [`persistQueryClientRestore`](#persistqueryclientrestore)) +2. 訂閱查詢快取並返回 `unsubscribe` 函數(參見 [`persistQueryClientSubscribe`](#persistqueryclientsubscribe))。 + +此功能從 3.x 版本保留至今。 + +```tsx +persistQueryClient({ + queryClient, + persister, + maxAge = 1000 * 60 * 60 * 24, // 24 小時 + buster = '', + hydrateOptions = undefined, + dehydrateOptions = undefined, +}) +``` + +### `Options` + +所有可用的選項如下: + +```tsx +interface PersistQueryClientOptions { + /** 要持久化的 QueryClient */ + queryClient: QueryClient + /** 用於將快取儲存到持久化位置或從中還原的 Persister 介面 */ + persister: Persister + /** 快取的最大允許存活時間(毫秒)。 + * 如果找到的快取比此時間更舊, + * 它將被**靜默**丟棄 + * (預設為 24 小時) */ + maxAge?: number + /** 一個唯一字串,可用於強制 + * 使現有快取失效,如果它們不共用相同的 buster 字串 */ + buster?: string + /** 傳遞給 hydrate 函數的選項 + * 不在 `persistQueryClientSave` 或 `persistQueryClientSubscribe` 中使用 */ + hydrateOptions?: HydrateOptions + /** 傳遞給 dehydrate 函數的選項 + * 不在 `persistQueryClientRestore` 中使用 */ + dehydrateOptions?: DehydrateOptions +} +``` + +實際上提供了三種介面: + +- `PersistedQueryClientSaveOptions` 用於 `persistQueryClientSave` 和 `persistQueryClientSubscribe`(不使用 `hydrateOptions`)。 +- `PersistedQueryClientRestoreOptions` 用於 `persistQueryClientRestore`(不使用 `dehydrateOptions`)。 +- `PersistQueryClientOptions` 用於 `persistQueryClient` + +## 與 React 一起使用 + +[persistQueryClient](#persistQueryClient) 會嘗試還原快取並自動訂閱後續更改,從而將您的客戶端同步到提供的儲存。 + +然而,還原是異步的,因為所有持久化工具本質上都是異步的,這意味著如果您在還原期間渲染您的 App,可能會在查詢掛載和獲取同時發生時遇到競爭條件。 + +此外,如果您在 React 元件生命週期之外訂閱更改,您將無法取消訂閱: + +```tsx +// 🚨 永遠不會取消訂閱同步 +persistQueryClient({ + queryClient, + persister: localStoragePersister, +}) + +// 🚨 與還原同時發生 +ReactDOM.createRoot(rootElement).render() +``` + +### PersistQueryClientProvider + +針對此使用情境,您可以使用 `PersistQueryClientProvider`。它將確保根據 React 元件生命週期正確訂閱/取消訂閱,並且還會確保在我們仍在還原時查詢不會開始獲取。查詢仍會渲染,但它們將被設置為 `fetchingState: 'idle'`,直到資料被還原。然後,除非還原的資料足夠新鮮,否則它們將重新獲取,並且 `initialData` 也將被尊重。它可以**代替**普通的 [QueryClientProvider](../reference/QueryClientProvider.md) 使用: + +```tsx +import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client' +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小時 + }, + }, +}) + +const persister = createSyncStoragePersister({ + storage: window.localStorage, +}) + +ReactDOM.createRoot(rootElement).render( + + + , +) +``` + +#### Props + +`PersistQueryClientProvider` 接受與 [QueryClientProvider](../reference/QueryClientProvider.md) 相同的 props,並額外包括: + +- `persistOptions: PersistQueryClientOptions` + - 您可以傳遞給 [persistQueryClient](#persistqueryclient) 的所有[選項](#options),但不包括 QueryClient 本身 +- `onSuccess?: () => Promise | unknown` + - 可選 + - 將在初始還原完成時呼叫 + - 可用於 [resumePausedMutations](../../../reference/QueryClient.md#queryclientresumepausedmutations) + - 如果返回 Promise,它將被等待;還原將被視為正在進行,直到那時 +- `onError?: () => Promise | unknown` + - 可選 + - 將在還原期間拋出錯誤時呼叫 + - 如果返回 Promise,它將被等待 + +### useIsRestoring + +如果您使用 `PersistQueryClientProvider`,您也可以同時使用 `useIsRestoring` 鉤子來檢查還原是否正在進行中。`useQuery` 和其他相關功能也會在內部檢查此狀態,以避免還原和掛載查詢之間的競爭條件。 + +## 持久化工具 + +### 持久化工具介面 + +持久化工具有以下介面: + +```tsx +export interface Persister { + persistClient(persistClient: PersistedClient): Promisable + restoreClient(): Promisable + removeClient(): Promisable +} +``` + +持久化的客戶端條目具有以下介面: + +```tsx +export interface PersistedClient { + timestamp: number + buster: string + cacheState: any +} +``` + +您可以導入這些(以建立持久化工具): + +```tsx +import { + PersistedClient, + Persister, +} from '@tanstack/react-query-persist-client' +``` + +### 建立持久化工具 + +您可以按自己的方式進行持久化。以下是建立 [Indexed DB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) 持久化工具的範例。與 `Web Storage API` 相比,Indexed DB 更快,儲存超過 5MB,且不需要序列化。這意味著它可以直接儲存 JavaScript 原生類型,例如 `Date` 和 `File`。 + +```tsx +import { get, set, del } from 'idb-keyval' +import { + PersistedClient, + Persister, +} from '@tanstack/react-query-persist-client' + +/** + * 建立 Indexed DB 持久化工具 + * @see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API + */ +export function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') { + return { + persistClient: async (client: PersistedClient) => { + await set(idbValidKey, client) + }, + restoreClient: async () => { + return await get(idbValidKey) + }, + removeClient: async () => { + await del(idbValidKey) + }, + } satisfies Persister +} +``` diff --git a/docs/zh-hant/framework/react/quick-start.md b/docs/zh-hant/framework/react/quick-start.md new file mode 100644 index 00000000000..a9c30b2ecf1 --- /dev/null +++ b/docs/zh-hant/framework/react/quick-start.md @@ -0,0 +1,79 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:16:41.564Z' +id: quick-start +title: 快速開始 +--- + +這段程式碼片段簡要說明了 React Query 的 3 個核心概念: + +- [查詢 (Queries)](./guides/queries.md) +- [變更 (Mutations)](./guides/mutations.md) +- [查詢失效 (Query Invalidation)](./guides/query-invalidation.md) + +[//]: # '範例' + +如果您需要完整可運作的範例,請參考我們的 [簡單 StackBlitz 範例](../examples/simple) + +```tsx +import { + useQuery, + useMutation, + useQueryClient, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' +import { getTodos, postTodo } from '../my-api' + +// 建立 client +const queryClient = new QueryClient() + +function App() { + return ( + // 將 client 提供給您的 App + + + + ) +} + +function Todos() { + // 存取 client + const queryClient = useQueryClient() + + // 查詢 + const query = useQuery({ queryKey: ['todos'], queryFn: getTodos }) + + // 變更 + const mutation = useMutation({ + mutationFn: postTodo, + onSuccess: () => { + // 使查詢失效並重新取得資料 + queryClient.invalidateQueries({ queryKey: ['todos'] }) + }, + }) + + return ( +
    +
      {query.data?.map((todo) =>
    • {todo.title}
    • )}
    + + +
    + ) +} + +render(, document.getElementById('root')) +``` + +[//]: # '範例' + +這三個概念構成了 React Query 的大部分核心功能。接下來的文件章節將會詳細說明這些核心概念。 diff --git a/docs/zh-hant/framework/react/react-native.md b/docs/zh-hant/framework/react/react-native.md new file mode 100644 index 00000000000..c1c3ead2b9b --- /dev/null +++ b/docs/zh-hant/framework/react/react-native.md @@ -0,0 +1,121 @@ +--- +source-updated-at: '2025-02-12T15:01:46.000Z' +translation-updated-at: '2025-05-08T20:16:49.312Z' +id: react-native +title: React Native +--- + +React Query 設計上能直接與 React Native 無縫協作,但目前開發者工具 (devtools) 僅支援 React DOM。 + +你可以嘗試以下第三方套件: + +- [Expo](https://docs.expo.dev/) 插件:https://github.com/expo/dev-plugins/tree/main/packages/react-query +- [Flipper](https://fbflipper.com/docs/getting-started/react-native/) 插件:https://github.com/bgaleotti/react-query-native-devtools +- [Reactotron](https://github.com/infinitered/reactotron/) 插件:https://github.com/hsndmr/reactotron-react-query + +若您願意協助我們讓內建開發者工具跨平台通用,歡迎聯繫我們! + +## 線上狀態管理 + +React Query 在網頁瀏覽器中已支援自動重新連線時重新擷取 (auto refetch on reconnect)。要在 React Native 實現此行為,需使用 React Query 的 `onlineManager`,如下範例: + +```tsx +import NetInfo from '@react-native-community/netinfo' +import { onlineManager } from '@tanstack/react-query' + +onlineManager.setEventListener((setOnline) => { + return NetInfo.addEventListener((state) => { + setOnline(!!state.isConnected) + }) +}) +``` + +或 + +```tsx +import { onlineManager } from '@tanstack/react-query' +import * as Network from 'expo-network' + +onlineManager.setEventListener((setOnline) => { + const eventSubscription = Network.addNetworkStateListener((state) => { + setOnline(!!state.isConnected) + }) + return eventSubscription.remove +}) +``` + +## 應用程式焦點時重新擷取 + +React Native 透過 [`AppState` 模組](https://reactnative.dev/docs/appstate#app-states)提供焦點狀態資訊(而非網頁的 `window` 事件監聽器)。您可以使用 `AppState` 的 "change" 事件在應用狀態變為 "active" 時觸發更新: + +```tsx +import { useEffect } from 'react' +import { AppState, Platform } from 'react-native' +import type { AppStateStatus } from 'react-native' +import { focusManager } from '@tanstack/react-query' + +function onAppStateChange(status: AppStateStatus) { + if (Platform.OS !== 'web') { + focusManager.setFocused(status === 'active') + } +} + +useEffect(() => { + const subscription = AppState.addEventListener('change', onAppStateChange) + + return () => subscription.remove() +}, []) +``` + +## 畫面焦點時重新整理 + +某些情境下,您可能希望在 React Native 畫面重新獲得焦點時重新擷取查詢。以下自訂勾子 (custom hook) 會在畫面再次聚焦時呼叫傳入的 `refetch` 函式: + +```tsx +import React from 'react' +import { useFocusEffect } from '@react-navigation/native' + +export function useRefreshOnFocus(refetch: () => Promise) { + const firstTimeRef = React.useRef(true) + + useFocusEffect( + React.useCallback(() => { + if (firstTimeRef.current) { + firstTimeRef.current = false + return + } + + refetch() + }, [refetch]), + ) +} +``` + +上述程式碼會跳過首次執行,因為 `useFocusEffect` 除了在畫面聚焦時,也會在元件掛載時呼叫回調函式。 + +## 停用非焦點畫面的查詢 + +若您不希望特定查詢在畫面失去焦點時保持「活動」狀態,可使用 `useQuery` 的 `subscribed` 屬性。此屬性讓您控制查詢是否持續訂閱更新,搭配 React Navigation 的 `useIsFocused` 即可在畫面非焦點時自動取消訂閱: + +使用範例: + +```tsx +import React from 'react' +import { useIsFocused } from '@react-navigation/native' +import { useQuery } from '@tanstack/react-query' +import { Text } from 'react-native' + +function MyComponent() { + const isFocused = useIsFocused() + + const { dataUpdatedAt } = useQuery({ + queryKey: ['key'], + queryFn: () => fetch(...), + subscribed: isFocused, + }) + + return DataUpdatedAt: {dataUpdatedAt} +} +``` + +當 `subscribed` 設為 `false` 時,查詢會取消訂閱更新,不會觸發重新渲染或為該畫面擷取新資料。當值再次變為 `true`(例如畫面重新獲得焦點時),查詢會重新訂閱並保持資料最新狀態。 diff --git a/docs/zh-hant/framework/react/reference/QueryClientProvider.md b/docs/zh-hant/framework/react/reference/QueryClientProvider.md new file mode 100644 index 00000000000..7fdf128360b --- /dev/null +++ b/docs/zh-hant/framework/react/reference/QueryClientProvider.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:19:46.178Z' +id: QueryClientProvider +title: QueryClientProvider +--- + +使用 `QueryClientProvider` 元件來連接並為你的應用程式提供一個 `QueryClient`: + +```tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +const queryClient = new QueryClient() + +function App() { + return ... +} +``` + +**選項** + +- `client: QueryClient` + - **必填** + - 要提供的 QueryClient 實例 diff --git a/docs/zh-hant/framework/react/reference/QueryErrorResetBoundary.md b/docs/zh-hant/framework/react/reference/QueryErrorResetBoundary.md new file mode 100644 index 00000000000..288b59abcb6 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/QueryErrorResetBoundary.md @@ -0,0 +1,31 @@ +--- +source-updated-at: '2024-04-11T09:16:39.000Z' +translation-updated-at: '2025-05-08T20:19:49.451Z' +id: QueryErrorResetBoundary +title: QueryErrorResetBoundary +--- + +當你在查詢中使用 **suspense** 或 **throwOnError** 時,需要一種方式讓查詢知道,當發生錯誤後重新渲染時,你想要再次嘗試。透過 `QueryErrorResetBoundary` 元件,你可以重置該元件範圍內任何查詢的錯誤狀態。 + +```tsx +import { QueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => ( + + {({ reset }) => ( + ( +
    + There was an error! + +
    + )} + > + +
    + )} +
    +) +``` diff --git a/docs/zh-hant/framework/react/reference/hydration.md b/docs/zh-hant/framework/react/reference/hydration.md new file mode 100644 index 00000000000..28078643aa7 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/hydration.md @@ -0,0 +1,130 @@ +--- +source-updated-at: '2025-02-17T12:52:17.000Z' +translation-updated-at: '2025-05-08T20:20:28.004Z' +id: hydration +title: hydration +--- + +## `dehydrate` + +`dehydrate` 會建立一個 `cache` 的凍結表示,之後可以用 `HydrationBoundary` 或 `hydrate` 進行水合 (hydration)。這對於將預先取得的查詢從伺服器傳遞到客戶端,或將查詢持久化到 localStorage 或其他持久化儲存位置非常有用。預設情況下,它只包含當前成功的查詢。 + +```tsx +import { dehydrate } from '@tanstack/react-query' + +const dehydratedState = dehydrate(queryClient, { + shouldDehydrateQuery, + shouldDehydrateMutation, +}) +``` + +**選項** + +- `client: QueryClient` + - **必填** + - 需要進行脫水 (dehydrate) 的 `queryClient` +- `options: DehydrateOptions` + - 選填 + - `shouldDehydrateMutation: (mutation: Mutation) => boolean` + - 選填 + - 是否對突變 (mutation) 進行脫水 + - 此函數會針對快取中的每個突變被呼叫 + - 回傳 `true` 表示將此突變包含在脫水結果中,`false` 則不包含 + - 預設只包含暫停中的突變 + - 如果你想在保留預設行為的同時擴充此函數,可以在回傳語句中導入並執行 `defaultShouldDehydrateMutation` + - `shouldDehydrateQuery: (query: Query) => boolean` + - 選填 + - 是否對查詢進行脫水 + - 此函數會針對快取中的每個查詢被呼叫 + - 回傳 `true` 表示將此查詢包含在脫水結果中,`false` 則不包含 + - 預設只包含成功的查詢 + - 如果你想在保留預設行為的同時擴充此函數,可以在回傳語句中導入並執行 `defaultShouldDehydrateQuery` + - `serializeData?: (data: any) => any` 一個在脫水過程中轉換(序列化)資料的函數 + - `shouldRedactErrors?: (error: unknown) => boolean` + - 選填 + - 是否在脫水過程中隱藏來自伺服器的錯誤 + - 此函數會針對快取中的每個錯誤被呼叫 + - 回傳 `true` 表示隱藏此錯誤,`false` 則不隱藏 + - 預設會隱藏所有錯誤 + +**回傳值** + +- `dehydratedState: DehydratedState` + - 包含後續水合 `queryClient` 所需的所有內容 + - 你**不應該**依賴此回傳值的確切格式,這不是公開 API 的一部分,隨時可能變更 + - 此結果並非序列化格式,如有需要請自行處理 + +### 限制 + +某些儲存系統(例如瀏覽器的 [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API))要求值必須是 JSON 可序列化的。如果你需要脫水無法自動序列化為 JSON 的值(如 `Error` 或 `undefined`),則必須自行序列化。由於預設只包含成功的查詢,若要同時包含 `Errors`,你需要提供 `shouldDehydrateQuery`,例如: + +```tsx +// 伺服器端 +const state = dehydrate(client, { shouldDehydrateQuery: () => true }) // 同時包含 Errors +const serializedState = mySerialize(state) // 將 Error 實例轉換為物件 + +// 客戶端 +const state = myDeserialize(serializedState) // 將物件轉換回 Error 實例 +hydrate(client, state) +``` + +## `hydrate` + +`hydrate` 將先前脫水的狀態加入 `cache` 中。 + +```tsx +import { hydrate } from '@tanstack/react-query' + +hydrate(queryClient, dehydratedState, options) +``` + +**選項** + +- `client: QueryClient` + - **必填** + - 要進行水合的 `queryClient` +- `dehydratedState: DehydratedState` + - **必填** + - 要水合到客戶端的狀態 +- `options: HydrateOptions` + - 選填 + - `defaultOptions: DefaultOptions` + - 選填 + - `mutations: MutationOptions` 用於水合突變的預設突變選項 + - `queries: QueryOptions` 用於水合查詢的預設查詢選項 + - `deserializeData?: (data: any) => any` 一個在將資料放入快取前進行轉換(反序列化)的函數 + - `queryClient?: QueryClient` + - 使用此選項可自訂 QueryClient。否則會使用最近上下文中的 QueryClient + +### 限制 + +如果你嘗試水合的查詢已經存在於 queryCache 中,`hydrate` 只會在資料比快取中的資料更新時覆寫它們。否則,水合**不會**生效。 + +[//]: # 'HydrationBoundary' + +## `HydrationBoundary` + +`HydrationBoundary` 將先前脫水的狀態加入到由 `useQueryClient()` 回傳的 `queryClient` 中。如果客戶端已包含資料,新查詢會根據更新時間戳記智能合併。 + +```tsx +import { HydrationBoundary } from '@tanstack/react-query' + +function App() { + return ... +} +``` + +> 注意:只有 `queries` 可以用 `HydrationBoundary` 進行脫水 + +**選項** + +- `state: DehydratedState` + - 要水合的狀態 +- `options: HydrateOptions` + - 選填 + - `defaultOptions: QueryOptions` + - 用於水合查詢的預設查詢選項 + - `queryClient?: QueryClient` + - 使用此選項可自訂 QueryClient。否則會使用最近上下文中的 QueryClient + +[//]: # 'HydrationBoundary' diff --git a/docs/zh-hant/framework/react/reference/infiniteQueryOptions.md b/docs/zh-hant/framework/react/reference/infiniteQueryOptions.md new file mode 100644 index 00000000000..46e6cc94a04 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/infiniteQueryOptions.md @@ -0,0 +1,21 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:37.299Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +--- + +```tsx +infiniteQueryOptions({ + queryKey, + ...options, +}) +``` + +**選項** + +通常你可以傳遞所有能傳給 [`useInfiniteQuery`](./useInfiniteQuery.md) 的參數給 `infiniteQueryOptions`。某些選項在轉發給像 `queryClient.prefetchInfiniteQuery` 這類函數時不會產生效果,但 TypeScript 仍會容許這些多餘的屬性。 + +- `queryKey: QueryKey` + - **必填** + - 用於生成選項的查詢鍵 (query key)。 diff --git a/docs/zh-hant/framework/react/reference/queryOptions.md b/docs/zh-hant/framework/react/reference/queryOptions.md new file mode 100644 index 00000000000..f8d53ed5173 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/queryOptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:40.534Z' +id: queryOptions +title: queryOptions +--- + +```tsx +queryOptions({ + queryKey, + ...options, +}) +``` + +**選項** + +通常你可以傳遞所有能傳給 [`useQuery`](./useQuery.md) 的參數到 `queryOptions`。某些選項在被轉發到像 `queryClient.prefetchQuery` 這類函數時會無效,但 TypeScript 仍會容許這些多餘的屬性。 + +- `queryKey: QueryKey` + - **必填** + - 用於生成選項的查詢鍵 (query key)。 +- `experimental_prefetchInRender?: boolean` + - 選填 + - 預設為 `false` + - 設為 `true` 時,查詢會在渲染期間預先獲取 (prefetch),這對某些優化情境很有用 + - 需啟用此選項才能使用實驗性的 `useQuery().promise` 功能 diff --git a/docs/zh-hant/framework/react/reference/useInfiniteQuery.md b/docs/zh-hant/framework/react/reference/useInfiniteQuery.md new file mode 100644 index 00000000000..a15ed11b6e4 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useInfiniteQuery.md @@ -0,0 +1,94 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:20:21.908Z' +id: useInfiniteQuery +title: useInfiniteQuery +--- + +```tsx +const { + fetchNextPage, + fetchPreviousPage, + hasNextPage, + hasPreviousPage, + isFetchingNextPage, + isFetchingPreviousPage, + promise, + ...result +} = useInfiniteQuery({ + queryKey, + queryFn: ({ pageParam }) => fetchPage(pageParam), + initialPageParam: 1, + ...options, + getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => + lastPage.nextCursor, + getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) => + firstPage.prevCursor, +}) +``` + +**選項** + +`useInfiniteQuery` 的選項與 [`useQuery` 鉤子 (hook)](./useQuery.md) 相同,但額外增加了以下選項: + +- `queryFn: (context: QueryFunctionContext) => Promise` + - **必填,但僅在未定義預設查詢函式時需要** [`defaultQueryFn`](../guides/default-query-function.md) + - 查詢用來請求資料的函式。 + - 接收一個 [QueryFunctionContext](../guides/query-functions.md#queryfunctioncontext) + - 必須回傳一個會解析資料或拋出錯誤的 promise。 +- `initialPageParam: TPageParam` + - **必填** + - 獲取第一頁時使用的預設頁面參數。 +- `getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null` + - **必填** + - 當此查詢接收到新資料時,此函式會收到無限資料列表的最後一頁、所有頁面的完整陣列,以及 pageParam 資訊。 + - 應回傳一個**單一變數**,該變數將作為最後一個可選參數傳遞給查詢函式。 + - 回傳 `undefined` 或 `null` 表示沒有下一頁可用。 +- `getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) => TPageParam | undefined | null` + - 當此查詢接收到新資料時,此函式會收到無限資料列表的第一頁、所有頁面的完整陣列,以及 pageParam 資訊。 + - 應回傳一個**單一變數**,該變數將作為最後一個可選參數傳遞給查詢函式。 + - 回傳 `undefined` 或 `null` 表示沒有上一頁可用。 +- `maxPages: number | undefined` + - 無限查詢資料中儲存的最大頁數。 + - 當達到最大頁數時,獲取新頁面將導致從頁面陣列中移除第一頁或最後一頁,具體取決於指定的方向。 + - 如果為 `undefined` 或等於 `0`,則頁數沒有限制。 + - 預設值為 `undefined`。 + - 如果 `maxPages` 值大於 `0`,則必須正確定義 `getNextPageParam` 和 `getPreviousPageParam`,以便在需要時允許雙向獲取頁面。 + +**回傳值** + +`useInfiniteQuery` 的回傳屬性與 [`useQuery` 鉤子 (hook)](./useQuery.md) 相同,但額外增加了以下屬性,並且 `isRefetching` 和 `isRefetchError` 有微小差異: + +- `data.pages: TData[]` + - 包含所有頁面的陣列。 +- `data.pageParams: unknown[]` + - 包含所有頁面參數的陣列。 +- `isFetchingNextPage: boolean` + - 當使用 `fetchNextPage` 獲取下一頁時,此值為 `true`。 +- `isFetchingPreviousPage: boolean` + - 當使用 `fetchPreviousPage` 獲取上一頁時,此值為 `true`。 +- `fetchNextPage: (options?: FetchNextPageOptions) => Promise` + - 此函式允許你獲取下一「頁」的結果。 + - `options.cancelRefetch: boolean` 如果設為 `true`,則重複呼叫 `fetchNextPage` 每次都會觸發 `queryFn`,無論前一次呼叫是否已解析。此外,前一次呼叫的結果將被忽略。如果設為 `false`,則重複呼叫 `fetchNextPage` 在第一次呼叫解析前不會有任何效果。預設為 `true`。 +- `fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise` + - 此函式允許你獲取上一「頁」的結果。 + - `options.cancelRefetch: boolean` 與 `fetchNextPage` 相同。 +- `hasNextPage: boolean` + - 如果有下一頁可供獲取(透過 `getNextPageParam` 選項得知),此值為 `true`。 +- `hasPreviousPage: boolean` + - 如果有上一頁可供獲取(透過 `getPreviousPageParam` 選項得知),此值為 `true`。 +- `isFetchNextPageError: boolean` + - 如果在獲取下一頁時查詢失敗,此值為 `true`。 +- `isFetchPreviousPageError: boolean` + - 如果在獲取上一頁時查詢失敗,此值為 `true`。 +- `isRefetching: boolean` + - 當背景重新獲取正在進行時,此值為 `true`,這**不包含**初始的 `pending` 狀態或獲取下一頁/上一頁。 + - 等同於 `isFetching && !isPending && !isFetchingNextPage && !isFetchingPreviousPage`。 +- `isRefetchError: boolean` + - 如果在重新獲取頁面時查詢失敗,此值為 `true`。 +- `promise: Promise` + - 一個穩定的 promise,解析為查詢結果。 + - 可與 `React.use()` 一起使用來獲取資料。 + - 需要在 `QueryClient` 上啟用 `experimental_prefetchInRender` 功能標誌。 + +請注意,命令式獲取呼叫(如 `fetchNextPage`)可能會干擾預設的重新獲取行為,導致資料過時。確保僅在回應使用者操作時呼叫這些函式,或添加條件如 `hasNextPage && !isFetching`。 diff --git a/docs/zh-hant/framework/react/reference/useIsFetching.md b/docs/zh-hant/framework/react/reference/useIsFetching.md new file mode 100644 index 00000000000..edf99556c61 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useIsFetching.md @@ -0,0 +1,27 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:31.746Z' +id: useIsFetching +title: useIsFetching +--- + +`useIsFetching` 是一個可選用的鉤子 (hook),它會回傳你的應用程式正在背景載入或擷取中的查詢 `數量`(適用於全應用程式的載入指示器)。 + +```tsx +import { useIsFetching } from '@tanstack/react-query' +// 有多少查詢正在擷取中? +const isFetching = useIsFetching() +// 符合 posts 前綴的查詢有多少正在擷取中? +const isFetchingPosts = useIsFetching({ queryKey: ['posts'] }) +``` + +**選項** + +- `filters?: QueryFilters`: [查詢過濾器](../guides/filters.md#query-filters) +- `queryClient?: QueryClient`, + - 使用此選項可指定自訂的 QueryClient。若未提供,則會使用最近上下文中的 QueryClient。 + +**回傳值** + +- `isFetching: number` + - 此數值代表你的應用程式目前正在背景載入或擷取中的查詢數量。 diff --git a/docs/zh-hant/framework/react/reference/useIsMutating.md b/docs/zh-hant/framework/react/reference/useIsMutating.md new file mode 100644 index 00000000000..e56cbb961a2 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useIsMutating.md @@ -0,0 +1,27 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:27.705Z' +id: useIsMutating +title: useIsMutating +--- + +`useIsMutating` 是一個可選的鉤子 (hook),它會回傳應用程式正在擷取的變異 (mutation) 數量,適用於全應用程式的載入指示器。 + +```tsx +import { useIsMutating } from '@tanstack/react-query' +// 有多少個變異正在擷取中? +const isMutating = useIsMutating() +// 符合 posts 前綴的變異有多少個正在擷取中? +const isMutatingPosts = useIsMutating({ mutationKey: ['posts'] }) +``` + +**選項** + +- `filters?: MutationFilters`: [變異過濾器](../guides/filters.md#mutation-filters) +- `queryClient?: QueryClient`, + - 使用此選項可指定自訂的 QueryClient,否則將使用最近上下文中的 QueryClient。 + +**回傳值** + +- `isMutating: number` + - 此數字代表應用程式當前正在擷取的變異數量。 diff --git a/docs/zh-hant/framework/react/reference/useMutation.md b/docs/zh-hant/framework/react/reference/useMutation.md new file mode 100644 index 00000000000..5817558c38f --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useMutation.md @@ -0,0 +1,162 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:20:36.993Z' +id: useMutation +title: useMutation +--- + +```tsx +const { + data, + error, + isError, + isIdle, + isPending, + isPaused, + isSuccess, + failureCount, + failureReason, + mutate, + mutateAsync, + reset, + status, + submittedAt, + variables, +} = useMutation( + { + mutationFn, + gcTime, + meta, + mutationKey, + networkMode, + onError, + onMutate, + onSettled, + onSuccess, + retry, + retryDelay, + scope, + throwOnError, + }, + queryClient, +) + +mutate(variables, { + onError, + onSettled, + onSuccess, +}) +``` + +**參數1 (選項)** + +- `mutationFn: (variables: TVariables) => Promise` + - **必填,但僅在未定義預設 mutation 函式時需要** + - 執行非同步任務並返回 promise 的函式 + - `variables` 是 `mutate` 將傳遞給 `mutationFn` 的物件 +- `gcTime: number | Infinity` + - 未使用/非活躍的快取資料在記憶體中保留的時間(毫秒)。當 mutation 的快取變為未使用或非活躍時,該快取資料將在此時間後被垃圾回收。若指定不同的快取時間,將使用最長的那個 + - 設為 `Infinity` 時會停用垃圾回收 + - 注意:最大允許時間約為 24 天。詳見[更多資訊](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value) +- `mutationKey: unknown[]` + - 選填 + - 可設定 mutation key 來繼承透過 `queryClient.setMutationDefaults` 設定的預設值 +- `networkMode: 'online' | 'always' | 'offlineFirst'` + - 選填 + - 預設為 `'online'` + - 詳見[網路模式](../guides/network-mode.md) +- `onMutate: (variables: TVariables) => Promise | TContext | void` + - 選填 + - 此函式會在 mutation 函式執行前觸發,並接收與 mutation 函式相同的變數 + - 適用於對資源執行樂觀更新 (optimistic update),期望 mutation 成功 + - 當 mutation 失敗時,此函式返回的值將傳遞給 `onError` 和 `onSettled` 函式,可用於回滾樂觀更新 +- `onSuccess: (data: TData, variables: TVariables, context: TContext) => Promise | unknown` + - 選填 + - 當 mutation 成功時觸發此函式,並傳遞 mutation 的結果 + - 若返回 promise,將等待其解析後再繼續 +- `onError: (err: TError, variables: TVariables, context?: TContext) => Promise | unknown` + - 選填 + - 當 mutation 發生錯誤時觸發此函式,並傳遞錯誤資訊 + - 若返回 promise,將等待其解析後再繼續 +- `onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise | unknown` + - 選填 + - 當 mutation 成功完成或發生錯誤時觸發此函式,並傳遞資料或錯誤資訊 + - 若返回 promise,將等待其解析後再繼續 +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - 預設為 `0` + - 若為 `false`,失敗的 mutation 不會重試 + - 若為 `true`,失敗的 mutation 會無限重試 + - 若設為數字(如 `3`),失敗的 mutation 會重試直到達到該次數 +- `retryDelay: number | (retryAttempt: number, error: TError) => number` + - 此函式接收 `retryAttempt` 整數和實際錯誤,返回下次嘗試前的延遲時間(毫秒) + - 如 `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` 這樣的函式會套用指數退避 (exponential backoff) + - 如 `attempt => attempt * 1000` 這樣的函式會套用線性退避 (linear backoff) +- `scope: { id: string }` + - 選填 + - 預設為唯一 id(使所有 mutation 並行執行) + - 具有相同 scope id 的 mutation 會序列化執行 +- `throwOnError: undefined | boolean | (error: TError) => boolean` + - 設為 `true` 時,mutation 錯誤會在渲染階段拋出並傳播到最近的錯誤邊界 (error boundary) + - 設為 `false` 時會停用將錯誤拋給錯誤邊界的行為 + - 若設為函式,會接收錯誤並返回布林值,指示是否在錯誤邊界顯示錯誤 (`true`) 或將錯誤作為狀態返回 (`false`) +- `meta: Record` + - 選填 + - 若設定,會在 mutation 快取條目中儲存額外資訊供需要時使用。該資訊可在 `mutation` 可用的任何地方存取(例如 `MutationCache` 的 `onError`、`onSuccess` 函式) + +**參數2 (QueryClient)** + +- `queryClient?: QueryClient` + - 用於使用自訂的 QueryClient。若未提供,則會使用最近上下文中的 QueryClient + +**返回值** + +- `mutate: (variables: TVariables, { onSuccess, onSettled, onError }) => void` + - 可呼叫的 mutation 函式,傳入變數來觸發 mutation,並可選掛鉤額外的回調選項 + - `variables: TVariables` + - 選填 + - 傳遞給 `mutationFn` 的變數物件 + - `onSuccess: (data: TData, variables: TVariables, context: TContext) => void` + - 選填 + - 當 mutation 成功時觸發此函式,並傳遞 mutation 的結果 + - 無返回值函式,回傳值會被忽略 + - `onError: (err: TError, variables: TVariables, context: TContext | undefined) => void` + - 選填 + - 當 mutation 發生錯誤時觸發此函式,並傳遞錯誤資訊 + - 無返回值函式,回傳值會被忽略 + - `onSettled: (data: TData | undefined, error: TError | null, variables: TVariables, context: TContext | undefined) => void` + - 選填 + - 當 mutation 成功完成或發生錯誤時觸發此函式,並傳遞資料或錯誤資訊 + - 無返回值函式,回傳值會被忽略 + - 若發送多個請求,`onSuccess` 只會在你發送的最新請求完成後觸發 +- `mutateAsync: (variables: TVariables, { onSuccess, onSettled, onError }) => Promise` + - 與 `mutate` 類似,但返回可被 await 的 promise +- `status: string` + - 可能值: + - `idle` mutation 函式執行前的初始狀態 + - `pending` mutation 正在執行中 + - `error` 上次 mutation 嘗試導致錯誤 + - `success` 上次 mutation 嘗試成功 +- `isIdle`, `isPending`, `isSuccess`, `isError`: 從 `status` 衍生的布林變數 +- `isPaused: boolean` + - 若 mutation 被 `暫停 (paused)` 則為 `true` + - 詳見[網路模式](../guides/network-mode.md) +- `data: undefined | unknown` + - 預設為 `undefined` + - mutation 最後成功解析的資料 +- `error: null | TError` + - 查詢的錯誤物件(若發生錯誤) +- `reset: () => void` + - 清理 mutation 內部狀態的函式(即將 mutation 重置為初始狀態) +- `failureCount: number` + - mutation 的失敗次數 + - 每次 mutation 失敗時遞增 + - mutation 成功時重置為 `0` +- `failureReason: null | TError` + - mutation 重試的失敗原因 + - mutation 成功時重置為 `null` +- `submittedAt: number` + - mutation 被提交的時間戳 + - 預設為 `0` +- `variables: undefined | TVariables` + - 傳遞給 `mutationFn` 的 `variables` 物件 + - 預設為 `undefined` diff --git a/docs/zh-hant/framework/react/reference/useMutationState.md b/docs/zh-hant/framework/react/reference/useMutationState.md new file mode 100644 index 00000000000..5a3e49dd399 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useMutationState.md @@ -0,0 +1,83 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:37.664Z' +id: useMutationState +title: useMutationState +--- + +`useMutationState` 是一個鉤子 (hook),可讓你存取 `MutationCache` 中的所有變異 (mutations)。你可以傳入 `filters` 來篩選變異,並使用 `select` 來轉換變異狀態。 + +**範例 1:取得所有執行中變異的變數** + +```tsx +import { useMutationState } from '@tanstack/react-query' + +const variables = useMutationState({ + filters: { status: 'pending' }, + select: (mutation) => mutation.state.variables, +}) +``` + +**範例 2:透過 `mutationKey` 取得特定變異的所有資料** + +```tsx +import { useMutation, useMutationState } from '@tanstack/react-query' + +const mutationKey = ['posts'] + +// 我們想取得狀態的某個變異 +const mutation = useMutation({ + mutationKey, + mutationFn: (newPost) => { + return axios.post('/posts', newPost) + }, +}) + +const data = useMutationState({ + // 此變異鍵需與指定變異的變異鍵相符 (參見上方) + filters: { mutationKey }, + select: (mutation) => mutation.state.data, +}) +``` + +**範例 3:透過 `mutationKey` 存取最新的變異資料** +每次呼叫 `mutate` 都會在 `gcTime` 毫秒內新增一個項目至變異快取 (mutation cache)。 + +若要存取最新的呼叫,你可以檢查 `useMutationState` 回傳的最後一個項目。 + +```tsx +import { useMutation, useMutationState } from '@tanstack/react-query' + +const mutationKey = ['posts'] + +// 我們想取得狀態的某個變異 +const mutation = useMutation({ + mutationKey, + mutationFn: (newPost) => { + return axios.post('/posts', newPost) + }, +}) + +const data = useMutationState({ + // 此變異鍵需與指定變異的變異鍵相符 (參見上方) + filters: { mutationKey }, + select: (mutation) => mutation.state.data, +}) + +// 最新的變異資料 +const latest = data[data.length - 1] +``` + +**選項** + +- `options` + - `filters?: MutationFilters`: [變異篩選器](../guides/filters.md#mutation-filters) + - `select?: (mutation: Mutation) => TResult` + - 用於轉換變異狀態。 +- `queryClient?: QueryClient`, + - 用於指定自訂的 QueryClient。若未提供,則會使用最近上下文中的 QueryClient。 + +**回傳值** + +- `Array` + - 會是一個陣列,包含 `select` 為每個符合條件的變異所回傳的值。 diff --git a/docs/zh-hant/framework/react/reference/usePrefetchInfiniteQuery.md b/docs/zh-hant/framework/react/reference/usePrefetchInfiniteQuery.md new file mode 100644 index 00000000000..a7ef69ad76a --- /dev/null +++ b/docs/zh-hant/framework/react/reference/usePrefetchInfiniteQuery.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:28.741Z' +id: usePrefetchInfiniteQuery +title: usePrefetchInfiniteQuery +--- + +```tsx +usePrefetchInfiniteQuery(options) +``` + +**選項** + +你可以將所有能傳遞給 [`queryClient.prefetchInfiniteQuery`](../../../reference/QueryClient.md#queryclientprefetchinfinitequery) 的參數傳遞給 `usePrefetchInfiniteQuery`。請注意以下為必要參數: + +- `queryKey: QueryKey` + + - **必填** + - 渲染期間需預取的查詢鍵 (query key) + +- `queryFn: (context: QueryFunctionContext) => Promise` + + - **必填(但僅在未定義預設查詢函式時適用)** 詳見 [預設查詢函式](../guides/default-query-function.md) 說明。 + +- `initialPageParam: TPageParam` + + - **必填** + - 獲取第一頁時使用的預設頁面參數 (page param)。 + +- `getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null` + + - **必填** + - 當此查詢接收到新資料時,此函式會取得無限資料清單的最後一頁、所有頁面的完整陣列,以及頁面參數資訊。 + - 應返回**單一變數**,該變數將作為最後一個可選參數傳遞給查詢函式。 + - 返回 `undefined` 或 `null` 表示沒有可用的下一頁。 + +- **返回值** + +`usePrefetchInfiniteQuery` 不會返回任何內容,其用途僅在於在渲染期間(於包裹 [`useSuspenseInfiniteQuery`](../reference/useSuspenseInfiniteQuery.md) 元件的 suspense boundary 之前)觸發預取。 diff --git a/docs/zh-hant/framework/react/reference/usePrefetchQuery.md b/docs/zh-hant/framework/react/reference/usePrefetchQuery.md new file mode 100644 index 00000000000..e4be6fd7754 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/usePrefetchQuery.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:21.386Z' +id: usePrefetchQuery +title: usePrefetchQuery +--- + +```tsx +usePrefetchQuery(options) +``` + +**選項** + +你可以傳遞所有能傳給 [`queryClient.prefetchQuery`](../../../reference/QueryClient.md#queryclientprefetchquery) 的參數給 `usePrefetchQuery`。請注意以下為必要參數: + +- `queryKey: QueryKey` + + - **必填** + - 在渲染期間預先擷取的查詢鍵值 (query key) + +- `queryFn: (context: QueryFunctionContext) => Promise` + - **必填(但僅在未定義預設查詢函式時適用)** 詳見[預設查詢函式](../guides/default-query-function.md)說明。 + +**回傳值** + +`usePrefetchQuery` 不會回傳任何內容,其用途僅在渲染階段觸發預先擷取,適用於包覆 [`useSuspenseQuery`](../reference/useSuspenseQuery.md) 元件的 suspense 邊界 (suspense boundary) 之前。 diff --git a/docs/zh-hant/framework/react/reference/useQueries.md b/docs/zh-hant/framework/react/reference/useQueries.md new file mode 100644 index 00000000000..e9249f9af51 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useQueries.md @@ -0,0 +1,69 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:19:38.883Z' +id: useQueries +title: useQueries +--- + +`useQueries` 鉤子 (hook) 可用於獲取數量不固定的查詢 (queries): + +```tsx +const ids = [1, 2, 3] +const results = useQueries({ + queries: ids.map((id) => ({ + queryKey: ['post', id], + queryFn: () => fetchPost(id), + staleTime: Infinity, + })), +}) +``` + +**選項** + +`useQueries` 鉤子接受一個選項物件,其中包含一個 **queries** 鍵,其值為一個陣列,陣列中的查詢選項物件與 [`useQuery` 鉤子](../reference/useQuery.md) 完全相同(不包括 `queryClient` 選項,因為 `QueryClient` 可以在頂層傳入)。 + +- `queryClient?: QueryClient` + - 用於提供自訂的 QueryClient。若未提供,則會使用最近上下文中的 QueryClient。 +- `combine?: (result: UseQueriesResults) => TCombinedResult` + - 用於將多個查詢的結果合併為單一值。 + +> 若在查詢物件陣列中多次使用相同的查詢鍵 (query key),可能會導致查詢之間共享部分數據。為避免此情況,建議去除重複查詢並將結果映射回所需結構。 + +**placeholderData** + +`useQueries` 也有 `placeholderData` 選項,但它不會像 `useQuery` 那樣從先前渲染的查詢中獲取資訊,因為 `useQueries` 的輸入在每次渲染時可能包含不同數量的查詢。 + +**返回值** + +`useQueries` 鉤子返回一個包含所有查詢結果的陣列,其順序與輸入順序相同。 + +## 合併結果 + +如果想將結果中的 `data`(或其他查詢資訊)合併為單一值,可以使用 `combine` 選項。返回的結果會盡可能保持結構共享 (structurally shared),以確保引用穩定性 (referentially stable)。 + +```tsx +const ids = [1, 2, 3] +const combinedQueries = useQueries({ + queries: ids.map((id) => ({ + queryKey: ['post', id], + queryFn: () => fetchPost(id), + })), + combine: (results) => { + return { + data: results.map((result) => result.data), + pending: results.some((result) => result.isPending), + } + }, +}) +``` + +在上述範例中,`combinedQueries` 會是一個包含 `data` 和 `pending` 屬性的物件。請注意,查詢結果的其他屬性將會丟失。 + +### 記憶化 (Memoization) + +`combine` 函數僅在以下情況下重新執行: + +- `combine` 函數本身的引用發生變化 +- 任何查詢結果發生變化 + +這意味著像上面範例中直接內聯的 `combine` 函數會在每次渲染時執行。為避免此情況,可以將 `combine` 函數用 `useCallback` 包裹,或將其提取為一個穩定的函數引用(如果它沒有依賴項)。 diff --git a/docs/zh-hant/framework/react/reference/useQuery.md b/docs/zh-hant/framework/react/reference/useQuery.md new file mode 100644 index 00000000000..82bf764e2f9 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useQuery.md @@ -0,0 +1,256 @@ +--- +source-updated-at: '2025-04-07T12:21:06.000Z' +translation-updated-at: '2025-05-08T20:21:30.979Z' +id: useQuery +title: useQuery +--- + +```tsx +const { + data, + dataUpdatedAt, + error, + errorUpdatedAt, + failureCount, + failureReason, + fetchStatus, + isError, + isFetched, + isFetchedAfterMount, + isFetching, + isInitialLoading, + isLoading, + isLoadingError, + isPaused, + isPending, + isPlaceholderData, + isRefetchError, + isRefetching, + isStale, + isSuccess, + promise, + refetch, + status, +} = useQuery( + { + queryKey, + queryFn, + gcTime, + enabled, + networkMode, + initialData, + initialDataUpdatedAt, + meta, + notifyOnChangeProps, + placeholderData, + queryKeyHashFn, + refetchInterval, + refetchIntervalInBackground, + refetchOnMount, + refetchOnReconnect, + refetchOnWindowFocus, + retry, + retryOnMount, + retryDelay, + select, + staleTime, + structuralSharing, + subscribed, + throwOnError, + }, + queryClient, +) +``` + +**參數1 (選項)** + +- `queryKey: unknown[]` + - **必填** + - 此查詢使用的查詢鍵 (query key)。 + - 查詢鍵會被雜湊成一個穩定的雜湊值。詳見[查詢鍵](../guides/query-keys.md)說明。 + - 當此鍵值變更時,查詢會自動更新(除非 `enabled` 設為 `false`)。 +- `queryFn: (context: QueryFunctionContext) => Promise` + - **若未定義預設查詢函式則為必填**,詳見[預設查詢函式](../guides/default-query-function.md)說明。 + - 查詢用於請求資料的函式。 + - 接收一個[查詢函式上下文 (QueryFunctionContext)](../guides/query-functions.md#queryfunctioncontext)。 + - 必須回傳一個會解析資料或拋出錯誤的 Promise。資料不可為 `undefined`。 +- `enabled: boolean | (query: Query) => boolean` + - 設為 `false` 可禁止此查詢自動執行。 + - 可用於[依賴查詢 (Dependent Queries)](../guides/dependent-queries.md)。 +- `networkMode: 'online' | 'always' | 'offlineFirst'` + - 選填 + - 預設為 `'online'` + - 詳見[網路模式 (Network Mode)](../guides/network-mode.md)說明。 +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - 若為 `false`,失敗的查詢預設不會重試。 + - 若為 `true`,失敗的查詢會無限重試。 + - 若設為數字(如 `3`),失敗的查詢會重試直到失敗次數達到該數字。 + - 用戶端預設為 `3`,伺服器端預設為 `0`。 +- `retryOnMount: boolean` + - 若設為 `false`,當查詢包含錯誤時不會在掛載時重試。預設為 `true`。 +- `retryDelay: number | (retryAttempt: number, error: TError) => number` + - 此函式接收 `retryAttempt` 整數與實際錯誤,並回傳下次重試前的延遲時間(毫秒)。 + - 如 `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` 的函式會套用指數退避。 + - 如 `attempt => attempt * 1000` 的函式會套用線性退避。 +- `staleTime: number | ((query: Query) => number)` + - 選填 + - 預設為 `0` + - 資料被視為過期 (stale) 的時間(毫秒)。此值僅適用於定義它的鉤子 (hook)。 + - 設為 `Infinity` 時,資料永遠不會被視為過期。 + - 設為函式時,會傳入查詢以計算 `staleTime`。 +- `gcTime: number | Infinity` + - 預設為 `5 * 60 * 1000`(5 分鐘)或 SSR 期間為 `Infinity` + - 未使用/非活躍的快取資料保留在記憶體中的時間(毫秒)。當查詢快取變為未使用或非活躍時,此時間後會進行垃圾回收。若指定不同回收時間,會採用最長者。 + - 注意:最大允許時間約為 24 天。詳見[說明](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value)。 + - 設為 `Infinity` 會停用垃圾回收。 +- `queryKeyHashFn: (queryKey: QueryKey) => string` + - 選填 + - 若指定,此函式用於將 `queryKey` 雜湊成字串。 +- `refetchInterval: number | false | ((query: Query) => number | false | undefined)` + - 選填 + - 設為數字時,所有查詢會以此頻率(毫秒)持續重新取得。 + - 設為函式時,會傳入查詢以計算頻率。 +- `refetchIntervalInBackground: boolean` + - 選填 + - 設為 `true` 時,設定為持續重新取得的查詢在分頁/視窗處於背景時仍會繼續重新取得。 +- `refetchOnMount: boolean | "always" | ((query: Query) => boolean | "always")` + - 選填 + - 預設為 `true` + - 設為 `true` 時,若資料過期會在掛載時重新取得。 + - 設為 `false` 時,掛載時不會重新取得。 + - 設為 `"always"` 時,掛載時總是重新取得。 + - 設為函式時,會傳入查詢以計算值。 +- `refetchOnWindowFocus: boolean | "always" | ((query: Query) => boolean | "always")` + - 選填 + - 預設為 `true` + - 設為 `true` 時,若資料過期會在視窗取得焦點時重新取得。 + - 設為 `false` 時,視窗取得焦點時不會重新取得。 + - 設為 `"always"` 時,視窗取得焦點時總是重新取得。 + - 設為函式時,會傳入查詢以計算值。 +- `refetchOnReconnect: boolean | "always" | ((query: Query) => boolean | "always")` + - 選填 + - 預設為 `true` + - 設為 `true` 時,若資料過期會在重新連線時重新取得。 + - 設為 `false` 時,重新連線時不會重新取得。 + - 設為 `"always"` 時,重新連線時總是重新取得。 + - 設為函式時,會傳入查詢以計算值。 +- `notifyOnChangeProps: string[] | "all" | (() => string[] | "all" | undefined)` + - 選填 + - 設定後,元件僅在列出的屬性變更時重新渲染。 + - 例如設為 `['data', 'error']` 時,僅在 `data` 或 `error` 屬性變更時重新渲染。 + - 設為 `"all"` 時,元件會退出智慧追蹤,並在查詢更新時總是重新渲染。 + - 設為函式時,會執行函式以計算屬性清單。 + - 預設會追蹤屬性存取,並僅在追蹤的屬性變更時重新渲染。 +- `select: (data: TData) => unknown` + - 選填 + - 此選項可用於轉換或選取查詢函式回傳資料的一部分。會影響回傳的 `data` 值,但不影響存入查詢快取的內容。 + - `select` 函式僅在 `data` 變更或 `select` 函式本身的參照變更時執行。建議用 `useCallback` 包裹函式以優化。 +- `initialData: TData | () => TData` + - 選填 + - 設定後,此值會用作查詢快取的初始資料(僅在查詢尚未建立或快取時)。 + - 設為函式時,會在共享/根查詢初始化期間呼叫**一次**,並預期同步回傳初始資料。 + - 初始資料預設視為過期,除非設定了 `staleTime`。 + - `initialData` **會持久化**到快取中。 +- `initialDataUpdatedAt: number | (() => number | undefined)` + - 選填 + - 設定後,此值會用作 `initialData` 本身最後更新的時間(毫秒)。 +- `placeholderData: TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData` + - 選填 + - 設定後,此值會用作此特定查詢觀察者 (query observer) 在查詢仍處於 `pending` 狀態時的佔位資料。 + - `placeholderData` **不會持久化**到快取中。 + - 若為 `placeholderData` 提供函式,第一個參數會接收先前觀測的查詢資料(如有),第二個參數會是完整的 previousQuery 實例。 +- `structuralSharing: boolean | (oldData: unknown | undefined, newData: unknown) => unknown)` + - 選填 + - 預設為 `true` + - 設為 `false` 時,會停用查詢結果間的結構共享 (structural sharing)。 + - 設為函式時,舊資料與新資料會傳入此函式,應將其合併為查詢的解析資料。如此可保留舊資料的參照以提升效能,即使資料包含不可序列化的值。 +- `subscribed: boolean` + - 選填 + - 預設為 `true` + - 設為 `false` 時,此 `useQuery` 實例不會訂閱快取。意味著它不會自行觸發 `queryFn`,也不會在資料透過其他方式進入快取時接收更新。 +- `throwOnError: undefined | boolean | (error: TError, query: Query) => boolean` + - 設為 `true` 時,錯誤會在渲染階段拋出並傳遞至最近的錯誤邊界 (error boundary)。 + - 設為 `false` 時,會停用 `suspense` 將錯誤拋至錯誤邊界的預設行為。 + - 設為函式時,會傳入錯誤與查詢,應回傳布林值指示是否在錯誤邊界顯示錯誤(`true`)或將錯誤作為狀態回傳(`false`)。 +- `meta: Record` + - 選填 + - 設定後,會在查詢快取條目中儲存額外資訊供需要時使用。可在 `query` 可用的任何地方存取,也是提供給 `queryFn` 的 `QueryFunctionContext` 的一部分。 + +**參數2 (QueryClient)** + +- `queryClient?: QueryClient`, + - 用於自訂 QueryClient。若未提供,會使用最接近上下文中的 QueryClient。 + +**回傳值** + +- `status: QueryStatus` + - 可能值: + - `pending`:若無快取資料且查詢嘗試尚未完成。 + - `error`:若查詢嘗試導致錯誤。對應的 `error` 屬性包含從嘗試取得收到的錯誤。 + - `success`:若查詢收到無錯誤的回應且準備顯示資料。查詢的 `data` 屬性為成功取得收到的資料,或若查詢的 `enabled` 屬性設為 `false` 且尚未取得時,`data` 為初始化時提供給查詢的第一個 `initialData`。 +- `isPending: boolean` + - 從上述 `status` 變數衍生的布林值,方便使用。 +- `isSuccess: boolean` + - 從上述 `status` 變數衍生的布林值,方便使用。 +- `isError: boolean` + - 從上述 `status` 變數衍生的布林值,方便使用。 +- `isLoadingError: boolean` + - 若首次取得時查詢失敗則為 `true`。 +- `isRefetchError: boolean` + - 若重新取得時查詢失敗則為 `true`。 +- `data: TData` + - 預設為 `undefined`。 + - 查詢最後成功解析的資料。 +- `dataUpdatedAt: number` + - 查詢最近一次回傳 `status` 為 `"success"` 的時間戳記。 +- `error: null | TError` + - 預設為 `null` + - 查詢的錯誤物件(若拋出錯誤)。 +- `errorUpdatedAt: number` + - 查詢最近一次回傳 `status` 為 `"error"` 的時間戳記。 +- `isStale: boolean` + - 若快取中的資料失效或資料比給定的 `staleTime` 舊則為 `true`。 +- `isPlaceholderData: boolean` + - 若顯示的資料為佔位資料則為 `true`。 +- `isFetched: boolean` + - 若查詢已取得過則為 `true`。 +- `isFetchedAfterMount: boolean` + - 若元件掛載後查詢已取得過則為 `true`。 + - 此屬性可用於不顯示任何先前快取的資料。 +- `fetchStatus: FetchStatus` + - `fetching`:當 `queryFn` 執行時為 `true`,包含初始 `pending` 與背景重新取得。 + - `paused`:查詢想取得但被 `paused`。 + - `idle`:查詢未在取得中。 + - 詳見[網路模式 (Network Mode)](../guides/network-mode)說明。 +- `isFetching: boolean` + - 從上述 `fetchStatus` 變數衍生的布林值,方便使用。 +- `isPaused: boolean` + - 從上述 `fetchStatus` 變數衍生的布林值,方便使用。 +- `isRefetching: boolean` + - 當背景重新取得進行中時為 `true`,**不包含**初始 `pending`。 + - 等同於 `isFetching && !isPending`。 +- `isLoading: boolean` + - 當查詢首次取得進行中時為 `true`。 + - 等同於 `isFetching && isPending`。 +- `isInitialLoading: boolean` + - **已棄用** + - `isLoading` 的別名,將在下一主要版本移除。 +- `failureCount: number` + - 查詢的失敗計數。 + - 每次查詢失敗時遞增。 + - 查詢成功時重置為 `0`。 +- `failureReason: null | TError` + - 查詢重試的失敗原因。 + - 查詢成功時重置為 `null`。 +- `errorUpdateCount: number` + - 所有錯誤的總和。 +- `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise` + - 手動重新取得查詢的函式。 + - 若查詢錯誤,錯誤僅會記錄。若想拋出錯誤,傳入 `throwOnError: true` 選項。 + - `cancelRefetch?: boolean` + - 預設為 `true` + - 預設情況下,發出新請求前會取消當前執行中的請求。 + - 設為 `false` 時,若已有請求執行中則不會重新取得。 +- `promise: Promise` + - 一個穩定的 Promise,會以查詢的資料解析。 + - 需在 `QueryClient` 上啟用 `experimental_prefetchInRender` 功能標誌。 diff --git a/docs/zh-hant/framework/react/reference/useQueryClient.md b/docs/zh-hant/framework/react/reference/useQueryClient.md new file mode 100644 index 00000000000..9c33be6f267 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useQueryClient.md @@ -0,0 +1,19 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:19:11.298Z' +id: useQueryClient +title: useQueryClient +--- + +`useQueryClient` 鉤子 (hook) 會回傳當前的 `QueryClient` 實例。 + +```tsx +import { useQueryClient } from '@tanstack/react-query' + +const queryClient = useQueryClient(queryClient?: QueryClient) +``` + +**選項** + +- `queryClient?: QueryClient`, + - 用於指定自訂的 QueryClient。若未提供,則會使用最鄰近上下文 (context) 中的 QueryClient。 diff --git a/docs/zh-hant/framework/react/reference/useQueryErrorResetBoundary.md b/docs/zh-hant/framework/react/reference/useQueryErrorResetBoundary.md new file mode 100644 index 00000000000..39a6ead410c --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useQueryErrorResetBoundary.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2024-04-11T09:16:39.000Z' +translation-updated-at: '2025-05-08T20:19:13.684Z' +id: useQueryErrorResetBoundary +title: useQueryErrorResetBoundary +--- + +這個鉤子 (hook) 會重置最接近的 `QueryErrorResetBoundary` 內任何查詢錯誤。如果沒有定義邊界 (boundary),則會全域重置這些錯誤: + +```tsx +import { useQueryErrorResetBoundary } from '@tanstack/react-query' +import { ErrorBoundary } from 'react-error-boundary' + +const App = () => { + const { reset } = useQueryErrorResetBoundary() + return ( + ( +
    + There was an error! + +
    + )} + > + +
    + ) +} +``` diff --git a/docs/zh-hant/framework/react/reference/useSuspenseInfiniteQuery.md b/docs/zh-hant/framework/react/reference/useSuspenseInfiniteQuery.md new file mode 100644 index 00000000000..723626c765d --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useSuspenseInfiniteQuery.md @@ -0,0 +1,32 @@ +--- +source-updated-at: '2025-04-12T20:05:20.000Z' +translation-updated-at: '2025-05-08T20:19:18.480Z' +id: useSuspenseInfiniteQuery +title: useSuspenseInfiniteQuery +--- + +```tsx +const result = useSuspenseInfiniteQuery(options) +``` + +**選項** + +與 [useInfiniteQuery](../reference/useInfiniteQuery.md) 相同,但以下選項除外: + +- `suspense` +- `throwOnError` +- `enabled` +- `placeholderData` + +**回傳值** + +回傳物件與 [useInfiniteQuery](../reference/useInfiniteQuery.md) 相同,但以下差異: + +- `data` 保證會被定義 +- `isPlaceholderData` 不存在 +- `status` 只會是 `success` 或 `error` + - 衍生的狀態標記會相應設定 + +**注意事項** + +[取消查詢](../guides/query-cancellation.md) 功能無法運作。 diff --git a/docs/zh-hant/framework/react/reference/useSuspenseQueries.md b/docs/zh-hant/framework/react/reference/useSuspenseQueries.md new file mode 100644 index 00000000000..e6ea212f47d --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useSuspenseQueries.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2025-04-12T20:05:20.000Z' +translation-updated-at: '2025-05-08T20:19:14.763Z' +id: useSuspenseQueries +title: useSuspenseQueries +--- + +```tsx +const result = useSuspenseQueries(options) +``` + +**選項** + +與 [useQueries](../reference/useQueries.md) 相同,但每個 `query` 不能包含以下屬性: + +- `suspense` +- `throwOnError` +- `enabled` +- `placeholderData` + +**返回值** + +結構與 [useQueries](../reference/useQueries.md) 相同,但針對每個 `query` 有以下差異: + +- `data` 保證會被定義 +- `isPlaceholderData` 不存在 +- `status` 僅會是 `success` 或 `error` + - 衍生的標誌會相應地設定 + +**注意事項** + +請注意,元件只會在**所有查詢**完成載入後才會重新掛載。因此,如果在所有查詢完成的時間內有查詢變為過時 (stale),重新掛載時會再次獲取資料。為避免此情況,請確保設定足夠高的 `staleTime`。 + +[取消查詢](../guides/query-cancellation.md) 功能在此不適用。 diff --git a/docs/zh-hant/framework/react/reference/useSuspenseQuery.md b/docs/zh-hant/framework/react/reference/useSuspenseQuery.md new file mode 100644 index 00000000000..a34423fcd38 --- /dev/null +++ b/docs/zh-hant/framework/react/reference/useSuspenseQuery.md @@ -0,0 +1,31 @@ +--- +source-updated-at: '2025-04-12T20:05:20.000Z' +translation-updated-at: '2025-05-08T20:19:06.636Z' +id: useSuspenseQuery +title: useSuspenseQuery +--- + +```tsx +const result = useSuspenseQuery(options) +``` + +**選項** + +與 [useQuery](../reference/useQuery.md) 相同,但以下選項除外: + +- `throwOnError` +- `enabled` +- `placeholderData` + +**回傳值** + +回傳物件與 [useQuery](../reference/useQuery.md) 相同,但以下屬性有差異: + +- `data` 保證會被定義 +- `isPlaceholderData` 不存在 +- `status` 只會是 `success` 或 `error` + - 衍生的狀態標記會相應設置 + +**注意事項** + +[查詢取消 (Cancellation)](../guides/query-cancellation.md) 功能在此不適用。 diff --git a/docs/zh-hant/framework/react/typescript.md b/docs/zh-hant/framework/react/typescript.md new file mode 100644 index 00000000000..ddc5a5ccfcf --- /dev/null +++ b/docs/zh-hant/framework/react/typescript.md @@ -0,0 +1,252 @@ +--- +source-updated-at: '2025-03-18T08:45:11.000Z' +translation-updated-at: '2025-05-08T20:18:01.672Z' +id: typescript +title: TypeScript +--- + +React Query 現已使用 **TypeScript** 撰寫,以確保函式庫與您的專案具備型別安全! + +注意事項: + +- 目前型別需使用 TypeScript **v4.7** 或更高版本 +- 此儲存庫中的型別變更視為**非破壞性**,通常以 **patch** 版號發佈(否則每個型別增強都會成為主要版本!)。 +- **強烈建議將 react-query 套件版本鎖定至特定 patch 版本,並在升級時預期型別可能在任一版本間被修正或更新** +- React Query 的非型別相關公開 API 仍嚴格遵循語意化版本控制。 + +## 型別推論 + +React Query 中的型別通常能良好流動,因此您無需自行提供型別註解 + +[//]: # 'TypeInference1' + +```tsx +const { data } = useQuery({ + // ^? const data: number | undefined + queryKey: ['test'], + queryFn: () => Promise.resolve(5), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFC8MQAdqnhIAJnRh0icALwoM2XHgAUAbSqDkIAEa4qAXQA0cFQEo5APjgAFciGAYAdLVQQANgDd0KgKxmzXgB6ILgw8IA9AH5eIA) + +[//]: # 'TypeInference1' +[//]: # 'TypeInference2' + +```tsx +const { data } = useQuery({ + // ^? const data: string | undefined + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFC8MQAdqnhIAJnRh0icALwoM2XHgAUAbSox0IqgF0ANHBUBKOQD44ABXIhgGAHS1UEADYA3dCoCsxw0gwu6EwAXHASUuZhknT2MBAAyjBQwIIA5iaExrwA9Nlw+QUAegD8vEA) + +[//]: # 'TypeInference2' + +當您的 `queryFn` 具有明確定義的回傳型別時,此功能效果最佳。請注意,大多數資料獲取函式庫預設回傳 `any`,因此請確保將其提取為具有適當型別的函式: + +[//]: # 'TypeInference3' + +```tsx +const fetchGroups = (): Promise => + axios.get('/groups').then((response) => response.data) + +const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const data: Group[] | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFCiSw4dAB7AIqUuUpURY1Nx68YeMOjgBxcsjBwAvIjjAAJgC44AO2QgARriK9eDCOdTwS6GAwAWmiNon6ABQAlGYAClLAGAA8vtoA2gC6AHx6qbLiAHQA5h6BVAD02Vpg8sGZMF7o5oG0qJAuarqpdQ0YmUZ0MHTBDjxOLvBInd1EeigY2Lh4gfFUxX6lVIkANKQe3nGlvTwFBXAHhwB6APxwA65wI3RmW0lwAD4o5kboJMDm6Ea8QA) + +[//]: # 'TypeInference3' + +## 型別縮窄 + +React Query 使用[判別聯合型別 (discriminated union type)](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) 作為查詢結果,以 `status` 欄位和衍生的狀態布林標誌進行判別。這讓您可以檢查例如 `success` 狀態來確保 `data` 已定義: + +[//]: # 'TypeNarrowing' + +```tsx +const { data, isSuccess } = useQuery({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), +}) + +if (isSuccess) { + data + // ^? const data: number +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAORToCGAxjALQCOO+VAsAFC8MQAdqnhIAJnRh0ANHGCoAysgYN0qVETgBeFBmy48ACgDaVGGphUAurMMBKbQD44ABXIh56AHS1UEADYAbuiGAKx2dry8wCRwhvJKKmqoDgi8cBlwElK8APS5GQB6APy8hLxAA) + +[//]: # 'TypeNarrowing' + +## 錯誤欄位的型別定義 + +錯誤的型別預設為 `Error`,因為這是大多數使用者所預期的。 + +[//]: # 'TypingError' + +```tsx +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: Error +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFZVLWR9VQ5ADSkwWGZ9WOSnJxwl1cAegD8QA) + +[//]: # 'TypingError' + +如果您想拋出自訂錯誤,或根本不是 `Error` 的內容,您可以指定錯誤欄位的型別: + +[//]: # 'TypingError2' + +```tsx +const { error } = useQuery(['groups'], fetchGroups) +// ^? const error: string | null +``` + +[//]: # 'TypingError2' + +然而,這會導致 `useQuery` 的其他泛型型別推論失效。通常不建議拋出非 `Error` 的內容,因此如果您有像 `AxiosError` 這樣的子類別,可以使用*型別縮窄*來使錯誤欄位更具體: + +[//]: # 'TypingError3' + +```tsx +import axios from 'axios' + +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: Error | null + +if (axios.isAxiosError(error)) { + error + // ^? const error: AxiosError +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA) + +[//]: # 'TypingError3' + +### 註冊全域錯誤型別 + +TanStack Query v5 允許透過擴充 `Register` 介面來設定全域錯誤型別,而無需在呼叫端指定泛型。這將確保型別推論仍有效,但錯誤欄位將是指定的型別: + +[//]: # 'RegisterErrorType' + +```tsx +import '@tanstack/react-query' + +declare module '@tanstack/react-query' { + interface Register { + defaultError: AxiosError + } +} + +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: AxiosError | null +``` + +[//]: # 'RegisterErrorType' +[//]: # 'TypingMeta' + +## 型別化 Meta + +### 註冊全域 Meta + +類似於註冊[全域錯誤型別](#registering-a-global-error),您也可以註冊全域 `Meta` 型別。這確保了[查詢](./reference/useQuery.md)和[變異](./reference/useMutation.md)上的選用 `meta` 欄位保持一致且具備型別安全。請注意,註冊的型別必須擴展 `Record`,以確保 `meta` 仍為物件。 + +```ts +import '@tanstack/react-query' + +interface MyMeta extends Record { + // 您的 meta 型別定義。 +} + +declare module '@tanstack/react-query' { + interface Register { + queryMeta: MyMeta + mutationMeta: MyMeta + } +} +``` + +[//]: # 'TypingMeta' +[//]: # 'TypingQueryAndMutationKeys' + +## 型別化查詢與變異鍵 + +### 註冊查詢與變異鍵型別 + +同樣類似於註冊[全域錯誤型別](#registering-a-global-error),您也可以註冊全域 `QueryKey` 和 `MutationKey` 型別。這讓您可以為鍵提供更多結構,符合您應用程式的層次結構,並讓它們在函式庫的所有介面上保持型別化。請注意,註冊的型別必須擴展 `Array` 型別,以確保您的鍵仍為陣列。 + +```ts +import '@tanstack/react-query' + +type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray] + +declare module '@tanstack/react-query' { + interface Register { + queryKey: QueryKey + mutationKey: QueryKey + } +} +``` + +[//]: # 'TypingQueryAndMutationKeys' +[//]: # 'TypingQueryOptions' + +## 型別化查詢選項 + +如果您將查詢選項內聯至 `useQuery`,您將獲得自動型別推論。然而,您可能希望將查詢選項提取到單獨的函式中,以便在 `useQuery` 和例如 `prefetchQuery` 之間共享。在這種情況下,您將失去型別推論。要重新獲得它,您可以使用 `queryOptions` 輔助函式: + +```ts +import { queryOptions } from '@tanstack/react-query' + +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +useQuery(groupOptions()) +queryClient.prefetchQuery(groupOptions()) +``` + +此外,`queryOptions` 回傳的 `queryKey` 知道與其關聯的 `queryFn`,我們可以利用該型別資訊來讓像 `queryClient.getQueryData` 這樣的函式也能感知這些型別: + +```ts +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +const data = queryClient.getQueryData(groupOptions().queryKey) +// ^? const data: Group[] | undefined +``` + +若沒有 `queryOptions`,`data` 的型別將為 `unknown`,除非我們傳遞泛型給它: + +```ts +const data = queryClient.getQueryData(['groups']) +``` + +[//]: # 'TypingQueryOptions' +[//]: # 'Materials' + +## 延伸閱讀 + +有關型別推論的技巧與訣竅,請參閱社群資源中的 [React Query 與 TypeScript](./community/tkdodos-blog.md#6-react-query-and-typescript)。要了解如何獲得最佳型別安全性,您可以閱讀 [型別安全的 React Query](./community/tkdodos-blog.md#19-type-safe-react-query)。 + +[//]: # 'Materials' + +## 使用 `skipToken` 安全地停用查詢 + +如果您使用 TypeScript,可以使用 `skipToken` 來停用查詢。這在您想根據條件停用查詢但仍希望保持查詢型別安全時非常有用。更多資訊請參閱[停用查詢](./guides/disabling-queries.md)指南。 diff --git a/docs/zh-hant/framework/react/videos.md b/docs/zh-hant/framework/react/videos.md new file mode 100644 index 00000000000..2cba8ca3942 --- /dev/null +++ b/docs/zh-hant/framework/react/videos.md @@ -0,0 +1,73 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:16:21.124Z' +id: videos +title: 影片與演講 +--- + + + +[點此查看上述演講所使用的程式碼庫](https://github.com/tannerlinsley/react-query-blog-refactor-example) + + + + + + + + diff --git a/docs/zh-hant/framework/solid/community/community-projects.md b/docs/zh-hant/framework/solid/community/community-projects.md new file mode 100644 index 00000000000..c6c1306c50e --- /dev/null +++ b/docs/zh-hant/framework/solid/community/community-projects.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.036Z' +id: community-projects +title: Community Projects +ref: docs/zh-hant/framework/react/community/community-projects.md +replace: + React Query: TanStack Query +--- diff --git a/docs/zh-hant/framework/solid/community/tkdodos-blog.md b/docs/zh-hant/framework/solid/community/tkdodos-blog.md new file mode 100644 index 00000000000..68195f62496 --- /dev/null +++ b/docs/zh-hant/framework/solid/community/tkdodos-blog.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.030Z' +id: tkdodos-blog +title: TkDodo's Blog +ref: docs/zh-hant/framework/react/community/tkdodos-blog.md +--- diff --git a/docs/zh-hant/framework/solid/devtools.md b/docs/zh-hant/framework/solid/devtools.md new file mode 100644 index 00000000000..b78674ad2bc --- /dev/null +++ b/docs/zh-hant/framework/solid/devtools.md @@ -0,0 +1,83 @@ +--- +source-updated-at: '2024-08-21T11:18:15.000Z' +translation-updated-at: '2025-05-08T20:16:26.028Z' +id: devtools +title: 開發工具 +--- + +揮舞你的雙手並歡呼吧,因為 Solid Query 附帶了專用的開發者工具 (devtools)!🥳 + +當你開始使用 Solid Query 時,你會希望這些開發者工具隨時在手邊。它們能幫助你可視化 Solid Query 的所有內部運作,並可能在緊要關頭為你節省數小時的除錯時間! + +## 安裝與導入開發者工具 + +開發者工具是一個獨立的套件,需要額外安裝: + +```bash +npm i @tanstack/solid-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/solid-query-devtools +``` + +或 + +```bash +yarn add @tanstack/solid-query-devtools +``` + +或 + +```bash +bun add @tanstack/solid-query-devtools +``` + +你可以這樣導入開發者工具: + +```tsx +import { SolidQueryDevtools } from '@tanstack/solid-query-devtools' +``` + +預設情況下,Solid Query 開發者工具僅在 `isServer === true` 時包含在打包檔案中([`isServer`](https://github.com/solidjs/solid/blob/a72d393a07b22f9b7496e5eb93712188ccce0d28/packages/solid/web/src/index.ts#L37) 來自 `solid-js/web` 套件),因此你無需擔心在生產環境建置時需要排除它們。 + +## 浮動模式 (Floating Mode) + +浮動模式會將開發者工具作為一個固定的浮動元素掛載到你的應用中,並在螢幕角落提供一個切換按鈕來顯示或隱藏開發者工具。此切換狀態會儲存在 localStorage 中,並在重新載入時記住。 + +將以下程式碼放在你的 Solid 應用中盡可能高的位置。越接近頁面的根元素,效果越好! + +```tsx +import { SolidQueryDevtools } from '@tanstack/solid-query-devtools' + +function App() { + return ( + + {/* 你的應用程式其餘部分 */} + + + ) +} +``` + +### 選項 + +- `initialIsOpen: Boolean` + - 設為 `true` 可讓開發者工具預設為開啟狀態 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` + - 預設為 `bottom-right` + - Solid Query 標誌按鈕的位置,用於開啟和關閉開發者工具面板 +- `position?: "top" | "bottom" | "left" | "right"` + - 預設為 `bottom` + - Solid Query 開發者工具面板的位置 +- `client?: QueryClient`, + - 使用此選項可自訂 QueryClient。否則會使用最近上下文中的 QueryClient。 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 使用此選項可預先定義一些可在查詢中觸發的錯誤。當從 UI 觸發該錯誤時,初始化器會(針對特定查詢)被調用。它必須回傳一個 Error。 +- `styleNonce?: string` + - 使用此選項可傳遞一個 nonce 給添加到文件頭部的 style 標籤。這在使用內容安全政策 (CSP) nonce 來允許內聯樣式時非常有用。 +- `shadowDOMTarget?: ShadowRoot` + - 預設行為會將開發者工具的樣式應用到 DOM 中的 head 標籤。 + - 使用此選項可傳遞一個 shadow DOM 目標給開發者工具,這樣樣式就會在 shadow DOM 內應用,而不是在 light DOM 的 head 標籤內。 diff --git a/docs/zh-hant/framework/solid/guides/advanced-ssr.md b/docs/zh-hant/framework/solid/guides/advanced-ssr.md new file mode 100644 index 00000000000..2507c2571a1 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/advanced-ssr.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:19:00.492Z' +id: advanced-ssr +title: 進階 SSR +--- + +即將推出 diff --git a/docs/zh-hant/framework/solid/guides/background-fetching-indicators.md b/docs/zh-hant/framework/solid/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..656869297e6 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/background-fetching-indicators.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.740Z' +id: background-fetching-indicators +title: Background Fetching Indicators +ref: docs/zh-hant/framework/react/guides/background-fetching-indicators.md +replace: + 'useMutation[(]': 'useMutation(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/caching.md b/docs/zh-hant/framework/solid/guides/caching.md new file mode 100644 index 00000000000..40fbf8e2b82 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/caching.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.735Z' +id: caching +title: Caching Examples +ref: docs/zh-hant/framework/react/guides/caching.md +--- diff --git a/docs/zh-hant/framework/solid/guides/default-query-function.md b/docs/zh-hant/framework/solid/guides/default-query-function.md new file mode 100644 index 00000000000..b09bf524c83 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/default-query-function.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.729Z' +id: default-query-function +title: Default Query Function +ref: docs/zh-hant/framework/react/guides/default-query-function.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/dependent-queries.md b/docs/zh-hant/framework/solid/guides/dependent-queries.md new file mode 100644 index 00000000000..521541b88e7 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/dependent-queries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.723Z' +id: dependent-queries +title: Dependent Queries +ref: docs/zh-hant/framework/react/guides/dependent-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/disabling-queries.md b/docs/zh-hant/framework/solid/guides/disabling-queries.md new file mode 100644 index 00000000000..66f012dd838 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/disabling-queries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.717Z' +id: disabling-queries +title: Disabling/Pausing Queries +ref: docs/zh-hant/framework/react/guides/disabling-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/does-this-replace-client-state.md b/docs/zh-hant/framework/solid/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..e420fdd9e3f --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/does-this-replace-client-state.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.712Z' +id: does-this-replace-client-state +title: Does TanStack Query replace global state managers? +ref: docs/zh-hant/framework/react/guides/does-this-replace-client-state.md +replace: + hook: function +--- diff --git a/docs/zh-hant/framework/solid/guides/filters.md b/docs/zh-hant/framework/solid/guides/filters.md new file mode 100644 index 00000000000..2affef5b138 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/filters.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.706Z' +id: filters +title: Filters +ref: docs/zh-hant/framework/react/guides/filters.md +--- diff --git a/docs/zh-hant/framework/solid/guides/important-defaults.md b/docs/zh-hant/framework/solid/guides/important-defaults.md new file mode 100644 index 00000000000..08d65ebe3c4 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/important-defaults.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.701Z' +id: important-defaults +title: Important Defaults +ref: docs/zh-hant/framework/react/guides/important-defaults.md +--- diff --git a/docs/zh-hant/framework/solid/guides/infinite-queries.md b/docs/zh-hant/framework/solid/guides/infinite-queries.md new file mode 100644 index 00000000000..aa5b4043741 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/infinite-queries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.695Z' +id: infinite-queries +title: Infinite Queries +ref: docs/zh-hant/framework/react/guides/infinite-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/initial-query-data.md b/docs/zh-hant/framework/solid/guides/initial-query-data.md new file mode 100644 index 00000000000..86735e18063 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/initial-query-data.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.689Z' +id: initial-query-data +title: Initial Query Data +ref: docs/zh-hant/framework/react/guides/initial-query-data.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/invalidations-from-mutations.md b/docs/zh-hant/framework/solid/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..6e2f5e63dbb --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/invalidations-from-mutations.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.684Z' +id: invalidations-from-mutations +title: Invalidations from Mutations +ref: docs/zh-hant/framework/react/guides/invalidations-from-mutations.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/mutations.md b/docs/zh-hant/framework/solid/guides/mutations.md new file mode 100644 index 00000000000..42fa62bd353 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/mutations.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.678Z' +id: mutations +title: Mutations +ref: docs/zh-hant/framework/react/guides/mutations.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/network-mode.md b/docs/zh-hant/framework/solid/guides/network-mode.md new file mode 100644 index 00000000000..2816ea7451d --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/network-mode.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.672Z' +id: network-mode +title: Network Mode +ref: docs/zh-hant/framework/react/guides/network-mode.md +--- diff --git a/docs/zh-hant/framework/solid/guides/optimistic-updates.md b/docs/zh-hant/framework/solid/guides/optimistic-updates.md new file mode 100644 index 00000000000..a74b7da4bde --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/optimistic-updates.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.666Z' +id: optimistic-updates +title: Optimistic Updates +ref: docs/zh-hant/framework/react/guides/optimistic-updates.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/paginated-queries.md b/docs/zh-hant/framework/solid/guides/paginated-queries.md new file mode 100644 index 00000000000..4299a52db4e --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/paginated-queries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.661Z' +id: paginated-queries +title: Paginated / Lagged Queries +ref: docs/zh-hant/framework/react/guides/paginated-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/parallel-queries.md b/docs/zh-hant/framework/solid/guides/parallel-queries.md new file mode 100644 index 00000000000..23daab406b6 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/parallel-queries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.655Z' +id: parallel-queries +title: Parallel Queries +ref: docs/zh-hant/framework/react/guides/parallel-queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/placeholder-query-data.md b/docs/zh-hant/framework/solid/guides/placeholder-query-data.md new file mode 100644 index 00000000000..19d5703b29b --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/placeholder-query-data.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.649Z' +id: placeholder-query-data +title: Placeholder Query Data +ref: docs/zh-hant/framework/react/guides/placeholder-query-data.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' + useMemo: createMemo +--- diff --git a/docs/zh-hant/framework/solid/guides/prefetching.md b/docs/zh-hant/framework/solid/guides/prefetching.md new file mode 100644 index 00000000000..2be18fbdaac --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/prefetching.md @@ -0,0 +1,16 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.644Z' +id: prefetching +title: Prefetching +ref: docs/zh-hant/framework/react/guides/prefetching.md +replace: + React.lazy: Solid.lazy + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' + /docs/framework/react/examples/basic-react-query-file-based: /docs/framework/solid/examples/basic-solid-query-file-based +--- diff --git a/docs/zh-hant/framework/solid/guides/queries.md b/docs/zh-hant/framework/solid/guides/queries.md new file mode 100644 index 00000000000..d1027d4a7df --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/queries.md @@ -0,0 +1,13 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.638Z' +id: queries +title: Queries +ref: docs/zh-hant/framework/react/guides/queries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/query-cancellation.md b/docs/zh-hant/framework/solid/guides/query-cancellation.md new file mode 100644 index 00000000000..e5161454268 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/query-cancellation.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.632Z' +id: query-cancellation +title: Query Cancellation +ref: docs/zh-hant/framework/react/guides/query-cancellation.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- + +[//]: # 'Example7' + +```ts +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, +}) + +const queryClient = useQueryClient() + +function onButtonClick() { + queryClient.cancelQueries({ queryKey: ['todos'] }) +} +``` + +[//]: # 'Example7' diff --git a/docs/zh-hant/framework/solid/guides/query-functions.md b/docs/zh-hant/framework/solid/guides/query-functions.md new file mode 100644 index 00000000000..16beaf6fac1 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/query-functions.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.626Z' +id: query-functions +title: Query Functions +ref: docs/zh-hant/framework/react/guides/query-functions.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/query-invalidation.md b/docs/zh-hant/framework/solid/guides/query-invalidation.md new file mode 100644 index 00000000000..39bbb6b5779 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/query-invalidation.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.621Z' +id: query-invalidation +title: Query Invalidation +ref: docs/zh-hant/framework/react/guides/query-invalidation.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/query-keys.md b/docs/zh-hant/framework/solid/guides/query-keys.md new file mode 100644 index 00000000000..25cca1f5205 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/query-keys.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.615Z' +id: query-keys +title: Query Keys +ref: docs/zh-hant/framework/react/guides/query-keys.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/query-options.md b/docs/zh-hant/framework/solid/guides/query-options.md new file mode 100644 index 00000000000..c4f7cfac040 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/query-options.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.610Z' +id: query-options +title: Query Options +ref: docs/zh-hant/framework/react/guides/query-options.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/query-retries.md b/docs/zh-hant/framework/solid/guides/query-retries.md new file mode 100644 index 00000000000..120012ff31c --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/query-retries.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.604Z' +id: query-retries +title: Query Retries +ref: docs/zh-hant/framework/react/guides/query-retries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/request-waterfalls.md b/docs/zh-hant/framework/solid/guides/request-waterfalls.md new file mode 100644 index 00000000000..eda0c197958 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/request-waterfalls.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.598Z' +id: request-waterfalls +title: Request Waterfalls +ref: docs/zh-hant/framework/react/guides/request-waterfalls.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/scroll-restoration.md b/docs/zh-hant/framework/solid/guides/scroll-restoration.md new file mode 100644 index 00000000000..29663667660 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/scroll-restoration.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.592Z' +id: scroll-restoration +title: Scroll Restoration +ref: docs/zh-hant/framework/react/guides/scroll-restoration.md +--- diff --git a/docs/zh-hant/framework/solid/guides/ssr.md b/docs/zh-hant/framework/solid/guides/ssr.md new file mode 100644 index 00000000000..d763fc7c463 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/ssr.md @@ -0,0 +1,8 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:56.586Z' +id: ssr +title: SSR +--- + +即將推出 diff --git a/docs/zh-hant/framework/solid/guides/suspense.md b/docs/zh-hant/framework/solid/guides/suspense.md new file mode 100644 index 00000000000..3e96b3ff2c1 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/suspense.md @@ -0,0 +1,44 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:19:10.251Z' +id: suspense +title: Suspense +--- + +Solid Query 也能與 Solid 的 [Suspense](https://docs.solidjs.com/reference/components/suspense) API 搭配使用。 + +要實現這點,你需要用 Vue 提供的 `Suspense` 元件包裹可暫停 (suspendable) 的元件: + +```tsx +import { Suspense } from 'solid-js' +;}> + + +``` + +你可以使用 `solid-query` 提供的非同步 `suspense` 函式: + +```vue + +``` + +## 渲染時擷取 (Fetch-on-render) vs 隨擷取隨渲染 (Render-as-you-fetch) + +開箱即用,Solid Query 在 `suspense` 模式下作為**渲染時擷取 (Fetch-on-render)** 解決方案表現出色,無需額外配置。這意味著當你的元件嘗試掛載時,它們會觸發查詢擷取並暫停,但僅在你導入並掛載它們後才會發生。如果你想更進一步,實現**隨擷取隨渲染 (Render-as-you-fetch)** 模式,我們建議在路由回調和/或用戶互動事件上實作[預擷取 (Prefetching)](../prefetching),以便在元件掛載前就開始載入查詢,甚至可以在導入或掛載其父元件之前就開始。 diff --git a/docs/zh-hant/framework/solid/guides/testing.md b/docs/zh-hant/framework/solid/guides/testing.md new file mode 100644 index 00000000000..b679af8be8c --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/testing.md @@ -0,0 +1,6 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.053Z' +id: testing +title: Testing +--- diff --git a/docs/zh-hant/framework/solid/guides/updates-from-mutation-responses.md b/docs/zh-hant/framework/solid/guides/updates-from-mutation-responses.md new file mode 100644 index 00000000000..5bf946ffe19 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/updates-from-mutation-responses.md @@ -0,0 +1,15 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.048Z' +id: updates-from-mutation-responses +title: Updates from Mutation Responses +ref: docs/zh-hant/framework/react/guides/updates-from-mutation-responses.md +replace: + React: Solid + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/guides/window-focus-refetching.md b/docs/zh-hant/framework/solid/guides/window-focus-refetching.md new file mode 100644 index 00000000000..527be5b9380 --- /dev/null +++ b/docs/zh-hant/framework/solid/guides/window-focus-refetching.md @@ -0,0 +1,14 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.041Z' +id: window-focus-refetching +title: Window Focus Refetching +ref: docs/zh-hant/framework/react/guides/window-focus-refetching.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' + 'useQuery[(]': 'useQuery(() => ' + 'useQueries[(]': 'useQueries(() => ' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' +--- diff --git a/docs/zh-hant/framework/solid/installation.md b/docs/zh-hant/framework/solid/installation.md new file mode 100644 index 00000000000..280a59cd49e --- /dev/null +++ b/docs/zh-hant/framework/solid/installation.md @@ -0,0 +1,61 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-08T20:15:59.011Z' +id: installation +title: 安裝 +--- + +# 安裝 (Installation) + +您可以透過 [NPM](https://npmjs.com/) 安裝 Solid Query,或是使用傳統的 ` +``` + +### 需求 (Requirements) + +Solid Query 針對現代瀏覽器進行了優化,相容於以下瀏覽器配置: + +``` +Chrome >= 91 +Firefox >= 90 +Edge >= 91 +Safari >= 15 +iOS >= 15 +Opera >= 77 +``` + +> 根據您的環境,可能需要加入 polyfill。如果您需要支援舊版瀏覽器,需自行從 `node_modules` 轉譯函式庫。 diff --git a/docs/zh-hant/framework/solid/overview.md b/docs/zh-hant/framework/solid/overview.md new file mode 100644 index 00000000000..2ba0e72681a --- /dev/null +++ b/docs/zh-hant/framework/solid/overview.md @@ -0,0 +1,140 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:16:43.252Z' +id: overview +title: 概述 +--- + +Solid Query 是 TanStack Query 的官方 SolidJS 轉接器,能讓你在網頁應用程式中輕鬆實現 **資料獲取、快取、同步與更新伺服器狀態 (server state)**。 + +## 動機 + +SolidJS 作為一個快速、反應式且宣告式的使用者介面建構函式庫,近年來越來越受歡迎。它內建了許多開箱即用的功能,像是 `createSignal` 和 `createStore` 等基本功能非常適合管理客戶端狀態 (client state)。與其他 UI 函式庫不同,SolidJS 對於非同步資料管理有明確的設計理念。`createResource` API 就是一個在 SolidJS 應用中處理伺服器狀態 (server state) 的絕佳基本功能。`resource` 是一種特殊的 signal,可以在資料載入狀態時觸發 `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 ( +
    +
    Static Content
    + {/* An error while fetching will be caught by the ErrorBoundary */} + Something went wrong!
    }> + {/* Suspense will trigger a loading state while the data is being fetched */} + Loading...}> +
    {repository()?.updated_at}
    +
    + + + ) +} + +const root = document.getElementById('root') + +render(() => , root!) +``` + +這非常棒!只需幾行程式碼,你就能從 API 獲取資料並處理載入和錯誤狀態。但是,隨著應用程式複雜度增加,你會需要更多功能來有效管理伺服器狀態 (server state)。這是因為 **伺服器狀態與客戶端狀態完全不同**。首先,伺服器狀態: + +- 儲存在遠端,你無法控制或擁有該位置 +- 需要非同步 API 來獲取和更新 +- 意味著共享所有權,其他人可能在你不察覺時更改資料 +- 如果不小心處理,應用中的資料可能會「過時」 + +一旦你理解了應用中伺服器狀態的本質,**更多挑戰將接踵而至**,例如: + +- 快取...(可能是程式設計中最難的事情) +- 將多個相同資料的請求合併為單一請求 +- 在背景更新「過時」資料 +- 知道資料何時「過時」 +- 盡快反映資料更新 +- 效能優化,如分頁 (pagination) 和懶載入 (lazy loading) 資料 +- 管理伺服器狀態的記憶體與垃圾回收 +- 透過結構共享 (structural sharing) 記憶化查詢結果 + +這就是 **Solid Query** 的用武之地。這個函式庫封裝了 `createResource`,並提供一組鉤子 (hooks) 和工具來有效管理伺服器狀態。它開箱即用,**零配置即可運作良好,並能隨著應用成長自訂**。 + +從技術角度來看,Solid Query 可能會: + +- 幫助你移除應用中**大量**複雜且難以理解的程式碼,僅用幾行 Solid Query 邏輯取代 +- 使應用更易維護,輕鬆新增功能,無需擔心連接新的伺服器狀態資料來源 +- 直接影響終端使用者,讓應用感覺比以往更快、反應更靈敏 +- 可能幫助你節省頻寬並提升記憶體效能 + +## 說夠了,直接看程式碼吧! + +在下面的範例中,你可以看到 Solid Query 最基本且簡單的形式,用於獲取 TanStack Query 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 ( +
    +
    Static Content
    + {/* An error while fetching will be caught by the ErrorBoundary */} + Something went wrong!
    }> + {/* Suspense will trigger a loading state while the data is being fetched */} + Loading...}> + {/* + The `data` property on a query is a SolidJS resource + so it will work with Suspense and transitions out of the box! + */} +
    {repositoryQuery.data?.updated_at}
    +
    + + + ) +} + +const root = document.getElementById('root') +const client = new QueryClient() + +render( + () => ( + + + + ), + root!, +) +``` + +## 嗯,這樣看起來程式碼更多了,但做的事情一樣? + +是的!但這幾行程式碼開啟了全新的可能性。在上面的範例中,你的查詢會被快取 5 分鐘,這意味著如果應用中任何地方有新的元件在 5 分鐘內使用相同的查詢,它不會重新獲取資料,而是使用快取的資料。這只是 Solid Query 開箱即用的眾多功能之一。其他功能包括: + +- **自動重新獲取 (Automatic Refetching)**:當查詢「過時」(根據 `staleTime` 選項)時,會在背景自動重新獲取 +- **自動快取 (Automatic Caching)**:查詢預設會被快取並在應用中共享 +- **請求去重 (Request Deduplication)**:多個元件可以共享相同的查詢並發送單一請求 +- **自動垃圾回收 (Automatic Garbage Collection)**:不再需要的查詢會被自動垃圾回收 +- **視窗焦點重新獲取 (Window Focus Refetching)**:當應用重新獲得焦點時,查詢會自動重新獲取 +- **分頁 (Pagination)**:內建分頁支援 +- **請求取消 (Request Cancellation)**:自動取消過時或不必要的請求 +- **輪詢/即時更新 (Polling/Realtime)**:透過簡單的 `refetchInterval` 選項即可輕鬆為查詢添加輪詢或即時更新 +- **伺服器渲染支援 (SSR Support)**:Solid Query 與伺服器端渲染 (SSR) 完美配合 +- **樂觀更新 (Optimistic Updates)**:輕鬆透過樂觀更新快取 +- **還有更多...** diff --git a/docs/zh-hant/framework/solid/plugins/broadcastQueryClient.md b/docs/zh-hant/framework/solid/plugins/broadcastQueryClient.md new file mode 100644 index 00000000000..c554c08a4b4 --- /dev/null +++ b/docs/zh-hant/framework/solid/plugins/broadcastQueryClient.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.024Z' +id: broadcastQueryClient +title: broadcastQueryClient (Experimental) +ref: docs/zh-hant/framework/react/plugins/broadcastQueryClient.md +replace: + react-query: vue-query +--- diff --git a/docs/zh-hant/framework/solid/plugins/createPersister.md b/docs/zh-hant/framework/solid/plugins/createPersister.md new file mode 100644 index 00000000000..7b78b57081e --- /dev/null +++ b/docs/zh-hant/framework/solid/plugins/createPersister.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.019Z' +id: createPersister +title: experimental_createPersister +ref: docs/zh-hant/framework/react/plugins/createPersister.md +replace: + react-query: solid-query +--- diff --git a/docs/zh-hant/framework/solid/quick-start.md b/docs/zh-hant/framework/solid/quick-start.md new file mode 100644 index 00000000000..385f22da6b1 --- /dev/null +++ b/docs/zh-hant/framework/solid/quick-start.md @@ -0,0 +1,231 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:16:38.217Z' +id: quick-start +title: 快速開始 +--- + +`@tanstack/solid-query` 套件提供了與 SolidJS 搭配使用 TanStack Query 的一流 API。 + +## 範例 + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/solid-query' +import { Switch, Match, For } from 'solid-js' + +const queryClient = new QueryClient() + +function Example() { + const query = useQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) + + return ( +
    + + +

    Loading...

    +
    + +

    Error: {query.error.message}

    +
    + + {(todo) =>

    {todo.title}

    }
    +
    +
    +
    + ) +} + +function App() { + return ( + + + + ) +} +``` + +## 可用函式 + +Solid Query 提供了實用的基本元件與函式,能讓管理 SolidJS 應用程式中的伺服器狀態 (server state) 更加容易。 + +- `useQuery` +- `createQueries` +- `createInfiniteQueries` +- `createMutation` +- `useIsFetching` +- `useIsMutating` +- `useQueryClient` +- `QueryClient` +- `QueryClientProvider` + +## Solid Query 與 React Query 的重要差異 + +Solid Query 提供的 API 與 React Query 相似,但有一些關鍵差異需要注意。 + +- 上述列出的 `solid-query` 基本元件(如 `useQuery`、`createMutation`、`useIsFetching`)的參數都是函式,以便在反應式作用域 (reactive scope) 中追蹤。 + +```tsx +// ❌ react 版本 +useQuery({ + queryKey: ['todos', todo], + queryFn: fetchTodos, +}) + +// ✅ solid 版本 +useQuery(() => ({ + queryKey: ['todos', todo], + queryFn: fetchTodos, +})) +``` + +- 如果在 `` 邊界內存取查詢資料,Suspense 會直接適用於查詢。 + +```tsx +import { For, Suspense } from 'solid-js' + +function Example() { + const query = useQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + })) + return ( +
    + {/* ✅ 會觸載入 fallback,資料在 suspense 邊界內存取。 */} + + {(todo) =>
    {todo.title}
    }
    +
    + {/* ❌ 不會觸載入 fallback,資料未在 suspense 邊界內存取。 */} + {(todo) =>
    {todo.title}
    }
    +
    + ) +} +``` + +- Solid Query 的基本元件 (`createX`) 不支援解構。這些函式的回傳值是一個 store,其屬性僅在反應式上下文 (reactive context) 中追蹤。 + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/solid-query' +import { Match, Switch } from 'solid-js' + +const queryClient = new QueryClient() + +export default function App() { + return ( + + + + ) +} + +function Example() { + // ❌ react 版本 — 支援在反應式上下文外解構 + // const { isPending, error, data } = useQuery({ + // queryKey: ['repoData'], + // queryFn: () => + // fetch('https://api.github.com/repos/tannerlinsley/react-query').then( + // (res) => res.json() + // ), + // }) + + // ✅ solid 版本 — 不支援在反應式上下文外解構 + const query = useQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + fetch('https://api.github.com/repos/tannerlinsley/react-query').then( + (res) => res.json(), + ), + })) + + // ✅ 在 JSX 反應式上下文中存取查詢屬性 + return ( + + Loading... + Error: {query.error.message} + +
    +

    {query.data.name}

    +

    {query.data.description}

    + 👀 {query.data.subscribers_count}{' '} + ✨ {query.data.stargazers_count}{' '} + 🍴 {query.data.forks_count} +
    +
    +
    + ) +} +``` + +- 訊號 (Signals) 和 store 值可以直接傳入函式參數。Solid Query 會自動更新查詢 `store`。 + +```tsx +import { + QueryClient, + QueryClientProvider, + useQuery, +} from '@tanstack/solid-query' +import { createSignal, For } from 'solid-js' + +const queryClient = new QueryClient() + +function Example() { + const [enabled, setEnabled] = createSignal(false) + const [todo, setTodo] = createSignal(0) + + // ✅ 直接傳遞訊號是安全的,觀察者會在訊號值變更時自動更新 + const todosQuery = useQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + enabled: enabled(), + })) + + const todoDetailsQuery = useQuery(() => ({ + queryKey: ['todo', todo()], + queryFn: fetchTodo, + enabled: todo() > 0, + })) + + return ( +
    + + +

    Loading...

    +
    + +

    Error: {todosQuery.error.message}

    +
    + + + {(todo) => ( + + )} + + +
    + +
    + ) +} + +function App() { + return ( + + + + ) +} +``` + +- 錯誤可以使用 SolidJS 原生的 `ErrorBoundary` 元件捕捉並重置。將 `throwOnError` 或 `suspense` 選項設為 `true` 以確保錯誤會拋給 `ErrorBoundary`。 + +- 由於屬性追蹤是透過 Solid 的細粒度反應性 (fine grained reactivity) 處理,因此不需要 `notifyOnChangeProps` 這類選項。 diff --git a/docs/zh-hant/framework/solid/reference/hydration.md b/docs/zh-hant/framework/solid/reference/hydration.md new file mode 100644 index 00000000000..948a4628cb7 --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/hydration.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.013Z' +id: hydration +title: hydration +ref: docs/zh-hant/framework/react/reference/hydration.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' +--- + +[//]: # 'HydrationBoundary' +[//]: # 'HydrationBoundary' diff --git a/docs/zh-hant/framework/solid/reference/infiniteQueryOptions.md b/docs/zh-hant/framework/solid/reference/infiniteQueryOptions.md new file mode 100644 index 00000000000..feee1b9aa0f --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/infiniteQueryOptions.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.008Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +ref: docs/zh-hant/framework/react/reference/infiniteQueryOptions.md +--- diff --git a/docs/zh-hant/framework/solid/reference/queryOptions.md b/docs/zh-hant/framework/solid/reference/queryOptions.md new file mode 100644 index 00000000000..a9bc049fc7f --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/queryOptions.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:50.002Z' +id: queryOptions +title: queryOptions +ref: docs/zh-hant/framework/react/reference/queryOptions.md +--- diff --git a/docs/zh-hant/framework/solid/reference/useInfiniteQuery.md b/docs/zh-hant/framework/solid/reference/useInfiniteQuery.md new file mode 100644 index 00000000000..a22e49f42b4 --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useInfiniteQuery.md @@ -0,0 +1,11 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.996Z' +id: useInfiniteQuery +title: useInfiniteQuery +ref: docs/zh-hant/framework/react/reference/useInfiniteQuery.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useInfiniteQuery[(]': 'useInfiniteQuery(() => ' + 'useMutation[(]': 'useMutation(() => ' +--- diff --git a/docs/zh-hant/framework/solid/reference/useIsFetching.md b/docs/zh-hant/framework/solid/reference/useIsFetching.md new file mode 100644 index 00000000000..47806f64302 --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useIsFetching.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.990Z' +id: useIsFetching +title: useIsFetching +ref: docs/zh-hant/framework/react/reference/useIsFetching.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' +--- diff --git a/docs/zh-hant/framework/solid/reference/useIsMutating.md b/docs/zh-hant/framework/solid/reference/useIsMutating.md new file mode 100644 index 00000000000..98f5f413d31 --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useIsMutating.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.984Z' +id: useIsMutating +title: useIsMutating +ref: docs/zh-hant/framework/react/reference/useIsMutating.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' +--- diff --git a/docs/zh-hant/framework/solid/reference/useMutation.md b/docs/zh-hant/framework/solid/reference/useMutation.md new file mode 100644 index 00000000000..738f48f237f --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useMutation.md @@ -0,0 +1,11 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.979Z' +id: useMutation +title: useMutation +ref: docs/zh-hant/framework/react/reference/useMutation.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' +--- diff --git a/docs/zh-hant/framework/solid/reference/useMutationState.md b/docs/zh-hant/framework/solid/reference/useMutationState.md new file mode 100644 index 00000000000..01d3f878a5b --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useMutationState.md @@ -0,0 +1,11 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.973Z' +id: useMutationState +title: useMutationState +ref: docs/zh-hant/framework/react/reference/useMutationState.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useMutationState[(]': 'useMutationState(() => ' + 'useMutation[(]': 'useMutation(() => ' +--- diff --git a/docs/zh-hant/framework/solid/reference/useQueries.md b/docs/zh-hant/framework/solid/reference/useQueries.md new file mode 100644 index 00000000000..47bce79146a --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useQueries.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.967Z' +id: useQueries +title: useQueries +ref: docs/zh-hant/framework/react/reference/useQueries.md +replace: + '@tanstack/react-query': '@tanstack/solid-query' + 'useQueries[(]': 'useQueries(() => ' +--- diff --git a/docs/zh-hant/framework/solid/reference/useQuery.md b/docs/zh-hant/framework/solid/reference/useQuery.md new file mode 100644 index 00000000000..e92709453df --- /dev/null +++ b/docs/zh-hant/framework/solid/reference/useQuery.md @@ -0,0 +1,376 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:18:49.961Z' +id: useQuery +title: useQuery +--- + +```tsx +const { + data, + dataUpdatedAt, + error, + errorUpdatedAt, + failureCount, + failureReason, + fetchStatus, + isError, + isFetched, + isFetchedAfterMount, + isFetching, + isInitialLoading, + isLoading, + isLoadingError, + isPaused, + isPending, + isPlaceholderData, + isRefetchError, + isRefetching, + isStale, + isSuccess, + refetch, + status, +} = useQuery( + () => ({ + queryKey, + queryFn, + enabled, + select, + placeholderData, + deferStream, + reconcile, + gcTime, + networkMode, + initialData, + initialDataUpdatedAt, + meta, + queryKeyHashFn, + refetchInterval, + refetchIntervalInBackground, + refetchOnMount, + refetchOnReconnect, + refetchOnWindowFocus, + retry, + retryOnMount, + retryDelay, + staleTime, + throwOnError, + }), + () => queryClient, +) +``` + +## Usage example + +Here are some examples of how to use the `useQuery` primitive in Solid Query. + +### Basic + +The most basic usage of `useQuery` is to create a query that fetches data from an API. + +```tsx +import { useQuery } from '@tanstack/solid-query' + +function App() { + const todos = useQuery(() => ({ + queryKey: 'todos', + queryFn: async () => { + const response = await fetch('/api/todos') + if (!response.ok) { + throw new Error('Failed to fetch todos') + } + return response.json() + }, + })) + + return ( +
    + +
    Error: {todos.error.message}
    +
    + +
    Loading...
    +
    + +
    +
    Todos:
    +
      + {(todo) =>
    • {todo.title}
    • }
      +
    +
    +
    +
    + ) +} +``` + +### Reactive Options + +The reason why `useQuery` accepts a function that returns an object is to allow for reactive options. This is useful when query options depend on other values/signals that might change over time. Solid Query can track the passed function in a reactive scope and re-run it whenever the dependencies change. + +```tsx +import { useQuery } from '@tanstack/solid-query' + +function App() { + const [filter, setFilter] = createSignal('all') + + const todos = useQuery(() => ({ + queryKey: ['todos', filter()], + queryFn: async () => { + const response = await fetch(`/api/todos?filter=${filter()}`) + if (!response.ok) { + throw new Error('Failed to fetch todos') + } + return response.json() + }, + })) + + return ( +
    +
    + + + +
    + +
    Error: {todos.error.message}
    +
    + +
    Loading...
    +
    + +
    +
    Todos:
    +
      + {(todo) =>
    • {todo.title}
    • }
      +
    +
    +
    +
    + ) +} +``` + +### Usage with `Suspense` + +`useQuery` supports triggering SolidJS `Suspense` and `ErrorBoundary` components when the query is in a pending or error state. This allows you to easily handle loading and error states in your components. + +```tsx +import { useQuery } from '@tanstack/solid-query' + +function App() { + const todos = useQuery(() => ({ + queryKey: 'todos', + queryFn: async () => { + const response = await fetch('/api/todos') + if (!response.ok) { + throw new Error('Failed to fetch todos') + } + return response.json() + }, + throwOnError: true, + })) + + return ( + Error: {todos.error.message}}> + Loading...}> +
    +
    Todos:
    +
      + {(todo) =>
    • {todo.title}
    • }
      +
    +
    +
    +
    + ) +} +``` + +## `useQuery` Parameters + +- ### Query Options - `Accessor` + + - ##### `queryKey: unknown[]` + - **Required** + - The query key to use for this query. + - The query key will be hashed into a stable hash. See [Query Keys](../../guides/query-keys) for more information. + - The query will automatically update when this key changes (as long as `enabled` is not set to `false`). + - ##### `queryFn: (context: QueryFunctionContext) => Promise` + - **Required, but only if no default query function has been defined** See [Default Query Function](../../guides/default-query-function) for more information. + - The function that the query will use to request data. + - Receives a [QueryFunctionContext](../../guides/query-functions#queryfunctioncontext) + - Must return a promise that will either resolve data or throw an error. The data cannot be `undefined`. + - ##### `enabled: boolean` + - Set this to `false` to disable this query from automatically running. + - Can be used for [Dependent Queries](../../guides/dependent-queries). + - ##### `select: (data: TData) => unknown` + - Optional + - This option can be used to transform or select a part of the data returned by the query function. It affects the returned `data` value, but does not affect what gets stored in the query cache. + - The `select` function will only run if `data` changed, or if the reference to the `select` function itself changes. To optimize, wrap the function in `useCallback`. + - ##### `placeholderData: TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData` + - Optional + - If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `pending` state. + - `placeholderData` is **not persisted** to the cache + - If you provide a function for `placeholderData`, as a first argument you will receive previously watched query data if available, and the second argument will be the complete previousQuery instance. + - ##### `deferStream: boolean` + - Optional + - Defaults to `false` + - Only applicable while rendering queries on the server with streaming. + - Set `deferStream` to `true` to wait for the query to resolve on the server before flushing the stream. + - This can be useful to avoid sending a loading state to the client before the query has resolved. + - ##### `reconcile: false | string | ((oldData: TData | undefined, newData: TData) => TData)` + - Optional + - Defaults to `false` + - Set this to a string to enable reconciliation between query results based on the string key. + - Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom reconciliation logic. + - ##### `gcTime: number | Infinity` + - Defaults to `5 * 60 * 1000` (5 minutes) or `Infinity` during SSR + - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different garbage collection times are specified, the longest one will be used. + - Note: the maximum allowed time is about 24 days. See [more](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value). + - If set to `Infinity`, will disable garbage collection + - ##### `networkMode: 'online' | 'always' | 'offlineFirst` + - optional + - defaults to `'online'` + - see [Network Mode](../../guides/network-mode) for more information. + - ##### `initialData: TData | () => TData` + - Optional + - If set, this value will be used as the initial data for the query cache (as long as the query hasn't been created or cached yet) + - If set to a function, the function will be called **once** during the shared/root query initialization, and be expected to synchronously return the initialData + - Initial data is considered stale by default unless a `staleTime` has been set. + - `initialData` **is persisted** to the cache + - ##### `initialDataUpdatedAt: number | (() => number | undefined)` + - Optional + - If set, this value will be used as the time (in milliseconds) of when the `initialData` itself was last updated. + - ##### `meta: Record` + - Optional + - If set, stores additional information on the query cache entry that can be used as needed. It will be accessible wherever the `query` is available, and is also part of the `QueryFunctionContext` provided to the `queryFn`. + - ##### `queryKeyHashFn: (queryKey: QueryKey) => string` + - Optional + - If specified, this function is used to hash the `queryKey` to a string. + - ##### `refetchInterval: number | false | ((query: Query) => number | false | undefined)` + - Optional + - If set to a number, all queries will continuously refetch at this frequency in milliseconds + - If set to a function, the function will be executed with the query to compute a frequency + - ##### `refetchIntervalInBackground: boolean` + - Optional + - If set to `true`, queries that are set to continuously refetch with a `refetchInterval` will continue to refetch while their tab/window is in the background + - ##### `refetchOnMount: boolean | "always" | ((query: Query) => boolean | "always")` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on mount if the data is stale. + - If set to `false`, the query will not refetch on mount. + - If set to `"always"`, the query will always refetch on mount. + - If set to a function, the function will be executed with the query to compute the value + - ##### `refetchOnWindowFocus: boolean | "always" | ((query: Query) => boolean | "always")` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on window focus if the data is stale. + - If set to `false`, the query will not refetch on window focus. + - If set to `"always"`, the query will always refetch on window focus. + - If set to a function, the function will be executed with the query to compute the value + - ##### `refetchOnReconnect: boolean | "always" | ((query: Query) => boolean | "always")` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on reconnect if the data is stale. + - If set to `false`, the query will not refetch on reconnect. + - If set to `"always"`, the query will always refetch on reconnect. + - If set to a function, the function will be executed with the query to compute the value + - ##### `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - If `false`, failed queries will not retry by default. + - If `true`, failed queries will retry infinitely. + - If set to a `number`, e.g. `3`, failed queries will retry until the failed query count meets that number. + - defaults to `3` on the client and `0` on the server + - ##### `retryOnMount: boolean` + - If set to `false`, the query will not be retried on mount if it contains an error. Defaults to `true`. + - ##### `retryDelay: number | (retryAttempt: number, error: TError) => number` + - This function receives a `retryAttempt` integer and the actual Error and returns the delay to apply before the next attempt in milliseconds. + - A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential backoff. + - A function like `attempt => attempt * 1000` applies linear backoff. + - ##### `staleTime: number | Infinity` + - Optional + - Defaults to `0` + - The time in milliseconds after data is considered stale. This value only applies to the hook it is defined on. + - If set to `Infinity`, the data will never be considered stale + - ##### `throwOnError: undefined | boolean | (error: TError, query: Query) => boolean` + - Set this to `true` if you want errors to be thrown in the render phase and propagate to the nearest error boundary + - Set this to `false` to disable `suspense`'s default behavior of throwing errors to the error boundary. + - If set to a function, it will be passed the error and the query, and it should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`) + +- ### Query Client - `Accessor` + - Optional + - Use this to use a custom QueryClient. Otherwise, the one from the nearest context will be used. + +## `useQuery` Return Value - `Store>` + +`useQuery` returns a SolidJS store with the following properties: + +- ##### `status: QueryStatus` + - Will be: + - `pending` if there's no cached data and no query attempt was finished yet. + - `error` if the query attempt resulted in an error. The corresponding `error` property has the error received from the attempted fetch + - `success` if the query has received a response with no errors and is ready to display its data. The corresponding `data` property on the query is the data received from the successful fetch or if the query's `enabled` property is set to `false` and has not been fetched yet `data` is the first `initialData` supplied to the query on initialization. +- ##### `isPending: boolean` + - A derived boolean from the `status` variable above, provided for convenience. +- ##### `isSuccess: boolean` + - A derived boolean from the `status` variable above, provided for convenience. +- ##### `isError: boolean` + - A derived boolean from the `status` variable above, provided for convenience. +- ##### `isLoadingError: boolean` + - Will be `true` if the query failed while fetching for the first time. +- ##### `isRefetchError: boolean` + - Will be `true` if the query failed while refetching. +- ##### `data: Resource` + - Defaults to `undefined`. + - The last successfully resolved data for the query. + - **Important**: The `data` property is a SolidJS resource. This means that if the data is accessed underneath a `` component, + it will trigger the Suspense boundary if the data is not available yet. +- ##### `dataUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` as `"success"`. +- ##### `error: null | TError` + - Defaults to `null` + - The error object for the query, if an error was thrown. +- ##### `errorUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` as `"error"`. +- ##### `isStale: boolean` + - Will be `true` if the data in the cache is invalidated or if the data is older than the given `staleTime`. +- ##### `isPlaceholderData: boolean` + - Will be `true` if the data shown is the placeholder data. +- ##### `isFetched: boolean` + - Will be `true` if the query has been fetched. +- ##### `isFetchedAfterMount: boolean` + - Will be `true` if the query has been fetched after the component mounted. + - This property can be used to not show any previously cached data. +- ##### `fetchStatus: FetchStatus` + - `fetching`: Is `true` whenever the queryFn is executing, which includes initial `pending` as well as background refetches. + - `paused`: The query wanted to fetch, but has been `paused`. + - `idle`: The query is not fetching. + - see [Network Mode](../../guides/network-mode) for more information. +- ##### `isFetching: boolean` + - A derived boolean from the `fetchStatus` variable above, provided for convenience. +- ##### `isPaused: boolean` + - A derived boolean from the `fetchStatus` variable above, provided for convenience. +- ##### `isRefetching: boolean` + - Is `true` whenever a background refetch is in-flight, which _does not_ include initial `pending` + - Is the same as `isFetching && !isPending` +- ##### `isLoading: boolean` + - Is `true` whenever the first fetch for a query is in-flight + - Is the same as `isFetching && isPending` +- ##### `isInitialLoading: boolean` + - **deprecated** + - An alias for `isLoading`, will be removed in the next major version. +- ##### `failureCount: number` + - The failure count for the query. + - Incremented every time the query fails. + - Reset to `0` when the query succeeds. +- ##### `failureReason: null | TError` + - The failure reason for the query retry. + - Reset to `null` when the query succeeds. +- ##### `errorUpdateCount: number` + - The sum of all errors. +- ##### `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise` + - A function to manually refetch the query. + - If the query errors, the error will only be logged. If you want an error to be thrown, pass the `throwOnError: true` option + - `cancelRefetch?: boolean` + - Defaults to `true` + - Per default, a currently running request will be cancelled before a new request is made + - When set to `false`, no refetch will be made if there is already a request running. diff --git a/docs/zh-hant/framework/solid/typescript.md b/docs/zh-hant/framework/solid/typescript.md new file mode 100644 index 00000000000..c28f32688dd --- /dev/null +++ b/docs/zh-hant/framework/solid/typescript.md @@ -0,0 +1,219 @@ +--- +source-updated-at: '2025-04-03T21:54:40.000Z' +translation-updated-at: '2025-05-08T20:17:30.955Z' +id: typescript +title: TypeScript +--- + +Solid Query 使用 **TypeScript** 撰寫,以確保函式庫與您的專案具備型別安全性! + +需注意的事項: + +- 目前型別需使用 TypeScript **v4.7** 或更高版本 +- 此儲存庫中的型別變更視為**非破壞性變更**,通常以 **patch** 版本號變更發布(否則每個型別增強都會成為主要版本!)。 +- **強烈建議將您的 solid-query 套件版本鎖定在特定 patch 版本,並在升級時預期型別可能在任一版本間被修正或升級** +- Solid Query 中與型別無關的公開 API 仍嚴格遵循語意化版本控制。 + +## 型別推論 + +Solid Query 中的型別通常能良好流動,因此您無需自行提供型別註解 + +```tsx +import { useQuery } from '@tanstack/solid-query' + +const query = useQuery(() => ({ + queryKey: ['number'], + queryFn: () => Promise.resolve(5), +})) + +query.data +// ^? (property) data: number | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUPOQR28SYRIBeFOiy4pRABQGAlHA0A+OAYTy4duGuIBpNEQBccANp0WeEACNCOgBdABo4W3tHIgAxFg8TM0sABWoQYDY0ADp0fgEANzQDAFZjeVJjMoU5aKzhLAx5Hh57OAA9AH55brkgA) + +```tsx +import { useQuery } from '@tanstack/solid-query' + +const query = useQuery(() => ({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), +})) + +query.data +// ^? (property) data: string | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUPOQR28SYRIBeFOiy4pRABQGAlHA0A+OAYTy4duGuIBpNEQBccANp1sHOgF0AGjhbe0ciADEWDxMzSwAFahBgNjQAOnR+AQA3NAMAVmNA0LtUgTRkGBjhLAxTCzga5jSYCABlGChgFgBzE2K5UmNjeXlwtKaMeR4eezgAPQB+UYU5IA) + +若您的 `queryFn` 有明確定義的回傳型別,此功能效果最佳。請注意,多數資料獲取函式庫預設回傳 `any`,因此請確保將其提取為具有正確型別的函式: + +```tsx +const fetchGroups = (): Promise => + axios.get('/groups').then((response) => response.data) + +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.data +// ^? (property) data: Group[] | undefined +``` + +[typescript playground](https://www.typescriptlang.org/play/?ssl=11&ssc=4&pln=6&pc=1#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEiw4GAB7AIbStVp01GtrLnyYRMGjgBxanjBwAvIjgiAXHBZ4QAI0Jl585Ah2eAo0GGQAC2sIWy1HAAoASjcABR1gNjQAHmjbAG0AXQA+BxL9TQA6AHMw+LoeKpswQ0SKmAi0Fnj0Nkh2C3sSnr7MiuEsDET-OUDguElCEkdUTGx8Rfik0rh4hHk4A-mpIgBpNCI3PLpGmOa6AoAaOH3DheIAMRY3UPCoprYHvJSIkpsY5G8iGMJvIeDxDnAAHoAfmm8iAA) + +## 型別縮小 (Type Narrowing) + +Solid Query 使用[判別聯合型別 (discriminated union type)](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) 作為查詢結果,以 `status` 欄位和衍生的狀態布林標誌進行判別。這讓您可以檢查例如 `success` 狀態,以確保 `data` 有定義: + +```tsx +const query = useQuery(() => ({ + queryKey: ['number'], + queryFn: () => Promise.resolve(5), +})) + +if (query.isSuccess) { + const data = query.data + // ^? const data: number +} +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEixEKdFjQBRChTTJ45KjXr8hYgFZtZc+cgjt4kwiQC8qzNnxOAFF4CUcZwA+OC8EeTg4R2IAaTQiAC44AG06FjwQACNCOgBdABpwyKkiADEWRL8A4IAFahBgNjQAOnQTADc0LwBWXwK5Ul9feXlgChCooiaGgGU8ZGQ0NjZ-MLkIiNt7OGEsDACipyad5kKInh51iIA9AH55UmHrOSA) + +## 為錯誤欄位指定型別 + +錯誤的型別預設為 `Error`,因為這是多數使用者所預期的。 + +```tsx +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: Error | null +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEiw4GAB7AIbStVp01GtrLnyYRMGjgBxanjBwAvIjgiAXHBZ4QAI0Jl585Ah2eAo0GGQAC2sIWy1HAAoASjcABR1gNjQAHmjbAG0AXQA+BxL9TQA6AHMw+LoeKpswQ0SKmAi0Fnj0Nkh2C3sSnr7MiuEsDET-OUDguElCEkdUTGx8Rfik0rh4hHk4A-mpIgBpNCI3PLpGmOa6AoAaOH3DheIAMRY3UPCoprYHvJSIkpsY5G8iBVCNQoPIeDxDnAAHoAfmm8iAA) + +若您想拋出自訂錯誤,或根本不是 `Error` 的內容,您可以指定錯誤欄位的型別: + +```tsx +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: string | null +``` + +然而,這會導致 `useQuery` 的所有其他泛型型別推論失效。通常不建議拋出非 `Error` 的內容,因此若您有像 `AxiosError` 這樣的子類別,可以使用**型別縮小**使錯誤欄位更具體: + +```tsx +import axios from 'axios' + +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: Error | null + +if (axios.isAxiosError(query.error)) { + query.error + // ^? (property) error: AxiosError +} +``` + +[typescript playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgYygUwIYzQRQK5pQCecAvnAGZQQhwDkAAjBgHYDOzyA1gPRsQAbYABMAtAEcCxOgFgAUKEiw4GAB7AIbStVp01GtrLnyYRMGjgBxanjBwAvIjgiAXHBZ4QAI0Jl585Ah2eAo0GGQAC2sIWy1HAAoASjcABR1gNjQAHmjbAG0AXQA+BxL9TQA6AHMw+LoeKpswQ0SKmAi0Fnj0Nkh2C3sSnr7MiuEsDET-OUDguElCEkdUTGx8Rfik0rh4hHk4A-mpIgBpNCI3PLpGmOa6AoAaOH3DheIAMRY3UPCoprYHvJSIkpsY5G8iBVCNQoPIeDxDnAAHoAfmmwAoO3KbAqGQAgupNABRKAw+IQqGk6AgxAvA4U6HQOlweGI1FA+RAA) + +## 註冊全域 `Error` + +TanStack Query v5 允許透過擴充 `Register` 介面來設定全域錯誤型別,而無需在呼叫端指定泛型。這將確保型別推論仍有效,但錯誤欄位會是指定的型別: + +```tsx +import '@tanstack/solid-query' + +declare module '@tanstack/solid-query' { + interface Register { + defaultError: AxiosError + } +} + +const query = useQuery(() => ({ + queryKey: ['groups'], + queryFn: fetchGroups, +})) + +query.error +// ^? (property) error: AxiosError | null +``` + +## 註冊全域 `Meta` + +類似於註冊[全域錯誤型別](#registering-a-global-error),您也可以註冊全域 `Meta` 型別。這確保了[查詢](../useQuery)和[變異](../createMutation)上的選用 `meta` 欄位保持一致且具備型別安全性。請注意,註冊的型別必須擴展 `Record`,以確保 `meta` 仍為物件。 + +```ts +import '@tanstack/solid-query' + +interface MyMeta extends Record { + // 您的 meta 型別定義。 +} + +declare module '@tanstack/solid-query' { + interface Register { + queryMeta: MyMeta + mutationMeta: MyMeta + } +} +``` + +## 為查詢選項指定型別 + +若您將查詢選項內聯至 `useQuery`,您將獲得自動型別推論。然而,您可能希望將查詢選項提取至獨立的函式,以在 `useQuery` 和例如 `prefetchQuery` 之間共享。此時,您將失去型別推論。要重新獲得它,您可以使用 `queryOptions` 輔助函式: + +```ts +import { queryOptions } from '@tanstack/solid-query' + +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +useQuery(groupOptions) +queryClient.prefetchQuery(groupOptions()) +``` + +此外,`queryOptions` 回傳的 `queryKey` 知道與其關聯的 `queryFn`,我們可以利用此型別資訊讓例如 `queryClient.getQueryData` 等函式也能感知這些型別: + +```ts +function groupOptions() { + return queryOptions({ + queryKey: ['groups'], + queryFn: fetchGroups, + staleTime: 5 * 1000, + }) +} + +const data = queryClient.getQueryData(groupOptions().queryKey) +// ^? const data: Group[] | undefined +``` + +若沒有 `queryOptions`,`data` 的型別會是 `unknown`,除非我們傳遞泛型給它: + +```ts +const data = queryClient.getQueryData(['groups']) +``` + +## 使用 `skipToken` 進行型別安全的查詢停用 + +若您使用 TypeScript,可以使用 `skipToken` 停用查詢。這在您想根據條件停用查詢,但仍希望保持查詢的型別安全時非常有用。 + +更多資訊請參閱[停用查詢](../disabling-queries)指南。 diff --git a/docs/zh-hant/framework/svelte/devtools.md b/docs/zh-hant/framework/svelte/devtools.md new file mode 100644 index 00000000000..5b7ed139551 --- /dev/null +++ b/docs/zh-hant/framework/svelte/devtools.md @@ -0,0 +1,77 @@ +--- +source-updated-at: '2025-03-07T10:54:04.000Z' +translation-updated-at: '2025-05-08T20:15:38.291Z' +id: devtools +title: 開發工具 +--- + +## 安裝並導入開發者工具 (Devtools) + +開發者工具是一個獨立的套件,需要額外安裝: + +```bash +npm i @tanstack/svelte-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/svelte-query-devtools +``` + +或 + +```bash +yarn add @tanstack/svelte-query-devtools +``` + +或 + +```bash +bun add @tanstack/svelte-query-devtools +``` + +導入方式如下: + +```ts +import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools' +``` + +## 浮動模式 (Floating Mode) + +浮動模式會將開發者工具固定為應用程式中的浮動元素,並在畫面角落提供切換按鈕來顯示/隱藏工具。此切換狀態會儲存在 localStorage 中,重新載入後仍會保持記憶。 + +請將以下程式碼盡可能放在 Svelte 應用的最上層,越接近頁面根元素效果越好: + +```ts + + + + {/* 應用程式的其餘部分 */} + + +``` + +### 選項設定 + +- `initialIsOpen: Boolean` + - 設為 `true` 可讓開發工具預設為開啟狀態 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` + - 預設值為 `bottom-right` + - 控制 TanStack 商標按鈕的位置,用於開關開發工具面板 + - 若設為 `relative`,按鈕會出現在你渲染開發工具的位置 +- `position?: "top" | "bottom" | "left" | "right"` + - 預設值為 `bottom` + - 控制 Svelte Query 開發工具面板的位置 +- `client?: QueryClient`, + - 可傳入自訂的 QueryClient,否則會使用最近上下文中的實例 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 用於預定義可在查詢中觸發的錯誤類型。當從 UI 觸發該錯誤時,初始化函式會接收特定查詢並必須回傳一個 Error 物件 +- `styleNonce?: string` + - 用於傳遞 nonce 給新增到文件 head 的 style 標籤。適用於使用內容安全策略 (CSP) nonce 來允許內聯樣式的情況 +- `shadowDOMTarget?: ShadowRoot` + - 預設行為會將開發工具的樣式應用到 DOM 中的 head 標籤 + - 可傳入 shadow DOM 目標,讓樣式改為在 shadow DOM 內應用,而非 light DOM 的 head 標籤內 diff --git a/docs/zh-hant/framework/svelte/installation.md b/docs/zh-hant/framework/svelte/installation.md new file mode 100644 index 00000000000..b21f9920061 --- /dev/null +++ b/docs/zh-hant/framework/svelte/installation.md @@ -0,0 +1,36 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-08T20:15:18.249Z' +id: installation +title: 安裝 +--- + +你可以透過 [NPM](https://npmjs.com) 安裝 Svelte Query。 + +> v5 目前以發佈候選版本 (release-candidate) 提供。我們預期不會再有重大 API 變更。鼓勵你嘗試並回報遇到的任何問題。 + +### NPM + +```bash +npm i @tanstack/svelte-query +``` + +或 + +```bash +pnpm add @tanstack/svelte-query +``` + +或 + +```bash +yarn add @tanstack/svelte-query +``` + +或 + +```bash +bun add @tanstack/svelte-query +``` + +> 想在下載前試用看看嗎?試試這個 [基本範例](../examples/basic)! diff --git a/docs/zh-hant/framework/svelte/overview.md b/docs/zh-hant/framework/svelte/overview.md new file mode 100644 index 00000000000..612953ddfe4 --- /dev/null +++ b/docs/zh-hant/framework/svelte/overview.md @@ -0,0 +1,76 @@ +--- +source-updated-at: '2025-01-23T16:50:04.000Z' +translation-updated-at: '2025-05-08T20:15:25.664Z' +id: overview +title: 概述 +--- + +`@tanstack/svelte-query` 套件提供了第一方 API,讓您能透過 Svelte 使用 TanStack Query。 + +## 範例 + +在專案根目錄附近加入 QueryClientProvider: + +```svelte + + + + + +``` + +接著在任何元件中呼叫任何函式(例如 createQuery): + +```svelte + + +
    + {#if $query.isLoading} +

    Loading...

    + {:else if $query.isError} +

    Error: {$query.error.message}

    + {:else if $query.isSuccess} + {#each $query.data as todo} +

    {todo.title}

    + {/each} + {/if} +
    +``` + +## SvelteKit + +如果您使用 SvelteKit,請參閱 [伺服器渲染 (SSR) 與 SvelteKit](../ssr)。 + +## 可用函式 + +Svelte Query 提供了實用的函式與元件,能簡化 Svelte 應用程式中的伺服器狀態管理。 + +- `createQuery` +- `createQueries` +- `createInfiniteQuery` +- `createMutation` +- `useQueryClient` +- `useIsFetching` +- `useIsMutating` +- `useHydrate` +- `` +- `` + +## Svelte Query 與 React Query 的重要差異 + +Svelte Query 提供的 API 與 React Query 相似,但需注意以下關鍵差異: + +- Svelte Query 中的許多函式會回傳 Svelte 的 store。若要響應式地存取這些 store 中的值,需在 store 名稱前加上 `$`。您可於[此處](https://learn.svelte.dev/tutorial/writable-stores)深入瞭解 Svelte store。 +- 若查詢 (query) 或變異 (mutation) 依賴變數,必須使用 store 作為選項。更多資訊請參閱[反應性 (reactivity) 章節](../reactivity)。 diff --git a/docs/zh-hant/framework/svelte/reactivity.md b/docs/zh-hant/framework/svelte/reactivity.md new file mode 100644 index 00000000000..23586e6e4f6 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reactivity.md @@ -0,0 +1,51 @@ +--- +source-updated-at: '2024-08-19T12:20:53.000Z' +translation-updated-at: '2025-05-08T20:15:22.292Z' +id: reactivity +title: 響應式 +--- + +Svelte 使用編譯器來構建你的程式碼,以優化渲染效能。預設情況下,元件只會執行一次,除非它們在標記中被引用。為了能夠對選項的變更做出反應,你需要使用[儲存 (stores)](https://svelte.dev/docs/svelte-store)。 + +在以下範例中,`refetchInterval` 選項是從變數 `intervalMs` 設定的,而 `intervalMs` 與輸入欄位綁定。然而,由於查詢無法對 `intervalMs` 的變更做出反應,當輸入值變更時,`refetchInterval` 並不會隨之改變。 + +```svelte + + + +``` + +為了解決這個問題,我們可以將 `intervalMs` 轉換為一個可寫儲存 (writable store)。然後,查詢選項可以被轉換為一個衍生儲存 (derived store),這樣就能以真正的反應性 (reactivity) 傳遞給函式。 + +```svelte + + + +``` diff --git a/docs/zh-hant/framework/svelte/reference/classes/hydrationboundary.md b/docs/zh-hant/framework/svelte/reference/classes/hydrationboundary.md new file mode 100644 index 00000000000..f6dec7945c4 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/classes/hydrationboundary.md @@ -0,0 +1,276 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.298Z' +id: HydrationBoundary +title: HydrationBoundary +--- + +# Class: HydrationBoundary\ + +Base class for Svelte components with some minor dev-enhancements. Used when dev=true. + +Can be used to create strongly typed Svelte components. + +#### Example: + +You have component library on npm called `component-library`, from which +you export a component called `MyComponent`. For Svelte+TypeScript users, +you want to provide typings. Therefore you create a `index.d.ts`: + +```ts +import { SvelteComponent } from 'svelte' +export class MyComponent extends SvelteComponent<{ foo: string }> {} +``` + +Typing this makes it possible for IDEs like VS Code with the Svelte extension +to provide intellisense and to use the component like this in a Svelte file +with TypeScript: + +```svelte + + + +``` + +## Extends + +- `SvelteComponent_1`\<`Props`, `Events`\> + +## Type Parameters + +• **Props** _extends_ `Record`\<`string`, `any`\> = `any` + +• **Events** _extends_ `Record`\<`string`, `any`\> = `any` + +• **Slots** _extends_ `Record`\<`string`, `any`\> = `any` + +## Indexable + +\[`prop`: `string`\]: `any` + +## Constructors + +### new HydrationBoundary() + +```ts +new HydrationBoundary(options): HydrationBoundary +``` + +#### Parameters + +• **options**: `ComponentConstructorOptions`\<`Props`\> + +#### Returns + +[`HydrationBoundary`](hydrationboundary.md)\<`Props`, `Events`, `Slots`\> + +#### Overrides + +`SvelteComponent_1.constructor` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:144 + +## Properties + +### $$ + +```ts +$$: any +``` + +### PRIVATE API + +Do not use, may change at any time + +#### Inherited from + +`SvelteComponent_1.$$` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:102 + +--- + +### $$events_def + +```ts +$$events_def: Events +``` + +For type checking capabilities only. +Does not exist at runtime. + +### DO NOT USE! + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:158 + +--- + +### $$prop_def + +```ts +$$prop_def: Props +``` + +For type checking capabilities only. +Does not exist at runtime. + +### DO NOT USE! + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:151 + +--- + +### $$set + +```ts +$$set: any +``` + +### PRIVATE API + +Do not use, may change at any time + +#### Inherited from + +`SvelteComponent_1.$$set` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:109 + +--- + +### $$slot_def + +```ts +$$slot_def: Slots +``` + +For type checking capabilities only. +Does not exist at runtime. + +### DO NOT USE! + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:165 + +## Methods + +### $capture_state() + +```ts +$capture_state(): void +``` + +#### Returns + +`void` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:167 + +--- + +### $destroy() + +```ts +$destroy(): void +``` + +#### Returns + +`void` + +#### Inherited from + +`SvelteComponent_1.$destroy` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:111 + +--- + +### $inject_state() + +```ts +$inject_state(): void +``` + +#### Returns + +`void` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:169 + +--- + +### $on() + +```ts +$on(type, callback): () => void +``` + +#### Type Parameters + +• **K** _extends_ `string` + +#### Parameters + +• **type**: `K` + +• **callback**: `undefined` \| `null` \| (`e`) => `void` + +#### Returns + +`Function` + +##### Returns + +`void` + +#### Inherited from + +`SvelteComponent_1.$on` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:113 + +--- + +### $set() + +```ts +$set(props): void +``` + +#### Parameters + +• **props**: `Partial`\<`Props`\> + +#### Returns + +`void` + +#### Inherited from + +`SvelteComponent_1.$set` + +#### Defined in + +node_modules/.pnpm/svelte@4.2.18/node_modules/svelte/types/index.d.ts:115 diff --git a/docs/zh-hant/framework/svelte/reference/functions/createinfinitequery.md b/docs/zh-hant/framework/svelte/reference/functions/createinfinitequery.md new file mode 100644 index 00000000000..4525dd4a010 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/createinfinitequery.md @@ -0,0 +1,44 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.496Z' +id: createInfiniteQuery +title: createInfiniteQuery +--- + +# Function: createInfiniteQuery() + +```ts +function createInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>(options, queryClient?): CreateInfiniteQueryResult +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`CreateInfiniteQueryOptions`](../type-aliases/createinfinitequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`, `TPageParam`\>\> + +• **queryClient?**: `QueryClient` + +## Returns + +[`CreateInfiniteQueryResult`](../type-aliases/createinfinitequeryresult.md)\<`TData`, `TError`\> + +## Defined in + +[packages/svelte-query/src/createInfiniteQuery.ts:16](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createInfiniteQuery.ts#L16) diff --git a/docs/zh-hant/framework/svelte/reference/functions/createmutation.md b/docs/zh-hant/framework/svelte/reference/functions/createmutation.md new file mode 100644 index 00000000000..b098b5b3f2d --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/createmutation.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.484Z' +id: createMutation +title: createMutation +--- + +# Function: createMutation() + +```ts +function createMutation( + options, + queryClient?, +): CreateMutationResult +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `Error` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`CreateMutationOptions`](../type-aliases/createmutationoptions.md)\<`TData`, `TError`, `TVariables`, `TContext`\>\> + +• **queryClient?**: `QueryClient` + +## Returns + +[`CreateMutationResult`](../type-aliases/createmutationresult.md)\<`TData`, `TError`, `TVariables`, `TContext`\> + +## Defined in + +[packages/svelte-query/src/createMutation.ts:13](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createMutation.ts#L13) diff --git a/docs/zh-hant/framework/svelte/reference/functions/createqueries.md b/docs/zh-hant/framework/svelte/reference/functions/createqueries.md new file mode 100644 index 00000000000..b48c7fed826 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/createqueries.md @@ -0,0 +1,39 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.472Z' +id: createQueries +title: createQueries +--- + +# Function: createQueries() + +```ts +function createQueries( + __namedParameters, + queryClient?, +): Readable +``` + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TCombinedResult** = `T` _extends_ [] ? [] : `T` _extends_ [`Head`] ? [`GetCreateQueryResult`\<`Head`\>] : `T` _extends_ [`Head`, `...Tails[]`] ? [`...Tails[]`] _extends_ [] ? [] : [`...Tails[]`] _extends_ [`Head`] ? [`GetCreateQueryResult`\<`Head`\>, `GetCreateQueryResult`\<`Head`\>] : [`...Tails[]`] _extends_ [`Head`, `...Tails[]`] ? [`...Tails[]`] _extends_ [] ? [] : [`...Tails[]`] _extends_ [`Head`] ? [`GetCreateQueryResult`\<`Head`\>, `GetCreateQueryResult`\<`Head`\>, `GetCreateQueryResult`\<`Head`\>] : [`...Tails[]`] _extends_ [`Head`, `...Tails[]`] ? [`...(...)[]`] _extends_ [] ? [] : ... _extends_ ... ? ... : ... : [`...(...)[]`] _extends_ ...[] ? ...[] : ...[] : [`...Tails[]`] _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] : `T` _extends_ `QueryObserverOptionsForCreateQueries`\<`TQueryFnData`, `TError`, `TData`, `any`\>[] ? `QueryObserverResult`\<`unknown` _extends_ `TData` ? `TQueryFnData` : `TData`, `unknown` _extends_ `TError` ? `Error` : `TError`\>[] : `QueryObserverResult`[] + +## Parameters + +• **\_\_namedParameters** + +• **\_\_namedParameters.combine?** + +• **\_\_namedParameters.queries?**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`...(T extends [] ? [] : T extends [Head] ? [GetQueryObserverOptionsForCreateQueries] : T extends [Head, ...Tails[]] ? [...Tails[]] extends [] ? [] : [...Tails[]] extends [Head] ? [GetQueryObserverOptionsForCreateQueries, GetQueryObserverOptionsForCreateQueries] : [...Tails[]] extends [Head, ...Tails[]] ? [...(...)[]] extends [] ? [] : (...) extends (...) ? (...) : (...) : readonly (...)[] extends [...(...)[]] ? [...(...)[]] : (...) extends (...) ? (...) : (...) : readonly unknown[] extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[])[]`]\> + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`TCombinedResult`\> + +## Defined in + +[packages/svelte-query/src/createQueries.ts:205](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQueries.ts#L205) diff --git a/docs/zh-hant/framework/svelte/reference/functions/createquery.md b/docs/zh-hant/framework/svelte/reference/functions/createquery.md new file mode 100644 index 00000000000..a18aa17ba41 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/createquery.md @@ -0,0 +1,107 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.459Z' +id: createQuery +title: createQuery +--- + +# Function: createQuery() + +## createQuery(options, queryClient) + +```ts +function createQuery( + options, + queryClient?, +): DefinedCreateQueryResult +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>\> + +• **queryClient?**: `QueryClient` + +### Returns + +[`DefinedCreateQueryResult`](../type-aliases/definedcreatequeryresult.md)\<`TData`, `TError`\> + +### Defined in + +[packages/svelte-query/src/createQuery.ts:15](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQuery.ts#L15) + +## createQuery(options, queryClient) + +```ts +function createQuery( + options, + queryClient?, +): CreateQueryResult +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>\> + +• **queryClient?**: `QueryClient` + +### Returns + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +### Defined in + +[packages/svelte-query/src/createQuery.ts:27](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQuery.ts#L27) + +## createQuery(options, queryClient) + +```ts +function createQuery( + options, + queryClient?, +): CreateQueryResult +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`StoreOrVal`](../type-aliases/storeorval.md)\<[`CreateQueryOptions`](../type-aliases/createqueryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>\> + +• **queryClient?**: `QueryClient` + +### Returns + +[`CreateQueryResult`](../type-aliases/createqueryresult.md)\<`TData`, `TError`\> + +### Defined in + +[packages/svelte-query/src/createQuery.ts:39](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQuery.ts#L39) diff --git a/docs/zh-hant/framework/svelte/reference/functions/getisrestoringcontext.md b/docs/zh-hant/framework/svelte/reference/functions/getisrestoringcontext.md new file mode 100644 index 00000000000..1a63c9b0d78 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/getisrestoringcontext.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.447Z' +id: getIsRestoringContext +title: getIsRestoringContext +--- + +# Function: getIsRestoringContext() + +```ts +function getIsRestoringContext(): Readable +``` + +Retrieves a `isRestoring` from Svelte's context + +## Returns + +`Readable`\<`boolean`\> + +## Defined in + +[packages/svelte-query/src/context.ts:28](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L28) diff --git a/docs/zh-hant/framework/svelte/reference/functions/getqueryclientcontext.md b/docs/zh-hant/framework/svelte/reference/functions/getqueryclientcontext.md new file mode 100644 index 00000000000..3b97307c291 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/getqueryclientcontext.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.435Z' +id: getQueryClientContext +title: getQueryClientContext +--- + +# Function: getQueryClientContext() + +```ts +function getQueryClientContext(): QueryClient +``` + +Retrieves a Client from Svelte's context + +## Returns + +`QueryClient` + +## Defined in + +[packages/svelte-query/src/context.ts:9](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L9) diff --git a/docs/zh-hant/framework/svelte/reference/functions/infinitequeryoptions.md b/docs/zh-hant/framework/svelte/reference/functions/infinitequeryoptions.md new file mode 100644 index 00000000000..dbc2e1bc184 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/infinitequeryoptions.md @@ -0,0 +1,51 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.422Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +--- + +# Function: infiniteQueryOptions() + +```ts +function infiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam, +>( + options, +): CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam +> +``` + +## Type Parameters + +• **TQueryFnData** + +• **TError** = `Error` + +• **TData** = `InfiniteData`\<`TQueryFnData`, `unknown`\> + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Parameters + +• **options**: [`CreateInfiniteQueryOptions`](../type-aliases/createinfinitequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`, `TPageParam`\> + +## Returns + +[`CreateInfiniteQueryOptions`](../type-aliases/createinfinitequeryoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`, `TPageParam`\> + +## Defined in + +[packages/svelte-query/src/infiniteQueryOptions.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/infiniteQueryOptions.ts#L4) diff --git a/docs/zh-hant/framework/svelte/reference/functions/queryoptions.md b/docs/zh-hant/framework/svelte/reference/functions/queryoptions.md new file mode 100644 index 00000000000..aa32d6f8841 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/queryoptions.md @@ -0,0 +1,68 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.410Z' +id: queryOptions +title: queryOptions +--- + +# Function: queryOptions() + +## queryOptions(options) + +```ts +function queryOptions( + options, +): DefinedInitialDataOptions & object +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +### Returns + +[`DefinedInitialDataOptions`](../type-aliases/definedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +### Defined in + +[packages/svelte-query/src/queryOptions.ts:26](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L26) + +## queryOptions(options) + +```ts +function queryOptions( + options, +): UndefinedInitialDataOptions & object +``` + +### Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `Error` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +### Parameters + +• **options**: [`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> + +### Returns + +[`UndefinedInitialDataOptions`](../type-aliases/undefinedinitialdataoptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` + +### Defined in + +[packages/svelte-query/src/queryOptions.ts:37](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L37) diff --git a/docs/zh-hant/framework/svelte/reference/functions/setisrestoringcontext.md b/docs/zh-hant/framework/svelte/reference/functions/setisrestoringcontext.md new file mode 100644 index 00000000000..271fc0f4797 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/setisrestoringcontext.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.397Z' +id: setIsRestoringContext +title: setIsRestoringContext +--- + +# Function: setIsRestoringContext() + +```ts +function setIsRestoringContext(isRestoring): void +``` + +Sets a `isRestoring` on Svelte's context + +## Parameters + +• **isRestoring**: `Readable`\<`boolean`\> + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/context.ts:40](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L40) diff --git a/docs/zh-hant/framework/svelte/reference/functions/setqueryclientcontext.md b/docs/zh-hant/framework/svelte/reference/functions/setqueryclientcontext.md new file mode 100644 index 00000000000..4b694705be0 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/setqueryclientcontext.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.385Z' +id: setQueryClientContext +title: setQueryClientContext +--- + +# Function: setQueryClientContext() + +```ts +function setQueryClientContext(client): void +``` + +Sets a QueryClient on Svelte's context + +## Parameters + +• **client**: `QueryClient` + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/context.ts:21](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/context.ts#L21) diff --git a/docs/zh-hant/framework/svelte/reference/functions/usehydrate.md b/docs/zh-hant/framework/svelte/reference/functions/usehydrate.md new file mode 100644 index 00000000000..ee8244b7169 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/usehydrate.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.373Z' +id: useHydrate +title: useHydrate +--- + +# Function: useHydrate() + +```ts +function useHydrate(state?, options?, queryClient?): void +``` + +## Parameters + +• **state?**: `unknown` + +• **options?**: `HydrateOptions` + +• **queryClient?**: `QueryClient` + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/useHydrate.ts:8](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useHydrate.ts#L8) diff --git a/docs/zh-hant/framework/svelte/reference/functions/useisfetching.md b/docs/zh-hant/framework/svelte/reference/functions/useisfetching.md new file mode 100644 index 00000000000..06c9aaf9dbd --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/useisfetching.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.361Z' +id: useIsFetching +title: useIsFetching +--- + +# Function: useIsFetching() + +```ts +function useIsFetching(filters?, queryClient?): Readable +``` + +## Parameters + +• **filters?**: `QueryFilters` + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`number`\> + +## Defined in + +[packages/svelte-query/src/useIsFetching.ts:10](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useIsFetching.ts#L10) diff --git a/docs/zh-hant/framework/svelte/reference/functions/useismutating.md b/docs/zh-hant/framework/svelte/reference/functions/useismutating.md new file mode 100644 index 00000000000..841bc02c530 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/useismutating.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.348Z' +id: useIsMutating +title: useIsMutating +--- + +# Function: useIsMutating() + +```ts +function useIsMutating(filters?, queryClient?): Readable +``` + +## Parameters + +• **filters?**: `MutationFilters` + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`number`\> + +## Defined in + +[packages/svelte-query/src/useIsMutating.ts:10](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useIsMutating.ts#L10) diff --git a/docs/zh-hant/framework/svelte/reference/functions/useisrestoring.md b/docs/zh-hant/framework/svelte/reference/functions/useisrestoring.md new file mode 100644 index 00000000000..32267ef1d2e --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/useisrestoring.md @@ -0,0 +1,20 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.336Z' +id: useIsRestoring +title: useIsRestoring +--- + +# Function: useIsRestoring() + +```ts +function useIsRestoring(): Readable +``` + +## Returns + +`Readable`\<`boolean`\> + +## Defined in + +[packages/svelte-query/src/useIsRestoring.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useIsRestoring.ts#L4) diff --git a/docs/zh-hant/framework/svelte/reference/functions/usemutationstate.md b/docs/zh-hant/framework/svelte/reference/functions/usemutationstate.md new file mode 100644 index 00000000000..d6f3d2cee73 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/usemutationstate.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.324Z' +id: useMutationState +title: useMutationState +--- + +# Function: useMutationState() + +```ts +function useMutationState(options, queryClient?): Readable +``` + +## Type Parameters + +• **TResult** = `MutationState`\<`unknown`, `Error`, `unknown`, `unknown`\> + +## Parameters + +• **options**: [`MutationStateOptions`](../type-aliases/mutationstateoptions.md)\<`TResult`\> = `{}` + +• **queryClient?**: `QueryClient` + +## Returns + +`Readable`\<`TResult`[]\> + +## Defined in + +[packages/svelte-query/src/useMutationState.ts:24](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useMutationState.ts#L24) diff --git a/docs/zh-hant/framework/svelte/reference/functions/usequeryclient.md b/docs/zh-hant/framework/svelte/reference/functions/usequeryclient.md new file mode 100644 index 00000000000..f124532b8e8 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/functions/usequeryclient.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.311Z' +id: useQueryClient +title: useQueryClient +--- + +# Function: useQueryClient() + +```ts +function useQueryClient(queryClient?): QueryClient +``` + +## Parameters + +• **queryClient?**: `QueryClient` + +## Returns + +`QueryClient` + +## Defined in + +[packages/svelte-query/src/useQueryClient.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/useQueryClient.ts#L4) diff --git a/docs/zh-hant/framework/svelte/reference/index.md b/docs/zh-hant/framework/svelte/reference/index.md new file mode 100644 index 00000000000..b9c48150bd5 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/index.md @@ -0,0 +1,59 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:17:10.159Z' +id: '@tanstack/svelte-query' +title: '@tanstack/svelte-query' +--- + +# @tanstack/svelte-query + +## References + +### QueryClientProvider + +Renames and re-exports [HydrationBoundary](classes/hydrationboundary.md) + +## Classes + +- [HydrationBoundary](classes/hydrationboundary.md) + +## Type Aliases + +- [CreateBaseMutationResult](type-aliases/createbasemutationresult.md) +- [CreateBaseQueryOptions](type-aliases/createbasequeryoptions.md) +- [CreateBaseQueryResult](type-aliases/createbasequeryresult.md) +- [CreateInfiniteQueryOptions](type-aliases/createinfinitequeryoptions.md) +- [CreateInfiniteQueryResult](type-aliases/createinfinitequeryresult.md) +- [CreateMutateAsyncFunction](type-aliases/createmutateasyncfunction.md) +- [CreateMutateFunction](type-aliases/createmutatefunction.md) +- [CreateMutationOptions](type-aliases/createmutationoptions.md) +- [CreateMutationResult](type-aliases/createmutationresult.md) +- [CreateQueryOptions](type-aliases/createqueryoptions.md) +- [CreateQueryResult](type-aliases/createqueryresult.md) +- [DefinedCreateBaseQueryResult](type-aliases/definedcreatebasequeryresult.md) +- [DefinedCreateQueryResult](type-aliases/definedcreatequeryresult.md) +- [DefinedInitialDataOptions](type-aliases/definedinitialdataoptions.md) +- [MutationStateOptions](type-aliases/mutationstateoptions.md) +- [QueriesOptions](type-aliases/queriesoptions.md) +- [QueriesResults](type-aliases/queriesresults.md) +- [StoreOrVal](type-aliases/storeorval.md) +- [UndefinedInitialDataOptions](type-aliases/undefinedinitialdataoptions.md) + +## Functions + +- [createInfiniteQuery](functions/createinfinitequery.md) +- [createMutation](functions/createmutation.md) +- [createQueries](functions/createqueries.md) +- [createQuery](functions/createquery.md) +- [getIsRestoringContext](functions/getisrestoringcontext.md) +- [getQueryClientContext](functions/getqueryclientcontext.md) +- [infiniteQueryOptions](functions/infinitequeryoptions.md) +- [queryOptions](functions/queryoptions.md) +- [setIsRestoringContext](functions/setisrestoringcontext.md) +- [setQueryClientContext](functions/setqueryclientcontext.md) +- [useHydrate](functions/usehydrate.md) +- [useIsFetching](functions/useisfetching.md) +- [useIsMutating](functions/useismutating.md) +- [useIsRestoring](functions/useisrestoring.md) +- [useMutationState](functions/usemutationstate.md) +- [useQueryClient](functions/usequeryclient.md) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createbasemutationresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createbasemutationresult.md new file mode 100644 index 00000000000..b473a5229c5 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createbasemutationresult.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.285Z' +id: CreateBaseMutationResult +title: CreateBaseMutationResult +--- + +# Type Alias: CreateBaseMutationResult\ + +```ts +type CreateBaseMutationResult: Override, object> & object; +``` + +## Type declaration + +### mutateAsync + +```ts +mutateAsync: CreateMutateAsyncFunction +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:113](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L113) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryoptions.md new file mode 100644 index 00000000000..e0087e11e52 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryoptions.md @@ -0,0 +1,30 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.272Z' +id: CreateBaseQueryOptions +title: CreateBaseQueryOptions +--- + +# Type Alias: CreateBaseQueryOptions\ + +```ts +type CreateBaseQueryOptions: QueryObserverOptions; +``` + +Options for createBaseQuery + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/types.ts:23](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L23) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryresult.md new file mode 100644 index 00000000000..c133cb940f8 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createbasequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.259Z' +id: CreateBaseQueryResult +title: CreateBaseQueryResult +--- + +# Type Alias: CreateBaseQueryResult\ + +```ts +type CreateBaseQueryResult: Readable>; +``` + +Result from createBaseQuery + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:32](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L32) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md new file mode 100644 index 00000000000..a67480a424b --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryoptions.md @@ -0,0 +1,32 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.246Z' +id: CreateInfiniteQueryOptions +title: CreateInfiniteQueryOptions +--- + +# Type Alias: CreateInfiniteQueryOptions\ + +```ts +type CreateInfiniteQueryOptions: InfiniteQueryObserverOptions; +``` + +Options for createInfiniteQuery + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +• **TPageParam** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:52](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L52) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryresult.md new file mode 100644 index 00000000000..af06ad385ea --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createinfinitequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.234Z' +id: CreateInfiniteQueryResult +title: CreateInfiniteQueryResult +--- + +# Type Alias: CreateInfiniteQueryResult\ + +```ts +type CreateInfiniteQueryResult: Readable>; +``` + +Result from createInfiniteQuery + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:69](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L69) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createmutateasyncfunction.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutateasyncfunction.md new file mode 100644 index 00000000000..e7d0f4463c9 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutateasyncfunction.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.221Z' +id: CreateMutateAsyncFunction +title: CreateMutateAsyncFunction +--- + +# Type Alias: CreateMutateAsyncFunction\ + +```ts +type CreateMutateAsyncFunction: MutateFunction; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:106](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L106) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createmutatefunction.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutatefunction.md new file mode 100644 index 00000000000..2daff322acf --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutatefunction.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.208Z' +id: CreateMutateFunction +title: CreateMutateFunction +--- + +# Type Alias: CreateMutateFunction()\ + +```ts +type CreateMutateFunction: (...args) => void; +``` + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Parameters + +• ...**args**: `Parameters`\<`MutateFunction`\<`TData`, `TError`, `TVariables`, `TContext`\>\> + +## Returns + +`void` + +## Defined in + +[packages/svelte-query/src/types.ts:97](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L97) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createmutationoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutationoptions.md new file mode 100644 index 00000000000..6fa18125c3c --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutationoptions.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.195Z' +id: CreateMutationOptions +title: CreateMutationOptions +--- + +# Type Alias: CreateMutationOptions\ + +```ts +type CreateMutationOptions: OmitKeyof, "_defaulted">; +``` + +Options for createMutation + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `void` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:87](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L87) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createmutationresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutationresult.md new file mode 100644 index 00000000000..8d07b5223e9 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createmutationresult.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.183Z' +id: CreateMutationResult +title: CreateMutationResult +--- + +# Type Alias: CreateMutationResult\ + +```ts +type CreateMutationResult: Readable>; +``` + +Result from createMutation + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +• **TVariables** = `unknown` + +• **TContext** = `unknown` + +## Defined in + +[packages/svelte-query/src/types.ts:126](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L126) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createqueryoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createqueryoptions.md new file mode 100644 index 00000000000..a98496b4148 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createqueryoptions.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.171Z' +id: CreateQueryOptions +title: CreateQueryOptions +--- + +# Type Alias: CreateQueryOptions\ + +```ts +type CreateQueryOptions: CreateBaseQueryOptions; +``` + +Options for createQuery + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/types.ts:38](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L38) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/createqueryresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/createqueryresult.md new file mode 100644 index 00000000000..73ed221d424 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/createqueryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.158Z' +id: CreateQueryResult +title: CreateQueryResult +--- + +# Type Alias: CreateQueryResult\ + +```ts +type CreateQueryResult: CreateBaseQueryResult; +``` + +Result from createQuery + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:46](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L46) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md new file mode 100644 index 00000000000..0e92bb6dd7d --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatebasequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.146Z' +id: DefinedCreateBaseQueryResult +title: DefinedCreateBaseQueryResult +--- + +# Type Alias: DefinedCreateBaseQueryResult\ + +```ts +type DefinedCreateBaseQueryResult: Readable>; +``` + +Options for createBaseQuery with initialData + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:75](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L75) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatequeryresult.md b/docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatequeryresult.md new file mode 100644 index 00000000000..8053e1f713e --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/definedcreatequeryresult.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.134Z' +id: DefinedCreateQueryResult +title: DefinedCreateQueryResult +--- + +# Type Alias: DefinedCreateQueryResult\ + +```ts +type DefinedCreateQueryResult: DefinedCreateBaseQueryResult; +``` + +Options for createQuery with initialData + +## Type Parameters + +• **TData** = `unknown` + +• **TError** = `DefaultError` + +## Defined in + +[packages/svelte-query/src/types.ts:81](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L81) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/definedinitialdataoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/definedinitialdataoptions.md new file mode 100644 index 00000000000..70f55d41c21 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/definedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.122Z' +id: DefinedInitialDataOptions +title: DefinedInitialDataOptions +--- + +# Type Alias: DefinedInitialDataOptions\ + +```ts +type DefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData + +```ts +initialData: NonUndefinedGuard | () => NonUndefinedGuard; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/queryOptions.ts:15](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L15) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/mutationstateoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/mutationstateoptions.md new file mode 100644 index 00000000000..77e111a4b78 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/mutationstateoptions.md @@ -0,0 +1,44 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.109Z' +id: MutationStateOptions +title: MutationStateOptions +--- + +# Type Alias: MutationStateOptions\ + +```ts +type MutationStateOptions: object; +``` + +Options for useMutationState + +## Type Parameters + +• **TResult** = `MutationState` + +## Type declaration + +### filters? + +```ts +optional filters: MutationFilters; +``` + +### select()? + +```ts +optional select: (mutation) => TResult; +``` + +#### Parameters + +• **mutation**: `Mutation`\<`unknown`, `DefaultError`, `unknown`, `unknown`\> + +#### Returns + +`TResult` + +## Defined in + +[packages/svelte-query/src/types.ts:140](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L140) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/queriesoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/queriesoptions.md new file mode 100644 index 00000000000..b85fbc290ae --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/queriesoptions.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.097Z' +id: QueriesOptions +title: QueriesOptions +--- + +# Type Alias: QueriesOptions\ + +```ts +type QueriesOptions: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetQueryObserverOptionsForCreateQueries] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions<[...Tails], [...TResults, GetQueryObserverOptionsForCreateQueries], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; +``` + +QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResults** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[packages/svelte-query/src/createQueries.ts:129](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQueries.ts#L129) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/queriesresults.md b/docs/zh-hant/framework/svelte/reference/type-aliases/queriesresults.md new file mode 100644 index 00000000000..2e88f1f1b22 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/queriesresults.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.084Z' +id: QueriesResults +title: QueriesResults +--- + +# Type Alias: QueriesResults\ + +```ts +type QueriesResults: TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResult] : T extends [infer Head, ...(infer Tails)] ? QueriesResults<[...Tails], [...TResults, GetCreateQueryResult], [...TDepth, 1]> : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverResult[] : QueryObserverResult[]; +``` + +QueriesResults reducer recursively maps type param to results + +## Type Parameters + +• **T** _extends_ `any`[] + +• **TResults** _extends_ `any`[] = [] + +• **TDepth** _extends_ `ReadonlyArray`\<`number`\> = [] + +## Defined in + +[packages/svelte-query/src/createQueries.ts:171](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/createQueries.ts#L171) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/storeorval.md b/docs/zh-hant/framework/svelte/reference/type-aliases/storeorval.md new file mode 100644 index 00000000000..6d6c628c336 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/storeorval.md @@ -0,0 +1,22 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.071Z' +id: StoreOrVal +title: StoreOrVal +--- + +# Type Alias: StoreOrVal\ + +```ts +type StoreOrVal: T | Readable; +``` + +Allows a type to be either the base object or a store of that object + +## Type Parameters + +• **T** + +## Defined in + +[packages/svelte-query/src/types.ts:20](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/types.ts#L20) diff --git a/docs/zh-hant/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md b/docs/zh-hant/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md new file mode 100644 index 00000000000..3c8f47c85b5 --- /dev/null +++ b/docs/zh-hant/framework/svelte/reference/type-aliases/undefinedinitialdataoptions.md @@ -0,0 +1,34 @@ +--- +source-updated-at: '2024-07-27T03:03:44.000Z' +translation-updated-at: '2025-05-08T20:25:52.058Z' +id: UndefinedInitialDataOptions +title: UndefinedInitialDataOptions +--- + +# Type Alias: UndefinedInitialDataOptions\ + +```ts +type UndefinedInitialDataOptions: CreateQueryOptions & object; +``` + +## Type declaration + +### initialData? + +```ts +optional initialData: undefined; +``` + +## Type Parameters + +• **TQueryFnData** = `unknown` + +• **TError** = `DefaultError` + +• **TData** = `TQueryFnData` + +• **TQueryKey** _extends_ `QueryKey` = `QueryKey` + +## Defined in + +[packages/svelte-query/src/queryOptions.ts:4](https://github.com/TanStack/query/blob/dac5da5416b82b0be38a8fb34dde1fc6670f0a59/packages/svelte-query/src/queryOptions.ts#L4) diff --git a/docs/zh-hant/framework/svelte/ssr.md b/docs/zh-hant/framework/svelte/ssr.md new file mode 100644 index 00000000000..e52e0852b69 --- /dev/null +++ b/docs/zh-hant/framework/svelte/ssr.md @@ -0,0 +1,156 @@ +--- +source-updated-at: '2024-04-02T22:46:31.000Z' +translation-updated-at: '2025-05-08T20:15:48.998Z' +id: overview +title: SSR 與 SvelteKit +--- + +## 設定 + +SvelteKit 預設會使用伺服器渲染 (SSR) 來渲染路由。因此,你需要在伺服器端停用查詢 (query)。否則,你的查詢會繼續在伺服器端非同步執行,即使在 HTML 已經傳送到客戶端之後也是如此。 + +推薦的做法是在 `QueryClient` 物件中使用 SvelteKit 的 `browser` 模組。這不會停用 `queryClient.prefetchQuery()`,該方法會在以下其中一種解決方案中使用。 + +**src/routes/+layout.svelte** + +```svelte + + + + + +``` + +## 預先取得資料 + +Svelte Query 支援兩種在伺服器端預先取得資料並透過 SvelteKit 傳遞給客戶端的方式。 + +如果你想查看理想的 SSR 設定,請參考 [SSR 範例](../examples/ssr)。 + +### 使用 `initialData` + +結合 SvelteKit 的 [`load`](https://kit.svelte.dev/docs/load),你可以將伺服器端載入的資料傳遞給 `createQuery` 的 `initialData` 選項: + +**src/routes/+page.ts** + +```ts +export async function load() { + const posts = await getPosts() + return { posts } +} +``` + +**src/routes/+page.svelte** + +```svelte + +``` + +優點: + +- 此設定非常簡潔,對於某些情況來說可以快速解決問題 +- 同時適用於 `+page.ts`/`+layout.ts` 和 `+page.server.ts`/`+layout.server.ts` 的 load 函數 + +缺點: + +- 如果你在樹狀結構較深的元件中呼叫 `createQuery`,則需要將 `initialData` 傳遞到該處 +- 如果你在多個位置使用相同的查詢呼叫 `createQuery`,則需要將 `initialData` 傳遞給所有位置 +- 無法知道查詢是在伺服器端的什麼時間點取得的,因此 `dataUpdatedAt` 和判斷查詢是否需要重新取得的依據會是頁面載入的時間 + +### 使用 `prefetchQuery` + +Svelte Query 支援在伺服器端預先取得查詢。使用以下設定,你可以在資料傳送到使用者的瀏覽器之前,先取得資料並傳遞給 QueryClientProvider。因此,這些資料已經存在快取中,客戶端不會進行初始取得。 + +**src/routes/+layout.ts** + +```ts +import { browser } from '$app/environment' +import { QueryClient } from '@tanstack/svelte-query' + +export async function load() { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + enabled: browser, + }, + }, + }) + + return { queryClient } +} +``` + +**src/routes/+layout.svelte** + +```svelte + + + + + +``` + +**src/routes/+page.ts** + +```ts +export async function load({ parent, fetch }) { + const { queryClient } = await parent() + + // 這裡需要使用 SvelteKit 的 fetch 函數 + await queryClient.prefetchQuery({ + queryKey: ['posts'], + queryFn: async () => (await fetch('/api/posts')).json(), + }) +} +``` + +**src/routes/+page.svelte** + +```svelte + +``` + +優點: + +- 伺服器載入的資料可以在任何地方存取,無需透過屬性傳遞 (prop-drilling) +- 頁面渲染後,客戶端不會進行初始取得,因為查詢快取保留了所有關於查詢的資訊,包括 `dataUpdatedAt` + +缺點: + +- 初始設定需要更多檔案 +- 不適用於 `+page.server.ts`/`+layout.server.ts` 的 load 函數(不過,與 TanStack Query 一起使用的 API 無論如何都需要完全暴露給瀏覽器) diff --git a/docs/zh-hant/framework/vue/community/community-projects.md b/docs/zh-hant/framework/vue/community/community-projects.md new file mode 100644 index 00000000000..b36169b7735 --- /dev/null +++ b/docs/zh-hant/framework/vue/community/community-projects.md @@ -0,0 +1,31 @@ +--- +source-updated-at: '2024-08-09T15:33:58.000Z' +translation-updated-at: '2025-05-08T20:19:02.058Z' +id: community-projects +title: 社群專案 +--- + +## 社群專案 (Community Projects) + +有許多社群專案基於 Vue Query 開發,並利用它來提供額外功能或增強開發者體驗。以下專案按字母順序排列。如果您有專案想加入此清單,請提交 PR! + +> 請注意,這些專案完全由社群維護。若您對這些專案有任何疑問,請直接聯繫專案維護者。 + +## Query Key 工廠 (Query Key factory) + +一個用於建立類型安全標準化查詢鍵 (query keys) 的函式庫,對於 `@tanstack/query` 的快取管理非常有用 + +連結: https://github.com/lukemorales/query-key-factory + +## 模型:統一資料來源 (Model: Unified Data Source) + +Zova 在 MVC 架構中基於 Vue Query 提供了 Model 機制,透過 Model 封裝統一資料來源,從而標準化資料使用方式、簡化程式碼結構,並提升程式碼的可維護性 + +連結: https://github.com/cabloy/zova +文件: https://zova.js.org/guide/techniques/model/introduction.html + +## Query Rewind + +在開發過程中進行狀態時間旅行 (time travel) 和視覺化 + +連結: https://reactqueryrewind.com/ diff --git a/docs/zh-hant/framework/vue/community/tkdodos-blog.md b/docs/zh-hant/framework/vue/community/tkdodos-blog.md new file mode 100644 index 00000000000..98d13c5594f --- /dev/null +++ b/docs/zh-hant/framework/vue/community/tkdodos-blog.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:45.894Z' +id: tkdodos-blog +title: TkDodo's Blog +ref: docs/zh-hant/framework/react/community/tkdodos-blog.md +--- diff --git a/docs/zh-hant/framework/vue/devtools.md b/docs/zh-hant/framework/vue/devtools.md new file mode 100644 index 00000000000..e05f0f1ea61 --- /dev/null +++ b/docs/zh-hant/framework/vue/devtools.md @@ -0,0 +1,91 @@ +--- +source-updated-at: '2024-08-21T11:18:15.000Z' +translation-updated-at: '2025-05-08T20:16:05.337Z' +id: devtools +title: 開發工具 +--- + +舉起雙手歡呼吧,因為 Vue Query 附帶專用的開發者工具 (devtools)! 🥳 + +當你開始使用 Vue Query 時,你會希望這些開發者工具伴隨左右。它們能幫助你可視化 Vue Query 的所有內部運作,並在你陷入困境時節省數小時的除錯時間! + +## 基於元件的開發者工具 (Vue 3) + +你可以使用專用套件直接將開發者工具元件整合到頁面中。 +基於元件的開發者工具採用與框架無關的實作,並始終保持最新狀態。 + +開發者工具元件是一個獨立的套件,需要先安裝: + +```bash +npm i @tanstack/vue-query-devtools +``` + +或 + +```bash +pnpm add @tanstack/vue-query-devtools +``` + +或 + +```bash +yarn add @tanstack/vue-query-devtools +``` + +或 + +```bash +bun add @tanstack/vue-query-devtools +``` + +預設情況下,Vue Query 開發者工具僅在 `process.env.NODE_ENV === 'development'` 時包含在套件中,因此你無需擔心在生產環境建置時排除它們。 + +開發者工具會以固定浮動元素的形式掛載到你的應用程式中,並在畫面角落提供一個切換按鈕來顯示或隱藏開發者工具。此切換狀態會儲存在 localStorage 中,並在重新載入時記住。 + +將以下程式碼放在 Vue 應用程式中盡可能高的位置。越接近頁面根目錄,效果越好! + +```vue + + + +``` + +### 選項 + +- `initialIsOpen: Boolean` + - 設為 `true` 可讓開發者工具預設為開啟狀態。 +- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` + - 預設為 `bottom-right`。 + - 用於設定開啟和關閉開發者工具面板的 React Query 標誌位置。 +- `position?: "top" | "bottom" | "left" | "right"` + - 預設為 `bottom`。 + - 用於設定 React Query 開發者工具面板的位置。 +- `client?: QueryClient` + - 使用此選項可自訂 QueryClient。否則會使用最近上下文中的 QueryClient。 +- `errorTypes?: { name: string; initializer: (query: Query) => TError}` + - 使用此選項可預定義一些能在查詢中觸發的錯誤。當從 UI 切換該錯誤時,初始化器會呼叫(傳入特定查詢)。它必須回傳一個 Error。 +- `styleNonce?: string` + - 使用此選項可傳遞 nonce 給新增到文件 head 的 style 標籤。這在使用內容安全策略 (CSP) nonce 允許內聯樣式時很有用。 +- `shadowDOMTarget?: ShadowRoot` + - 預設行為會將開發者工具的樣式套用到 DOM 中的 head 標籤。 + - 使用此選項可傳遞 shadow DOM 目標給開發者工具,讓樣式套用在 shadow DOM 中,而非 light DOM 的 head 標籤內。 + +## 傳統開發者工具 + +Vue Query 將無縫整合 [官方 Vue 開發者工具](https://github.com/vuejs/devtools-next),新增自訂檢查器和時間軸事件。 +開發者工具的程式碼預設會從生產環境套件中被 Tree Shaking 移除。 + +要使其運作,你只需在插件選項中啟用: + +```ts +app.use(VueQueryPlugin, { + enableDevtoolsV6Plugin: true, +}) +``` + +同時支援 v6 和 v7 版本的開發者工具。 diff --git a/docs/zh-hant/framework/vue/graphql.md b/docs/zh-hant/framework/vue/graphql.md new file mode 100644 index 00000000000..0e83af79bfc --- /dev/null +++ b/docs/zh-hant/framework/vue/graphql.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:15:31.418Z' +id: graphql +title: GraphQL +--- + +由於 Vue Query 的資料獲取機制是基於 Promise 無關性 (agnostically) 建構的,您實際上可以將 Vue Query 與任何非同步資料獲取客戶端搭配使用,包括 GraphQL! + +> 請注意,Vue Query 不支援正規化快取 (normalized caching)。雖然絕大多數使用者實際上並不需要正規化快取,甚至從中獲得的效益不如他們想像的那麼多,但在極少數情況下可能需要此功能,因此請務必先與我們確認,以確保這確實是您需要的功能! diff --git a/docs/zh-hant/framework/vue/guides/background-fetching-indicators.md b/docs/zh-hant/framework/vue/guides/background-fetching-indicators.md new file mode 100644 index 00000000000..9b09ade9a61 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/background-fetching-indicators.md @@ -0,0 +1,47 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:19:00.063Z' +id: background-fetching-indicators +title: 背景獲取指示器 +--- + +## 背景擷取指示器 (Background Fetching Indicators) + +查詢的 `status === 'pending'` 狀態足以顯示查詢的初始硬載入狀態,但有時您可能希望顯示額外的指示器,表示查詢正在背景中重新擷取。為此,查詢還提供了一個 `isFetching` 布林值,您可以用來顯示它正在擷取狀態,無論 `status` 變數的狀態為何: + +```vue + + + +``` + +## 顯示全域背景擷取載入狀態 (Displaying Global Background Fetching Loading State) + +除了個別查詢的載入狀態外,如果您希望在**任何**查詢正在擷取時(包括在背景中)顯示全域載入指示器,可以使用 `useIsFetching` 鉤子 (hook): + +```vue + + + +``` diff --git a/docs/zh-hant/framework/vue/guides/caching.md b/docs/zh-hant/framework/vue/guides/caching.md new file mode 100644 index 00000000000..914a0b93e19 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/caching.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:40.691Z' +id: caching +title: Caching Examples +ref: docs/zh-hant/framework/react/guides/caching.md +--- diff --git a/docs/zh-hant/framework/vue/guides/custom-client.md b/docs/zh-hant/framework/vue/guides/custom-client.md new file mode 100644 index 00000000000..27eaf50dd17 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/custom-client.md @@ -0,0 +1,74 @@ +--- +source-updated-at: '2024-05-06T05:23:35.000Z' +translation-updated-at: '2025-05-08T20:19:04.830Z' +id: custom-client +title: 自訂客戶端 +--- + +### 自訂客戶端 (Custom client) + +Vue Query 允許為 Vue 的上下文 (context) 提供自訂的 `QueryClient`。 + +當您需要預先建立 `QueryClient` 以整合其他無法存取 Vue 上下文的函式庫時,這會非常實用。 + +因此,`VueQueryPlugin` 接受 `QueryClientConfig` 或 `QueryClient` 作為外掛 (plugin) 選項。 + +如果您提供 `QueryClientConfig`,則會內部建立 `QueryClient` 實例並提供給 Vue 上下文。 + +```tsx +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { queries: { staleTime: 3600 } }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +```tsx +const myClient = new QueryClient(queryClientConfig) +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClient: myClient, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +### 自訂上下文鍵 (Custom context key) + +您也可以自訂 `QueryClient` 在 Vue 上下文中可存取的鍵名 (key)。這在您想要避免 Vue2 同頁面多個應用程式之間的名稱衝突時特別有用。 + +此功能同時適用於預設和自訂的 `QueryClient`。 + +```tsx +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientKey: 'Foo', +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +```tsx +const myClient = new QueryClient(queryClientConfig) +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClient: myClient, + queryClientKey: 'Foo', +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +若要使用自訂的客戶端鍵名,您必須在查詢選項 (query options) 中提供它: + +```js +useQuery({ + queryKey: ['query1'], + queryFn: fetcher, + queryClientKey: 'foo', +}) +``` + +在內部,自訂鍵名會與預設查詢鍵名結合作為後綴 (suffix),但使用者無需擔心這一點。 + +```tsx +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientKey: 'Foo', +} +app.use(VueQueryPlugin, vueQueryPluginOptions) // -> VUE_QUERY_CLIENT:Foo +``` diff --git a/docs/zh-hant/framework/vue/guides/default-query-function.md b/docs/zh-hant/framework/vue/guides/default-query-function.md new file mode 100644 index 00000000000..b5b57608a21 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/default-query-function.md @@ -0,0 +1,33 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:49.955Z' +id: default-query-function +title: 預設查詢函數 +--- + +如果你基於任何原因,希望能在整個應用程式中共享同一個查詢函式,並僅透過查詢鍵 (query key) 來識別應該獲取的資料,你可以透過為 TanStack Query 提供一個 **預設查詢函式 (default query function)** 來實現: + +```tsx +// 定義一個預設查詢函式,它會接收查詢鍵 +const defaultQueryFn = async ({ queryKey }) => { + const { data } = await axios.get( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + ) + return data +} + +// 透過 defaultOptions 將預設查詢函式提供給你的應用程式 +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { queries: { queryFn: defaultQueryFn } }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) + +// 現在你只需要傳入一個鍵! +const { status, data, error, isFetching } = useQuery({ + queryKey: [`/posts/${postId}`], +}) +``` + +如果你想覆寫預設的 queryFn,只需像平常一樣提供你自己的函式即可。 diff --git a/docs/zh-hant/framework/vue/guides/dependent-queries.md b/docs/zh-hant/framework/vue/guides/dependent-queries.md new file mode 100644 index 00000000000..cada8fafc16 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/dependent-queries.md @@ -0,0 +1,89 @@ +--- +source-updated-at: '2024-04-08T07:32:55.000Z' +translation-updated-at: '2025-05-08T20:19:01.844Z' +id: dependent-queries +title: 依賴查詢 +--- + +## useQuery 相依查詢 (Dependent Query) + +相依查詢 (或稱序列查詢) 需要等待前一個查詢完成後才能執行。實現方式很簡單,只需使用 `enabled` 選項來告訴查詢何時可以執行: + +```js +// 取得使用者資料 +const { data: user } = useQuery({ + queryKey: ['user', email], + queryFn: () => getUserByEmail(email.value), +}) + +const userId = computed(() => user.value?.id) +const enabled = computed(() => !!user.value?.id) + +// 接著取得使用者的專案資料 +const { isIdle, data: projects } = useQuery({ + queryKey: ['projects', userId], + queryFn: () => getProjectsByUser(userId.value), + enabled, // 此查詢在 `enabled == true` 前不會執行 +}) +``` + +`projects` 查詢會以以下狀態開始: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'idle' +``` + +當 `user` 資料可用時,`projects` 查詢會被 `enabled` 並轉換為: + +```tsx +status: 'pending' +isPending: true +fetchStatus: 'fetching' +``` + +取得專案資料後,狀態會變為: + +```tsx +status: 'success' +isPending: false +fetchStatus: 'idle' +``` + +## useQueries 相依查詢 (Dependent Query) + +動態平行查詢 - `useQueries` 也可以依賴前一個查詢,實現方式如下: + +```tsx +// 取得使用者 ID 列表 +const { data: userIds } = useQuery({ + queryKey: ['users'], + queryFn: getUsersData, + select: (users) => users.map((user) => user.id), +}) + +const queries = computed(() => { + return userIds.value.length + ? userIds.value.map((id) => { + return { + queryKey: ['messages', id], + queryFn: () => getMessagesByUsers(id), + } + }) + : [] +}) + +// 接著取得使用者的訊息資料 +const usersMessages = useQueries({ + queries, // 如果 users 是 undefined,會回傳空陣列 +}) +``` + +**注意** `useQueries` 會回傳一個**查詢結果陣列** + +## 關於效能的注意事項 + +相依查詢本質上會形成一種[請求瀑布流 (request waterfall)](./request-waterfalls.md),這會影響效能。假設兩個查詢花費相同時間,序列執行總會比平行執行多花一倍時間,對於高延遲的客戶端影響尤其嚴重。如果可能,最好重構後端 API 讓兩個查詢能平行取得,雖然這在實務上不一定可行。 + +在上面的範例中,與其先執行 `getUserByEmail` 才能執行 `getProjectsByUser`,引入新的 `getProjectsByUserEmail` 查詢可以消除瀑布流問題。 diff --git a/docs/zh-hant/framework/vue/guides/disabling-queries.md b/docs/zh-hant/framework/vue/guides/disabling-queries.md new file mode 100644 index 00000000000..5ce8fc5baf6 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/disabling-queries.md @@ -0,0 +1,105 @@ +--- +source-updated-at: '2025-04-10T12:03:08.000Z' +translation-updated-at: '2025-05-08T20:19:06.924Z' +id: disabling-queries +title: 停用/暫停查詢 +--- + +若想阻止查詢 (query) 自動執行,可以使用 `enabled = false` 選項。`enabled` 選項也接受回調函式來返回布林值。 + +當 `enabled` 設為 `false` 時: + +- 若查詢有快取資料,則會以 `status === 'success'` 或 `isSuccess` 狀態初始化。 +- 若查詢無快取資料,則會以 `status === 'pending'` 和 `fetchStatus === 'idle'` 狀態開始。 +- 查詢不會在掛載時自動執行。 +- 查詢不會在背景自動重新取得資料。 +- 查詢會忽略查詢客戶端 (query client) 的 `invalidateQueries` 和 `refetchQueries` 呼叫(這些呼叫通常會觸發查詢重新取得資料)。 +- 從 `useQuery` 返回的 `refetch` 可用於手動觸發查詢,但與 `skipToken` 併用時會失效。 + +> TypeScript 使用者可改用 [skipToken](#typesafe-disabling-of-queries-using-skiptoken) 來替代 `enabled = false`。 + +```vue + + + +``` + +永久停用查詢會導致無法使用 TanStack Query 的許多強大功能(例如背景重新取得資料),這也不是慣用做法。此做法會讓你從宣告式模式(定義查詢執行時機的依賴條件)轉為命令式模式(點擊按鈕時才取得資料),且無法傳遞參數給 `refetch`。通常你需要的只是一個延遲初始執行的「惰性查詢 (lazy query)」: + +## 惰性查詢 (Lazy Queries) + +`enabled` 選項不僅能永久停用查詢,還可動態啟用/停用。典型範例是篩選表單——僅在使用者輸入篩選值後才發送首次請求: + +```vue + + + +``` + +### isLoading (原為 `isInitialLoading`) + +惰性查詢會從一開始就處於 `status: 'pending'` 狀態,因為 `pending` 表示尚未取得資料。雖然技術上正確,但由於此時並未實際取得資料(查詢未被 _啟用_),因此不適合用此標誌來顯示載入指示器。 + +若使用停用或惰性查詢,可改用 `isLoading` 標誌。這是個衍生標誌,由以下條件計算得出: + +`isPending && isFetching` + +因此僅在查詢首次執行時會返回 `true`。 + +## 使用 `skipToken` 實現類型安全的查詢停用 + +若使用 TypeScript,可用 `skipToken` 停用查詢。這適用於需基於條件停用查詢,同時保持類型安全的情況。 + +> 重要:`useQuery` 的 `refetch` 方法與 `skipToken` 併用時會失效。除此之外,`skipToken` 的行為與 `enabled: false` 相同。 + +```vue + + + +``` diff --git a/docs/zh-hant/framework/vue/guides/does-this-replace-client-state.md b/docs/zh-hant/framework/vue/guides/does-this-replace-client-state.md new file mode 100644 index 00000000000..276e4484a1e --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/does-this-replace-client-state.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:45.874Z' +id: does-this-replace-client-state +title: '這會取代 [Vuex, Pinia] 嗎?' +--- + +好的,讓我們從幾個重點開始: + +- TanStack Query 是一個 **伺服器狀態 (server-state)** 函式庫,負責管理伺服器與客戶端之間的異步操作 +- Vuex、Pinia、Zustand 等則是 **客戶端狀態 (client-state)** 函式庫,雖然可用於儲存異步數據,但與 TanStack Query 這類工具相比效率較低 + +明白這些要點後,簡短的回答是:TanStack Query **取代了那些用來管理客戶端狀態中緩存數據的樣板程式碼和相關配置,並將其簡化為寥寥數行程式碼。** + +對於大多數應用程式來說,當你把所有異步程式碼遷移到 TanStack Query 後,真正剩下的**全局可訪問客戶端狀態**通常非常少。 + +> 當然仍有某些情況,應用程式可能確實存在大量同步的純客戶端狀態(例如視覺設計工具或音樂製作應用程式),這時你可能還是需要客戶端狀態管理工具。需要注意的是,**TanStack Query 並非本地/客戶端狀態管理的替代品**。不過你可以毫無問題地將 TanStack Query 與大多數客戶端狀態管理工具搭配使用。 + +## 一個刻意的範例 + +這裡我們有一個由全局狀態函式庫管理的「全局」狀態: + +```tsx +const globalState = { + projects, + teams, + tasks, + users, + themeMode, + sidebarStatus, +} +``` + +目前全局狀態管理器緩存了 4 種伺服器狀態:`projects`、`teams`、`tasks` 和 `users`。如果我們將這些伺服器狀態資源移至 TanStack Query,剩下的全局狀態會變成這樣: + +```tsx +const globalState = { + themeMode, + sidebarStatus, +} +``` + +這也意味著,只需調用幾次 `useQuery` 和 `useMutation` 鉤子,我們就能移除所有用於管理伺服器狀態的樣板程式碼,例如: + +- 連接器 (Connectors) +- 動作創建器 (Action Creators) +- 中介軟體 (Middlewares) +- 歸約器 (Reducers) +- 載入/錯誤/結果狀態 (Loading/Error/Result states) +- 上下文 (Contexts) + +移除這些東西後,你可能會問自己:**「為了這麼一點全局狀態,還值得繼續使用客戶端狀態管理工具嗎?」** + +**這就由你決定了!** + +但 TanStack Query 的角色很明確。它能從你的應用程式中移除異步配置和樣板程式碼,並用寥寥數行程式碼取而代之。 + +還在等什麼?趕快試試看吧! diff --git a/docs/zh-hant/framework/vue/guides/filters.md b/docs/zh-hant/framework/vue/guides/filters.md new file mode 100644 index 00000000000..327c0e9e4d8 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/filters.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:19.446Z' +id: filters +title: Filters +ref: docs/zh-hant/framework/react/guides/filters.md +--- diff --git a/docs/zh-hant/framework/vue/guides/important-defaults.md b/docs/zh-hant/framework/vue/guides/important-defaults.md new file mode 100644 index 00000000000..7954a2fe00a --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/important-defaults.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:19.427Z' +id: important-defaults +title: Important Defaults +ref: docs/zh-hant/framework/react/guides/important-defaults.md +--- + +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/vue/guides/infinite-queries.md b/docs/zh-hant/framework/vue/guides/infinite-queries.md new file mode 100644 index 00000000000..47d043b5714 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/infinite-queries.md @@ -0,0 +1,58 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:19.408Z' +id: infinite-queries +title: Infinite Queries +ref: docs/zh-hant/framework/react/guides/infinite-queries.md +--- + +[//]: # 'Example' + +```vue + + + +``` + +[//]: # 'Example' diff --git a/docs/zh-hant/framework/vue/guides/initial-query-data.md b/docs/zh-hant/framework/vue/guides/initial-query-data.md new file mode 100644 index 00000000000..064c8fc56d3 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/initial-query-data.md @@ -0,0 +1,10 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:19.388Z' +id: initial-query-data +title: Initial Query Data +ref: docs/zh-hant/framework/react/guides/initial-query-data.md +--- + +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/zh-hant/framework/vue/guides/invalidations-from-mutations.md b/docs/zh-hant/framework/vue/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..31f4052b0c1 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/invalidations-from-mutations.md @@ -0,0 +1,35 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:33.177Z' +id: invalidations-from-mutations +title: 來自變更的失效 +--- + +## 失效化與變異的關聯 + +僅讓查詢失效只是成功的一半,知道**何時**讓它們失效才是關鍵。通常當應用程式中的變異 (mutation) 成功時,很可能會有相關的查詢需要被失效化,甚至重新獲取資料以反映變異帶來的新變更。 + +舉例來說,假設我們有一個新增待辦事項的變異: + +```tsx +const mutation = useMutation({ mutationFn: postTodo }) +``` + +當 `postTodo` 變異成功時,我們通常會希望所有 `todos` 查詢都失效化,並可能重新獲取資料以顯示新的待辦事項。要實現這一點,可以使用 `useMutation` 的 `onSuccess` 選項和 `client` 的 `invalidateQueries` 方法: + +```tsx +import { useMutation, useQueryClient } from '@tanstack/vue-query' + +const queryClient = useQueryClient() + +// 當此變異成功時,讓所有帶有 `todos` 或 `reminders` 查詢鍵的查詢失效化 +const mutation = useMutation({ + mutationFn: addTodo, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['todos'] }) + queryClient.invalidateQueries({ queryKey: ['reminders'] }) + }, +}) +``` + +你可以利用 [`useMutation` 鉤子](./mutations.md) 提供的任何回調函數來觸發失效化操作。 diff --git a/docs/zh-hant/framework/vue/guides/migrating-to-v5.md b/docs/zh-hant/framework/vue/guides/migrating-to-v5.md new file mode 100644 index 00000000000..010cc03fbfb --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/migrating-to-v5.md @@ -0,0 +1,325 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:20:54.366Z' +id: migrating-to-tanstack-query-5 +title: 遷移至 v5 +--- + +## 重大變更 + +v5 是一個主要版本,因此需要注意以下重大變更: + +### 僅支援單一簽名格式(單一物件) + +`useQuery` 及其相關方法過去在 TypeScript 中有多種重載形式:即函數可以有多種調用方式。這不僅在類型維護上相當困難,還需要運行時檢查來判斷第一和第二參數的類型,以正確創建選項。 + +現在我們僅支援物件格式。 + +```tsx +useQuery(key, fn, options) // [!code --] +useQuery({ queryKey, queryFn, ...options }) // [!code ++] +useInfiniteQuery(key, fn, options) // [!code --] +useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +useMutation(fn, options) // [!code --] +useMutation({ mutationFn, ...options }) // [!code ++] +useIsFetching(key, filters) // [!code --] +useIsFetching({ queryKey, ...filters }) // [!code ++] +useIsMutating(key, filters) // [!code --] +useIsMutating({ mutationKey, ...filters }) // [!code ++] +``` + +```tsx +queryClient.isFetching(key, filters) // [!code --] +queryClient.isFetching({ queryKey, ...filters }) // [!code ++] +queryClient.ensureQueryData(key, filters) // [!code --] +queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++] +queryClient.getQueriesData(key, filters) // [!code --] +queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++] +queryClient.setQueriesData(key, updater, filters, options) // [!code --] +queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++] +queryClient.removeQueries(key, filters) // [!code --] +queryClient.removeQueries({ queryKey, ...filters }) // [!code ++] +queryClient.resetQueries(key, filters, options) // [!code --] +queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.cancelQueries(key, filters, options) // [!code --] +queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.invalidateQueries(key, filters, options) // [!code --] +queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.refetchQueries(key, filters, options) // [!code --] +queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++] +queryClient.fetchQuery(key, fn, options) // [!code --] +queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchQuery(key, fn, options) // [!code --] +queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.fetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --] +queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++] +``` + +```tsx +queryCache.find(key, filters) // [!code --] +queryCache.find({ queryKey, ...filters }) // [!code ++] +queryCache.findAll(key, filters) // [!code --] +queryCache.findAll({ queryKey, ...filters }) // [!code ++] +``` + +### `queryClient.getQueryData` 現在僅接受 `queryKey` 作為參數 + +`queryClient.getQueryData` 的參數變更為僅接受 `queryKey` + +```tsx +queryClient.getQueryData(queryKey, filters) // [!code --] +queryClient.getQueryData(queryKey) // [!code ++] +``` + +### `queryClient.getQueryState` 現在僅接受 `queryKey` 作為參數 + +`queryClient.getQueryState` 的參數變更為僅接受 `queryKey` + +```tsx +queryClient.getQueryState(queryKey, filters) // [!code --] +queryClient.getQueryState(queryKey) // [!code ++] +``` + +#### 代碼修改工具 (Codemod) + +為了簡化移除重載的遷移過程,v5 提供了一個代碼修改工具。 + +> 此代碼修改工具會盡最大努力協助您遷移重大變更。請徹底檢查生成的代碼!此外,有些邊緣情況無法通過代碼修改工具檢測到,因此請注意日誌輸出。 + +如果您想針對 `.js` 或 `.jsx` 文件運行此工具,請使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=js,jsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +如果您想針對 `.ts` 或 `.tsx` 文件運行此工具,請使用以下命令: + +``` +npx jscodeshift@latest ./path/to/src/ \ + --extensions=ts,tsx \ + --parser=tsx \ + --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs +``` + +請注意,在 `TypeScript` 的情況下,您需要使用 `tsx` 作為解析器;否則代碼修改工具將無法正確應用! + +**注意:** 應用代碼修改工具可能會破壞您的代碼格式,因此在應用代碼修改工具後,請別忘記運行 `prettier` 和/或 `eslint`! + +關於代碼修改工具如何運作的幾點說明: + +- 一般情況下,我們會尋找幸運的情況,即第一個參數是一個物件表達式並包含 "queryKey" 或 "mutationKey" 屬性(取決於正在轉換的鉤子/方法調用)。如果是這種情況,您的代碼已經符合新的簽名,因此代碼修改工具不會觸碰它。🎉 +- 如果上述條件不滿足,則代碼修改工具將檢查第一個參數是否為數組表達式或引用數組表達式的標識符。如果是這種情況,代碼修改工具會將其放入物件表達式中,然後它將成為第一個參數。 +- 如果可以推斷出物件參數,代碼修改工具將嘗試將已存在的屬性複製到新創建的物件中。 +- 如果代碼修改工具無法推斷使用情況,則會在控制台上留下一條消息。該消息包含使用情況的文件名和行號。在這種情況下,您需要手動進行遷移。 +- 如果轉換導致錯誤,您也會在控制台上看到一條消息。此消息將通知您發生了意外情況,請手動進行遷移。 + +### `useQuery`(和 `QueryObserver`)上的回調函數已被移除 + +`onSuccess`、`onError` 和 `onSettled` 已從查詢中移除。它們在突變中未被觸碰。請參閱 [此 RFC](https://github.com/TanStack/query/discussions/5279) 了解此變更的動機以及替代方案。 + +### `refetchInterval` 回調函數現在僅傳遞 `query` + +這簡化了回調的調用方式(`refetchOnWindowFocus`、`refetchOnMount` 和 `refetchOnReconnect` 回調也僅傳遞查詢),並修復了當回調獲取由 `select` 轉換的數據時的一些類型問題。 + +```tsx +- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --] ++ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++] +``` + +您仍然可以通過 `query.state.data` 訪問數據,但它不會是被 `select` 轉換後的數據。如果您需要訪問轉換後的數據,可以對 `query.state.data` 再次調用轉換函數。 + +### `useQuery` 中的 `remove` 方法已被移除 + +以前,`remove` 方法用於從 `queryCache` 中移除查詢而不通知觀察者。它最適合用於強制移除不再需要的數據,例如在用戶註銷時。 + +但在查詢仍然活躍時這樣做並無太大意義,因為它只會在下一次重新渲染時觸發硬加載狀態。 + +如果您仍然需要移除查詢,可以使用 `queryClient.removeQueries({queryKey: key})` + +```tsx +const queryClient = useQueryClient() +const query = useQuery({ queryKey, queryFn }) + +query.remove() // [!code --] +queryClient.removeQueries({ queryKey }) // [!code ++] +``` + +### 最低要求的 TypeScript 版本現在是 4.7 + +主要是因為修復了一個關於類型推斷的重要問題。請參閱此 [TypeScript 問題](https://github.com/microsoft/TypeScript/issues/43371) 了解更多信息。 + +### `useQuery` 中的 `isDataEqual` 選項已被移除 + +以前,此函數用於指示是否使用先前的 `data`(`true`)或新數據(`false`)作為查詢的解析數據。 + +您可以通過向 `structuralSharing` 傳遞一個函數來實現相同的功能: + +```tsx + import { replaceEqualDeep } from '@tanstack/react-query' + +- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --] ++ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++] +``` + +### 已棄用的自定義記錄器已被移除 + +自定義記錄器在 v4 中已被棄用,並在此版本中被移除。記錄僅在開發模式下有效,而在開發模式下傳遞自定義記錄器是不必要的。 + +### 支持的瀏覽器 + +我們已更新我們的 `browserslist` 以生成更現代、性能更好且更小的捆綁包。您可以在 [這裡](../../installation#requirements) 閱讀相關要求。 + +### 私有類字段和方法 + +TanStack Query 一直都有類的私有字段和方法,但它們並不是真正的私有——它們只是在 `TypeScript` 中是私有的。我們現在使用 [ECMAScript 私有類特性](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields),這意味著這些字段現在在運行時是真正私有的,無法從外部訪問。 + +### 將 `cacheTime` 重命名為 `gcTime` + +幾乎每個人都誤解了 `cacheTime`。它聽起來像是「數據被緩存的時間」,但這並不正確。 + +`cacheTime` 在查詢仍在使用時不會執行任何操作。它僅在查詢變為未使用後才開始生效。時間過後,數據將被「垃圾回收」以避免緩存增長。 + +`gc` 指的是「垃圾回收」時間。這有點技術性,但也是計算機科學中 [廣為人知的縮寫]()。 + +```tsx +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, // [!code --] ++ gcTime: 10 * MINUTE, // [!code ++] + }, + }, +}) +``` + +### `useErrorBoundary` 選項已更名為 `throwOnError` + +為了使 `useErrorBoundary` 選項更框架無關並避免與已建立的 React 函數前綴 "`use`"(用於鉤子)和 "ErrorBoundary" 組件名稱混淆,它已更名為 `throwOnError` 以更準確地反映其功能。 + +### TypeScript: `Error` 現在是錯誤的默認類型,而不是 `unknown` + +儘管在 JavaScript 中,您可以 `throw` 任何東西(這使得 `unknown` 是最正確的類型),但幾乎總是會拋出 `Error`(或 `Error` 的子類)。此變更使在 TypeScript 中處理 `error` 字段更容易。 + +如果您想拋出不是 `Error` 的東西,現在必須自己設置泛型: + +```ts +useQuery({ + queryKey: ['some-query'], + queryFn: async () => { + if (Math.random() > 0.5) { + throw 'some error' + } + return 42 + }, +}) +``` + +有關全局設置不同類型錯誤的方法,請參閱 [TypeScript 指南](../typescript.md#registering-a-global-error)。 + +### eslint `prefer-query-object-syntax` 規則已被移除 + +由於現在唯一支持的語法是物件語法,因此不再需要此規則。 + +### 移除 `keepPreviousData` 以支持 `placeholderData` 恆等函數 + +我們已移除 `keepPreviousData` 選項和 `isPreviousData` 標誌,因為它們的功能與 `placeholderData` 和 `isPlaceholderData` 標誌幾乎相同。 + +為了實現與 `keepPreviousData` 相同的功能,我們將先前查詢的 `data` 作為參數添加到 `placeholderData` 中,該參數接受一個恆等函數。因此,您只需向 `placeholderData` 提供一個恆等函數或使用 Tanstack Query 中包含的 `keepPreviousData` 函數。 + +> 需要注意的是,`useQueries` 不會在 `placeholderData` 函數中接收 `previousData` 作為參數。這是由於傳遞到數組中的查詢具有動態性質,這可能導致佔位符和 `queryFn` 的結果形狀不同。 + +```tsx +import { + useQuery, ++ keepPreviousData // [!code ++] +} from "@tanstack/react-query"; + +const { + data, +- isPreviousData, // [!code --] ++ isPlaceholderData, // [!code ++] +} = useQuery({ + queryKey, + queryFn, +- keepPreviousData: true, // [!code --] ++ placeholderData: keepPreviousData // [!code ++] +}); +``` + +在 Tanstack Query 的上下文中,恆等函數指的是始終返回其提供的參數(即數據)而不進行更改的函數。 + +```ts +useQuery({ + queryKey, + queryFn, + placeholderData: (previousData, previousQuery) => previousData, // 恆等函數,行為與 `keepPreviousData` 相同 +}) +``` + +然而,此變更有一些需要注意的地方: + +- `placeholderData` 始終會將您置於 `success` 狀態,而 `keepPreviousData` 會給您先前查詢的狀態。如果我們成功獲取數據後在後台重新獲取時出現錯誤,該狀態可能是 `error`。然而,錯誤本身並未共享,因此我們決定堅持 `placeholderData` 的行為。 +- `keepPreviousData` 會給您先前數據的 `dataUpdatedAt` 時間戳,而使用 `placeholderData` 時,`dataUpdatedAt` 將保持為 `0`。如果您想在屏幕上連續顯示該時間戳,這可能會很煩人。但您可以通過 `useEffect` 繞過這個問題。 + + ```ts + const [updatedAt, setUpdatedAt] = useState(0) + + const { data, dataUpdatedAt } = useQuery({ + queryKey: ['projects', page], + queryFn: () => fetchProjects(page), + }) + + useEffect(() => { + if (dataUpdatedAt > updatedAt) { + setUpdatedAt(dataUpdatedAt) + } + }, [dataUpdatedAt]) + ``` + +### 窗口焦點重新獲取不再監聽 `focus` 事件 + +現在僅使用 `visibilitychange` 事件。這是可行的,因為我們僅支持支持 `visibilitychange` 事件的瀏覽器。這修復了 [此處列出](https://github.com/TanStack/query/pull/4805) 的一系列問題。 + +### 網絡狀態不再依賴 `navigator.onLine` 屬性 + +`navigator.onLine` 在基於 Chromium 的瀏覽器中效果不佳。有 [許多問題](https://bugs.chromium.org/p/chromium/issues/list?q=navigator.online) 關於假陰性,這導致查詢被錯誤地標記為 `offline`。 + +為了解決這個問題,我們現在始終以 `online: true` 開始,並且僅監聽 `online` 和 `offline` 事件來更新狀態。 + +這應該會減少假陰性的可能性,然而,對於通過 `serviceWorkers` 加載的離線應用程序,這可能意味著假陽性,這些應用程序即使沒有互聯網連接也可以工作。 + +### 移除自定義 `context` 屬性以支持自定義 `queryClient` 實例 + +在 v4 中,我們引入了向所有 react-query 鉤子傳遞自定義 `context` 的可能性。這在使用微前端時實現了適當的隔離。 + +然而,`context` 是一個僅限於 react 的特性。`context` 所做的只是讓我們訪問 `queryClient`。我們可以通過允許直接傳入自定義 `queryClient` 來實現相同的隔離。 +這反過來將使其他框架能夠以框架無關的方式擁有相同的功能。 + +```tsx +import { queryClient } from './my-client' + +const { data } = useQuery( + { + queryKey: ['users', id], + queryFn: () => fetch(...), +- context: customContext // [!code --] + }, ++ queryClient, // [!code ++] +) +``` + +### 移除 `refetchPage` 以支持 `maxPages` + +在 v4 中,我們引入了通過 `refetchPage` 函數定義無限查詢要重新獲取的頁面的可能性。 + +然而,重新獲取所有頁面可能會導致 UI 不一致。此外,此選項在例如 `queryClient.refetchQueries` 上可用,但它僅對無限查詢有效,而不是「普通」查詢。 + +v5 包括無限查詢的新 `maxPages` 選項,以限制存儲在查詢數據中並隨後重新獲取的頁面數量。您可以根據您想要提供的 UX 和重新獲取性能調整 `maxPages` 值。 + +請注意,無限列表必須是雙向的,這需要同時定義 diff --git a/docs/zh-hant/framework/vue/guides/mutations.md b/docs/zh-hant/framework/vue/guides/mutations.md new file mode 100644 index 00000000000..d28df00b208 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/mutations.md @@ -0,0 +1,306 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:19:54.070Z' +id: mutations +title: 變更 +--- + +與查詢不同,變異 (mutations) 通常用於建立/更新/刪除資料或執行伺服器副作用 (server side-effects)。為此,TanStack Query 導出了 `useMutation` 鉤子 (hook)。 + +以下是一個將新待辦事項 (todo) 新增至伺服器的變異範例: + +```vue + + + +``` + +在任何時刻,變異只能處於以下其中一種狀態: + +- `isIdle` 或 `status === 'idle'` - 變異目前處於閒置或全新/重置狀態 +- `isPending` 或 `status === 'pending'` - 變異正在執行中 +- `isError` 或 `status === 'error'` - 變異發生錯誤 +- `isSuccess` 或 `status === 'success'` - 變異成功且變異資料可供使用 + +除了這些主要狀態外,根據變異的狀態還可取得更多資訊: + +- `error` - 若變異處於 `error` 狀態,可透過 `error` 屬性取得錯誤資訊。 +- `data` - 若變異處於 `success` 狀態,可透過 `data` 屬性取得資料。 + +在上面的範例中,你也看到可以透過呼叫 `mutate` 函式並傳入**單一變數或物件**來將變數傳遞給變異函式。 + +即使只有變數,變異本身並不特別,但當與 `onSuccess` 選項、[Query Client 的 `invalidateQueries` 方法](../../../reference/QueryClient.md#queryclientinvalidatequeries) 以及 [Query Client 的 `setQueryData` 方法](../../../reference/QueryClient.md#queryclientsetquerydata) 搭配使用時,變異就成為非常強大的工具。 + +## 重置變異狀態 + +有時你可能需要清除變異請求的 `error` 或 `data`。為此,你可以使用 `reset` 函式來處理: + +```vue + + + +``` + +## 變異副作用 + +`useMutation` 提供了一些輔助選項,讓你能在變異生命週期的任何階段快速且輕鬆地處理副作用。這些選項對於[變異後使查詢失效並重新擷取](./invalidations-from-mutations.md) 甚至 [樂觀更新 (optimistic updates)](./optimistic-updates.md) 都非常有用: + +```tsx +useMutation({ + mutationFn: addTodo, + onMutate: (variables) => { + // 變異即將發生! + + // 可選返回包含資料的上下文,用於例如回滾操作 + return { id: 1 } + }, + onError: (error, variables, context) => { + // 發生錯誤! + console.log(`回滾樂觀更新,ID:${context.id}`) + }, + onSuccess: (data, variables, context) => { + // 成功! + }, + onSettled: (data, error, variables, context) => { + // 無論錯誤或成功...都不重要! + }, +}) +``` + +當在任何回調函式中返回 Promise 時,它會先被等待,然後才會呼叫下一個回調: + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: async () => { + console.log('我是第一個!') + }, + onSettled: async () => { + console.log('我是第二個!') + }, +}) +``` + +你可能會發現,除了在 `useMutation` 上定義的回調外,你還想在呼叫 `mutate` 時**觸發額外的回調**。這可以用來觸發元件特定的副作用。為此,你可以在變異變數之後將任何相同的回調選項提供給 `mutate` 函式。支援的選項包括:`onSuccess`、`onError` 和 `onSettled`。請注意,若你的元件在變異完成前卸載,這些額外的回調將不會執行。 + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 我會先觸發 + }, + onError: (error, variables, context) => { + // 我會先觸發 + }, + onSettled: (data, error, variables, context) => { + // 我會先觸發 + }, +}) + +mutate(todo, { + onSuccess: (data, variables, context) => { + // 我會第二個觸發! + }, + onError: (error, variables, context) => { + // 我會第二個觸發! + }, + onSettled: (data, error, variables, context) => { + // 我會第二個觸發! + }, +}) +``` + +### 連續變異 + +在處理連續變異時,`onSuccess`、`onError` 和 `onSettled` 回調的行為略有不同。當傳遞給 `mutate` 函式時,它們只會觸發一次,且僅在元件仍然掛載時。這是因為每次呼叫 `mutate` 函式時,變異觀察者 (mutation observer) 都會被移除並重新訂閱。相反地,`useMutation` 的處理程序會針對每個 `mutate` 呼叫執行。 + +> 請注意,傳遞給 `useMutation` 的 `mutationFn` 很可能是非同步的。在這種情況下,變異完成的順序可能與 `mutate` 函式呼叫的順序不同。 + +```tsx +useMutation({ + mutationFn: addTodo, + onSuccess: (data, variables, context) => { + // 會被呼叫 3 次 + }, +}) + +const todos = ['待辦事項 1', '待辦事項 2', '待辦事項 3'] +todos.forEach((todo) => { + mutate(todo, { + onSuccess: (data, variables, context) => { + // 只會執行一次,針對最後一個變異 (待辦事項 3), + // 無論哪個變異先完成 + }, + }) +}) +``` + +## Promise + +使用 `mutateAsync` 而非 `mutate` 來取得一個會在成功時解析 (resolve) 或在錯誤時拋出 (throw) 的 Promise。這可以用於例如組合副作用。 + +```tsx +const mutation = useMutation({ mutationFn: addTodo }) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('完成') +} +``` + +## 重試 + +預設情況下,TanStack Query 不會在錯誤時重試變異,但可以透過 `retry` 選項啟用: + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + retry: 3, +}) +``` + +若變異因裝置離線而失敗,它們會在裝置重新連線時以相同的順序重試。 + +## 持久化變異 + +若有需要,可以將變異持久化到儲存空間,並在稍後恢復。這可以透過水合 (hydration) 函式實現: + +```tsx +const queryClient = new QueryClient() + +// 定義 "addTodo" 變異 +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables) => { + // 取消目前的待辦事項列表查詢 + await queryClient.cancelQueries({ queryKey: ['todos'] }) + + // 建立樂觀待辦事項 + const optimisticTodo = { id: uuid(), title: variables.title } + + // 將樂觀待辦事項新增至待辦事項列表 + queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // 返回包含樂觀待辦事項的上下文 + return { optimisticTodo } + }, + onSuccess: (result, variables, context) => { + // 將待辦事項列表中的樂觀待辦事項替換為結果 + queryClient.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === context.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, context) => { + // 從待辦事項列表中移除樂觀待辦事項 + queryClient.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== context.optimisticTodo.id), + ) + }, + retry: 3, +}) + +// 在某個元件中開始變異: +const mutation = useMutation({ mutationKey: ['addTodo'] }) +mutation.mutate({ title: '標題' }) + +// 若變異因裝置離線等原因暫停, +// 則可以在應用程式退出時將暫停的變異脫水 (dehydrate): +const state = dehydrate(queryClient) + +// 然後可以在應用程式啟動時再次水合 (hydrate) 變異: +hydrate(queryClient, state) + +// 恢復暫停的變異: +queryClient.resumePausedMutations() +``` + +### 持久化離線變異 + +若你使用 [persistQueryClient 插件](../plugins/persistQueryClient.md) 持久化離線變異,除非你提供預設的變異函式,否則在頁面重新載入時無法恢復變異。 + +這是一個技術限制。當持久化到外部儲存空間時,只有變異的狀態會被持久化,因為函式無法被序列化。水合後,觸發變異的元件可能尚未掛載,因此呼叫 `resumePausedMutations` 可能會導致錯誤:`找不到 mutationFn`。 + +```js +const client = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 小時 + }, + }, +}) + +// 我們需要一個預設的變異函式,以便暫停的變異在頁面重新載入後可以恢復 +queryClient.setMutationDefaults({ + mutationKey: ['todos'], + mutationFn: ({ id, data }) => { + return api.updateTodo(id, data) + }, +}) + +const vueQueryOptions: VueQueryPluginOptions = { + queryClient: client, + clientPersister: (queryClient) => { + return persistQueryClient({ + queryClient, + persister: createSyncStoragePersister({ storage: localStorage }), + }) + }, + clientPersisterOnSuccess: (queryClient) => { + queryClient.resumePausedMutations() + }, +} + +createApp(App).use(VueQueryPlugin, vueQueryOptions).mount('#app') +``` + +我們還有一個涵蓋查詢和變異的完整 [離線範例](../examples/offline)。 + +## 變異範圍 + +預設情況下,所有變異都是並行執行的 — 即使你多次呼叫相同變異的 `.mutate()`。可以透過為變異指定帶有 `id` 的 `scope` 來避免這種情況。所有具有相同 `scope.id` 的變異將會序列化執行,這意味著當它們被觸發時,若該範圍已有變異正在進行中,它們會以 `isPaused: true` 狀態開始。它們會被放入佇列中,並在輪到它們時自動恢復。 + +```tsx +const mutation = useMutation({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +}) +``` diff --git a/docs/zh-hant/framework/vue/guides/network-mode.md b/docs/zh-hant/framework/vue/guides/network-mode.md new file mode 100644 index 00000000000..131e3676bcd --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/network-mode.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:01.706Z' +id: network-mode +title: Network Mode +ref: docs/zh-hant/framework/react/guides/network-mode.md +--- diff --git a/docs/zh-hant/framework/vue/guides/optimistic-updates.md b/docs/zh-hant/framework/vue/guides/optimistic-updates.md new file mode 100644 index 00000000000..3b7d6479fdf --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/optimistic-updates.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:18:01.687Z' +id: optimistic-updates +title: Optimistic Updates +ref: docs/zh-hant/framework/react/guides/optimistic-updates.md +replace: + React: Vue +--- diff --git a/docs/zh-hant/framework/vue/guides/paginated-queries.md b/docs/zh-hant/framework/vue/guides/paginated-queries.md new file mode 100644 index 00000000000..6cca5558152 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/paginated-queries.md @@ -0,0 +1,77 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:38.975Z' +id: paginated-queries +title: 分頁查詢 +--- + +分頁渲染資料是一種非常常見的 UI 模式,在 TanStack Query 中,只需將頁面資訊包含在查詢鍵 (query key) 中即可「直接運作」: + +```tsx +const result = useQuery({ + queryKey: ['projects', page], + queryFn: fetchProjects, +}) +``` + +然而,當你執行這個簡單範例時,可能會注意到一個奇怪的現象: + +**UI 會在 `success` 和 `pending` 狀態之間跳動,因為每個新頁面都被視為一個全新的查詢。** + +這種體驗並不理想,但不幸的是,這也是目前許多工具堅持的運作方式。不過 TanStack Query 可不一樣!如你所料,TanStack Query 提供了一個名為 `placeholderData` 的強大功能,讓我們能夠解決這個問題。 + +## 使用 `placeholderData` 實現更好的分頁查詢 + +考慮以下範例,我們理想情況下會希望遞增查詢的 `pageIndex`(或游標)。如果使用 `useQuery`,**技術上雖然仍能正常運作**,但當為每個頁面或游標建立和銷毀不同的查詢時,UI 會在 `success` 和 `pending` 狀態之間跳動。透過將 `placeholderData` 設為 `(previousData) => previousData` 或使用 TanStack Query 導出的 `keepPreviousData` 函數,我們可以獲得以下優勢: + +- **即使查詢鍵已改變,上次成功取得的資料仍可在請求新資料時使用**。 +- 當新資料到達時,舊的 `data` 會無縫切換為新資料。 +- 可透過 `isPlaceholderData` 得知查詢目前提供的資料類型 + +```vue + + + +``` + +## 使用 `placeholderData` 延遲無限查詢結果 + +雖然較不常見,但 `placeholderData` 選項也能完美搭配 `useInfiniteQuery` 鉤子 (hook) 使用,因此當無限查詢鍵隨時間變化時,你可以無縫地讓使用者繼續查看快取的資料。 diff --git a/docs/zh-hant/framework/vue/guides/parallel-queries.md b/docs/zh-hant/framework/vue/guides/parallel-queries.md new file mode 100644 index 00000000000..242a8caa246 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/parallel-queries.md @@ -0,0 +1,42 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:19.576Z' +id: parallel-queries +title: 平行查詢 +--- + +「平行 (Parallel)」查詢指的是同時執行多個查詢,以最大化資料獲取的並發性。 + +## 手動平行查詢 + +當平行查詢的數量固定時,使用平行查詢**不需要額外處理**。只需並列使用多個 TanStack Query 的 `useQuery` 和 `useInfiniteQuery` 鉤子即可! + +```vue + +``` + +## 使用 `useQueries` 實現動態平行查詢 + +如果需要執行的查詢數量會隨渲染變動,則不能使用手動查詢方式(這會違反鉤子規則)。此時 TanStack Query 提供了 `useQueries` 鉤子,可動態並行執行任意數量的查詢。 + +`useQueries` 接受一個包含 **queries 鍵**的**選項物件**,該鍵值為**查詢物件陣列**,並回傳**查詢結果陣列**: + +```js +const users = computed(...) +const queries = computed(() => users.value.map(user => { + return { + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + } + }) +); +const userQueries = useQueries({queries: queries}) +``` diff --git a/docs/zh-hant/framework/vue/guides/placeholder-query-data.md b/docs/zh-hant/framework/vue/guides/placeholder-query-data.md new file mode 100644 index 00000000000..5d110d797dc --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/placeholder-query-data.md @@ -0,0 +1,61 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:36.160Z' +id: placeholder-query-data +title: 佔位查詢資料 +--- + +## 什麼是預留位置資料 (placeholder data)? + +預留位置資料讓查詢 (query) 能表現得像已經有資料一樣,類似 `initialData` 選項,但**這些資料不會被持久化到快取 (cache)**。這在以下情境非常實用:當您有足夠的部分(或模擬)資料可以成功渲染查詢,同時在背景取得實際資料。 + +> 範例:單篇部落格文章的查詢可以從父層的部落格文章列表中提取「預覽」資料,該列表僅包含標題和文章內容的一小段摘要。您不會想把這些部分資料持久化到單篇文章查詢的結果中,但對於盡快顯示內容佈局非常有用,同時等待實際查詢完成取得完整物件。 + +有幾種方式可以在需要之前為查詢提供預留位置資料到快取: + +- 宣告式 (Declaratively): + - 提供 `placeholderData` 給查詢,以便在快取為空時預先填充 +- 命令式 (Imperatively): + - [使用 `queryClient` 和 `placeholderData` 選項預取或取得資料](./prefetching.md) + +當我們使用 `placeholderData` 時,我們的查詢不會處於 `pending` 狀態——它會從一開始就處於 `success` 狀態,因為我們有 `data` 可以顯示——即使這些資料只是「預留位置」資料。為了區分它與「真實」資料,我們還會在查詢結果中將 `isPlaceholderData` 標記設為 `true`。 + +## 預留位置資料作為值 + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: () => fetch('/todos'), + placeholderData: placeholderTodos, +}) +``` + +## 預留位置資料作為函式 + +`placeholderData` 也可以是一個函式,您可以在其中存取「先前」成功查詢的資料和查詢元資訊 (meta information)。這在您想將一個查詢的資料用作另一個查詢的預留位置資料時非常有用。當查詢鍵 (QueryKey) 變更時,例如從 `['todos', 1]` 變為 `['todos', 2]`,我們可以繼續顯示「舊」資料,而不必在資料從一個查詢「過渡」到下一個時顯示載入動畫。更多資訊請參見[分頁查詢 (Paginated Queries)](./paginated-queries.md)。 + +```tsx +const result = useQuery({ + queryKey: ['todos', id], + queryFn: () => fetch(`/todos/${id}`), + placeholderData: (previousData, previousQuery) => previousData, +}) +``` + +### 從快取取得預留位置資料 + +在某些情況下,您可能可以從另一個查詢的快取結果中為當前查詢提供預留位置資料。一個很好的例子是:從部落格文章列表查詢的快取資料中搜尋文章的預覽版本,然後將其用作單篇文章查詢的預留位置資料: + +```tsx +const result = useQuery({ + queryKey: ['blogPost', blogPostId], + queryFn: () => fetch(`/blogPosts/${blogPostId}`), + placeholderData: () => { + // 使用來自 'blogPosts' 查詢的較小/預覽版本的部落格文章 + // 作為此部落格文章查詢的預留位置資料 + return queryClient + .getQueryData(['blogPosts']) + ?.find((d) => d.id === blogPostId) + }, +}) +``` diff --git a/docs/zh-hant/framework/vue/guides/prefetching.md b/docs/zh-hant/framework/vue/guides/prefetching.md new file mode 100644 index 00000000000..5f29fe5e346 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/prefetching.md @@ -0,0 +1,49 @@ +--- +source-updated-at: '2024-03-03T09:42:51.000Z' +translation-updated-at: '2025-05-08T20:18:14.837Z' +id: prefetching +title: 預獲取 +--- + +如果你夠幸運,可能已經足夠了解使用者行為,能在他們需要資料之前預先取得!這種情況下,可以使用 `prefetchQuery` 方法預先取得查詢結果並存入快取: + +[//]: # 'ExamplePrefetching' + +```tsx +const prefetchTodos = async () => { + // 此查詢結果會像一般查詢一樣被快取 + await queryClient.prefetchQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) +} +``` + +[//]: # 'ExamplePrefetching' + +- 若快取中已有此查詢的**最新**資料,則不會重新取得 +- 若傳入 `staleTime` 參數(例如 `prefetchQuery({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })`)且資料已超過指定的 `staleTime` 時限,則會重新取得查詢 +- 若預取的查詢沒有對應的 `useQuery` 實例,則會在 `gcTime` 設定的時間後被刪除並進行垃圾回收 + +## 預取無限查詢 (Prefetching Infinite Queries) + +無限查詢 (Infinite Queries) 可像一般查詢一樣預取。預設只會預取第一頁資料,並儲存在指定的 QueryKey 下。若要預取多頁資料,可使用 `pages` 選項,此時需同時提供 `getNextPageParam` 函數: + +[//]: # 'ExampleInfiniteQuery' + +```tsx +const prefetchProjects = async () => { + // 此查詢結果會像一般查詢一樣被快取 + await queryClient.prefetchInfiniteQuery({ + queryKey: ['projects'], + queryFn: fetchProjects, + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => lastPage.nextCursor, + pages: 3, // 預取前 3 頁 + }) +} +``` + +[//]: # 'ExampleInfiniteQuery' + +上述程式碼會依序嘗試預取 3 頁資料,並對每頁執行 `getNextPageParam` 以決定下一頁的預取參數。若 `getNextPageParam` 回傳 `undefined`,則會停止預取。 diff --git a/docs/zh-hant/framework/vue/guides/queries.md b/docs/zh-hant/framework/vue/guides/queries.md new file mode 100644 index 00000000000..5a3b23f30bb --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/queries.md @@ -0,0 +1,109 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:40.671Z' +id: queries +title: 查詢 +--- + +## 查詢基礎 + +查詢是與**唯一鍵**綁定的非同步資料來源的宣告式依賴。查詢可與任何基於 Promise 的方法(包括 GET 和 POST 方法)一起使用,以從伺服器獲取資料。若您的方法會修改伺服器上的資料,建議改用[變更 (Mutations)](./mutations.md)。 + +要在元件或自訂鉤子中訂閱查詢,請呼叫 `useQuery` 鉤子並至少提供以下參數: + +- **查詢的唯一鍵** +- 一個回傳 Promise 的函式,該 Promise 會: + - 解析資料,或 + - 拋出錯誤 + +```ts +import { useQuery } from '@tanstack/vue-query' + +const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +``` + +您提供的**唯一鍵**會在內部用於重新獲取、快取及在應用程式中共享查詢。 + +`useQuery` 回傳的查詢結果包含所有與查詢相關的資訊,可供模板化或任何其他資料使用: + +```tsx +const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList }) +``` + +`result` 物件包含幾個非常重要的狀態,您需要了解這些狀態才能有效使用。查詢在任一時刻只能處於以下其中一種狀態: + +- `isPending` 或 `status === 'pending'` - 查詢尚未取得資料 +- `isError` 或 `status === 'error'` - 查詢遇到錯誤 +- `isSuccess` 或 `status === 'success'` - 查詢成功且資料可用 + +除了這些主要狀態外,根據查詢的狀態還可獲取更多資訊: + +- `error` - 若查詢處於 `isError` 狀態,可透過 `error` 屬性取得錯誤資訊。 +- `data` - 若查詢處於 `isSuccess` 狀態,可透過 `data` 屬性取得資料。 +- `isFetching` - 在任何狀態下,若查詢正在獲取資料(包括背景重新獲取),`isFetching` 會為 `true`。 + +對於**大多數**查詢,通常只需檢查 `isPending` 狀態,接著檢查 `isError` 狀態,最後假設資料已可用並渲染成功狀態: + +```vue + + + +``` + +若不喜歡布林值,也可以使用 `status` 狀態: + +```vue + + + +``` + +若您在存取 `data` 前已檢查過 `pending` 和 `error`,TypeScript 也會正確縮小 `data` 的型別範圍。 + +### 獲取狀態 (FetchStatus) + +除了 `status` 欄位外,您還會獲得一個額外的 `fetchStatus` 屬性,其選項如下: + +- `fetchStatus === 'fetching'` - 查詢正在獲取資料。 +- `fetchStatus === 'paused'` - 查詢想要獲取資料,但已暫停。詳情請參閱[網路模式 (Network Mode)](./network-mode.md) 指南。 +- `fetchStatus === 'idle'` - 查詢目前未進行任何操作。 + +### 為何有兩種不同狀態? + +背景重新獲取和過期資料重新驗證邏輯使得 `status` 和 `fetchStatus` 的所有組合都有可能出現。例如: + +- 處於 `success` 狀態的查詢通常會處於 `idle` 獲取狀態,但若正在進行背景重新獲取,也可能處於 `fetching` 狀態。 +- 剛掛載且無資料的查詢通常會處於 `pending` 狀態和 `fetching` 獲取狀態,但若無網路連線,也可能處於 `paused` 狀態。 + +因此請記住,查詢可能處於 `pending` 狀態但實際上並未獲取資料。作為經驗法則: + +- `status` 提供關於 `data` 的資訊:我們是否有資料? +- `fetchStatus` 提供關於 `queryFn` 的資訊:它是否正在執行? diff --git a/docs/zh-hant/framework/vue/guides/query-cancellation.md b/docs/zh-hant/framework/vue/guides/query-cancellation.md new file mode 100644 index 00000000000..ab6b6f7ff60 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/query-cancellation.md @@ -0,0 +1,27 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:51.476Z' +id: query-cancellation +title: Query Cancellation +ref: docs/zh-hant/framework/react/guides/query-cancellation.md +--- + +[//]: # 'Example7' + +```ts +const query = useQuery({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const resp = await fetch('/todos', { signal }) + return resp.json() + }, +}) + +const queryClient = useQueryClient() + +function onButtonClick() { + queryClient.cancelQueries({ queryKey: ['todos'] }) +} +``` + +[//]: # 'Example7' diff --git a/docs/zh-hant/framework/vue/guides/query-functions.md b/docs/zh-hant/framework/vue/guides/query-functions.md new file mode 100644 index 00000000000..9a69ee7671a --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/query-functions.md @@ -0,0 +1,24 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:51.456Z' +id: query-functions +title: Query Functions +ref: docs/zh-hant/framework/react/guides/query-functions.md +--- + +[//]: # 'Example4' + +```js +const result = useQuery({ + queryKey: ['todos', { status, page }], + queryFn: fetchTodoList, +}) + +// Access the key, status and page variables in your query function! +function fetchTodoList({ queryKey }) { + const [_key, { status, page }] = queryKey + return new Promise() +} +``` + +[//]: # 'Example4' diff --git a/docs/zh-hant/framework/vue/guides/query-invalidation.md b/docs/zh-hant/framework/vue/guides/query-invalidation.md new file mode 100644 index 00000000000..a014d8a4989 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/query-invalidation.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:51.437Z' +id: query-invalidation +title: Query Invalidation +ref: docs/zh-hant/framework/react/guides/query-invalidation.md +replace: + react-query: vue-query +--- diff --git a/docs/zh-hant/framework/vue/guides/query-keys.md b/docs/zh-hant/framework/vue/guides/query-keys.md new file mode 100644 index 00000000000..afa17cd76d1 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/query-keys.md @@ -0,0 +1,21 @@ +--- +source-updated-at: '2024-12-03T07:43:25.000Z' +translation-updated-at: '2025-05-08T20:17:51.417Z' +id: query-keys +title: Query Keys +ref: docs/zh-hant/framework/react/guides/query-keys.md +--- + +[//]: # 'Example5' + +```js +function useTodos(todoId) { + const queryKey = ['todos', todoId] + return useQuery({ + queryKey, + queryFn: () => fetchTodoById(todoId.value), + }) +} +``` + +[//]: # 'Example5' diff --git a/docs/zh-hant/framework/vue/guides/query-options.md b/docs/zh-hant/framework/vue/guides/query-options.md new file mode 100644 index 00000000000..dcd050bfb42 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/query-options.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:17:51.409Z' +id: query-options +title: Query Options +ref: docs/zh-hant/framework/react/guides/query-options.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/guides/query-retries.md b/docs/zh-hant/framework/vue/guides/query-retries.md new file mode 100644 index 00000000000..5e2681c62fa --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/query-retries.md @@ -0,0 +1,61 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:18:13.970Z' +id: query-retries +title: 查詢重試 +--- + +當 `useQuery` 查詢失敗(查詢函數拋出錯誤)時,TanStack Query 會自動重試該查詢,前提是該查詢的請求尚未達到最大連續重試次數(預設為 `3`)或提供了決定是否允許重試的函數。 + +您可以在全域層級和單一查詢層級上配置重試行為: + +- 設定 `retry = false` 將停用重試功能。 +- 設定 `retry = 6` 會在顯示函數拋出的最終錯誤前,重試失敗的請求 6 次。 +- 設定 `retry = true` 會無限次重試失敗的請求。 +- 設定 `retry = (failureCount, error) => ...` 可根據請求失敗的原因自訂重試邏輯。 + +> 在伺服器端,重試次數預設為 `0`,以確保伺服器渲染 (server rendering) 的速度最快。 + +```tsx +import { useQuery } from '@tanstack/vue-query' + +// 讓特定查詢重試指定次數 +const result = useQuery({ + queryKey: ['todos', 1], + queryFn: fetchTodoListPage, + retry: 10, // 會在顯示錯誤前重試失敗的請求 10 次 +}) +``` + +> 提示:在最後一次重試嘗試之前,`error` 屬性的內容將屬於 `useQuery` 回應的 `failureReason` 屬性。因此,在上述範例中,任何錯誤內容在前 9 次重試嘗試(總共 10 次嘗試)中都會屬於 `failureReason` 屬性,如果所有重試嘗試後錯誤仍然存在,最終這些內容會在最後一次嘗試後屬於 `error` 屬性。 + +## 重試延遲 + +預設情況下,TanStack Query 不會在請求失敗後立即重試。按照標準做法,每次重試嘗試會逐漸增加退避延遲 (back-off delay)。 + +預設的 `retryDelay` 設定為每次嘗試加倍(從 `1000` 毫秒開始),但不超過 30 秒: + +```ts +import { VueQueryPlugin } from '@tanstack/vue-query' + +const vueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, + }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +雖然不建議這樣做,但您顯然可以在 Plugin 和單一查詢選項中覆寫 `retryDelay` 函數/整數。如果設定為整數而非函數,延遲時間將始終相同: + +```tsx +const result = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodoList, + retryDelay: 1000, // 無論重試多少次,總是等待 1000 毫秒後重試 +}) +``` diff --git a/docs/zh-hant/framework/vue/guides/scroll-restoration.md b/docs/zh-hant/framework/vue/guides/scroll-restoration.md new file mode 100644 index 00000000000..d26a8ae0eed --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/scroll-restoration.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:43.481Z' +id: scroll-restoration +title: Scroll Restoration +ref: docs/zh-hant/framework/react/guides/scroll-restoration.md +--- diff --git a/docs/zh-hant/framework/vue/guides/ssr.md b/docs/zh-hant/framework/vue/guides/ssr.md new file mode 100644 index 00000000000..08bedb3fcff --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/ssr.md @@ -0,0 +1,260 @@ +--- +source-updated-at: '2024-09-01T21:12:58.000Z' +translation-updated-at: '2025-05-08T20:18:57.153Z' +id: ssr +title: SSR 與 Nuxt +--- + +Vue Query 支援在伺服器端預先擷取多個查詢,並將這些查詢 _脫水 (dehydrate)_ 至 queryClient。這表示伺服器可以預先渲染頁面載入時立即可見的標記,一旦 JS 準備就緒,Vue Query 就能使用函式庫的完整功能來升級或 _水合 (hydrate)_ 這些查詢。這包括在客戶端重新擷取那些自伺服器渲染後已過時的查詢。 + +## 使用 Nuxt.js + +### Nuxt 3 + +首先在 `plugins` 目錄下建立 `vue-query.ts` 檔案,內容如下: + +```ts +import type { + DehydratedState, + VueQueryPluginOptions, +} from '@tanstack/vue-query' +import { + VueQueryPlugin, + QueryClient, + hydrate, + dehydrate, +} from '@tanstack/vue-query' +// Nuxt 3 app aliases +import { defineNuxtPlugin, useState } from '#imports' + +export default defineNuxtPlugin((nuxt) => { + const vueQueryState = useState('vue-query') + + // 在此修改你的 Vue Query 全域設定 + const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: 5000 } }, + }) + const options: VueQueryPluginOptions = { queryClient } + + nuxt.vueApp.use(VueQueryPlugin, options) + + if (import.meta.server) { + nuxt.hooks.hook('app:rendered', () => { + vueQueryState.value = dehydrate(queryClient) + }) + } + + if (import.meta.client) { + hydrate(queryClient, vueQueryState.value) + } +}) +``` + +現在你可以使用 `onServerPrefetch` 在頁面中預先擷取資料。 + +- 使用 `queryClient.prefetchQuery` 或 `suspense` 預先擷取所有需要的查詢 + +```ts +export default defineComponent({ + setup() { + const { data, suspense } = useQuery({ + queryKey: ['test'], + queryFn: fetcher, + }) + + onServerPrefetch(async () => { + await suspense() + }) + + return { data } + }, +}) +``` + +### Nuxt 2 + +首先在 `plugins` 目錄下建立 `vue-query.js` 檔案,內容如下: + +```js +import Vue from 'vue' +import { VueQueryPlugin, QueryClient, hydrate } from '@tanstack/vue-query' + +export default (context) => { + // 在此修改你的 Vue Query 全域設定 + const queryClient = new QueryClient({ + defaultOptions: { queries: { staleTime: 5000 } }, + }) + + if (process.server) { + context.ssrContext.VueQuery = queryClient + } + + if (process.client) { + Vue.use(VueQueryPlugin, { queryClient }) + + if (context.nuxtState && context.nuxtState.vueQueryState) { + hydrate(queryClient, context.nuxtState.vueQueryState) + } + } +} +``` + +將此插件加入你的 `nuxt.config.js` + +```js +module.exports = { + ... + plugins: ['~/plugins/vue-query.js'], +} +``` + +現在你可以使用 `onServerPrefetch` 在頁面中預先擷取資料。 + +- 使用 `useContext` 取得 nuxt 上下文 +- 使用 `useQueryClient` 取得伺服器端的 `queryClient` 實例 +- 使用 `queryClient.prefetchQuery` 或 `suspense` 預先擷取所有需要的查詢 +- 將 `queryClient` 脫水至 `nuxtContext` + +```vue +// pages/todos.vue + + + +``` + +如範例所示,可以預先擷取部分查詢,並讓其他查詢在 queryClient 上擷取。這表示你可以透過為特定查詢新增或移除 `prefetchQuery` 或 `suspense` 來控制伺服器渲染的內容。 + +## 使用 Vite SSR + +將 VueQuery 客戶端狀態與 [vite-ssr](https://github.com/frandiox/vite-ssr) 同步,以便在 DOM 中序列化: + +```js +// main.js (入口點) +import App from './App.vue' +import viteSSR from 'vite-ssr/vue' +import { + QueryClient, + VueQueryPlugin, + hydrate, + dehydrate, +} from '@tanstack/vue-query' + +export default viteSSR(App, { routes: [] }, ({ app, initialState }) => { + // -- 這是 Vite SSR 的主要鉤子,每個請求呼叫一次 + + // 建立一個新的 VueQuery 客戶端 + const queryClient = new QueryClient() + + // 將 initialState 與客戶端狀態同步 + if (import.meta.env.SSR) { + // 指示如何在 SSR 期間存取和序列化 VueQuery 狀態 + initialState.vueQueryState = { toJSON: () => dehydrate(queryClient) } + } else { + // 在瀏覽器中重用現有狀態 + hydrate(queryClient, initialState.vueQueryState) + } + + // 掛載並將客戶端提供給應用程式元件 + app.use(VueQueryPlugin, { queryClient }) +}) +``` + +然後,使用 Vue 的 `onServerPrefetch` 在任何元件中呼叫 VueQuery: + +```html + + + + +``` + +## 提示、技巧與注意事項 + +### 僅成功的查詢會包含在脫水過程中 + +任何有錯誤的查詢會自動排除在脫水過程之外。這表示預設行為是假裝這些查詢從未在伺服器上載入,通常會顯示載入狀態,並在 queryClient 上重試查詢。無論錯誤為何,都會發生這種情況。 + +有時這種行為並不理想,你可能希望在特定錯誤或查詢時渲染帶有正確狀態碼的錯誤頁面。在這些情況下,使用 `fetchQuery` 並捕獲任何錯誤以手動處理。 + +### 過時性是從查詢在伺服器上擷取時開始計算 + +查詢是否過時取決於 `dataUpdatedAt` 的時間。這裡需要注意的是,伺服器需要有正確的時間才能正常工作,但使用的是 UTC 時間,因此時區不會影響這一點。 + +由於 `staleTime` 預設為 `0`,查詢預設會在頁面載入時在背景重新擷取。你可能希望使用較高的 `staleTime` 來避免這種雙重擷取,特別是如果你沒有快取標記。 + +這種過時查詢的重新擷取非常適合在 CDN 中快取標記!你可以將頁面本身的快取時間設置得較高,以避免在伺服器上重新渲染頁面,但將查詢的 `staleTime` 設置得較低,以確保在用戶訪問頁面時立即在背景重新擷取資料。也許你想將頁面快取一週,但如果資料超過一天,則在頁面載入時自動重新擷取? + +### 伺服器上的高記憶體消耗 + +如果你為每個請求建立 `QueryClient`,Vue Query 會為此客戶端建立隔離的快取,該快取會在 `gcTime` 期間保留在記憶體中。如果在該期間內有大量請求,可能會導致伺服器上的高記憶體消耗。 + +在伺服器上,`gcTime` 預設為 `Infinity`,這會停用手動垃圾回收,並在請求完成後自動清除記憶體。如果你明確設置了非 Infinity 的 `gcTime`,則需要負責提前清除快取。 + +為了在不再需要時清除快取並降低記憶體消耗,你可以在請求處理完畢並將脫水狀態發送到客戶端後,呼叫 [`queryClient.clear()`](../../../../reference/QueryClient/#queryclientclear)。 + +或者,你可以設置較小的 `gcTime`。 diff --git a/docs/zh-hant/framework/vue/guides/suspense.md b/docs/zh-hant/framework/vue/guides/suspense.md new file mode 100644 index 00000000000..502a8b288c3 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/suspense.md @@ -0,0 +1,56 @@ +--- +source-updated-at: '2024-04-02T22:46:31.000Z' +translation-updated-at: '2025-05-08T20:17:51.394Z' +id: suspense +title: Suspense +--- + +> 注意:Vue Query 的 Suspense 模式目前處於實驗階段,與 Vue 本身的 Suspense 功能相同。這些 API 將會變更,除非您將 Vue 和 Vue Query 的版本鎖定在相互兼容的修補版本,否則不應在生產環境中使用。 + +Vue Query 也能與 Vue 的新 [Suspense](https://vuejs.org/guide/built-ins/suspense.html) API 搭配使用。 + +為此,您需要使用 Vue 提供的 `Suspense` 元件來包裹您的可暫停元件 (suspendable component)。 + +```vue + + + +``` + +並將可暫停元件中的 `setup` 函式改為 `async`。接著您就能使用 `vue-query` 提供的非同步 `suspense` 函式。 + +```vue + +``` + +## 渲染時擷取 (Fetch-on-render) vs 隨擷取隨渲染 (Render-as-you-fetch) + +預設情況下,Vue Query 在 `suspense` 模式下無需額外配置,就能完美作為**渲染時擷取 (Fetch-on-render)** 解決方案。這意味著當您的元件嘗試掛載時,它們會觸發查詢擷取並暫停,但只有在您導入並掛載它們後才會執行。如果您想進一步提升並實現**隨擷取隨渲染 (Render-as-you-fetch)** 模式,我們建議在路由回調和/或用戶互動事件上實作[預擷取 (Prefetching)](../prefetching),以便在元件掛載前開始載入查詢,甚至能在您開始導入或掛載其父元件之前就進行。 diff --git a/docs/zh-hant/framework/vue/guides/testing.md b/docs/zh-hant/framework/vue/guides/testing.md new file mode 100644 index 00000000000..d67bd554b69 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/testing.md @@ -0,0 +1,6 @@ +--- +source-updated-at: '2024-01-25T20:57:22.000Z' +translation-updated-at: '2025-05-08T20:17:24.525Z' +id: testing +title: Testing +--- diff --git a/docs/zh-hant/framework/vue/guides/updates-from-mutation-responses.md b/docs/zh-hant/framework/vue/guides/updates-from-mutation-responses.md new file mode 100644 index 00000000000..20819440307 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/updates-from-mutation-responses.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:24.506Z' +id: updates-from-mutation-responses +title: Updates from Mutation Responses +ref: docs/zh-hant/framework/react/guides/updates-from-mutation-responses.md +--- diff --git a/docs/zh-hant/framework/vue/guides/window-focus-refetching.md b/docs/zh-hant/framework/vue/guides/window-focus-refetching.md new file mode 100644 index 00000000000..98bf3b505c2 --- /dev/null +++ b/docs/zh-hant/framework/vue/guides/window-focus-refetching.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:24.487Z' +id: window-focus-refetching +title: Window Focus Refetching +ref: docs/zh-hant/framework/react/guides/window-focus-refetching.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + +[//]: # 'Example' + +```js +const vueQueryPluginOptions: VueQueryPluginOptions = { + queryClientConfig: { + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, + }, +} +app.use(VueQueryPlugin, vueQueryPluginOptions) +``` + +[//]: # 'Example' +[//]: # 'ReactNative' +[//]: # 'ReactNative' diff --git a/docs/zh-hant/framework/vue/installation.md b/docs/zh-hant/framework/vue/installation.md new file mode 100644 index 00000000000..e19684ae69f --- /dev/null +++ b/docs/zh-hant/framework/vue/installation.md @@ -0,0 +1,71 @@ +--- +source-updated-at: '2024-08-19T08:36:40.000Z' +translation-updated-at: '2025-05-08T20:15:42.275Z' +id: installation +title: 安裝 +--- + +## 安裝 + +您可以透過 [NPM](https://npmjs.com) 安裝 Vue Query。 + +### NPM + +```bash +npm i @tanstack/vue-query +``` + +或 + +```bash +pnpm add @tanstack/vue-query +``` + +或 + +```bash +yarn add @tanstack/vue-query +``` + +或 + +```bash +bun add @tanstack/vue-query +``` + +> 想在下載前試用看看嗎?試試這個 [基礎範例](../examples/basic)! + +Vue Query 相容於 Vue 2.x 和 3.x + +> 如果您使用的是 Vue 2.6,請確保同時設定 [@vue/composition-api](https://github.com/vuejs/composition-api) + +### Vue Query 初始化 + +在使用 Vue Query 之前,您需要使用 `VueQueryPlugin` 進行初始化 + +```tsx +import { VueQueryPlugin } from '@tanstack/vue-query' + +app.use(VueQueryPlugin) +``` + +### 搭配 ` + + +``` diff --git a/docs/zh-hant/framework/vue/overview.md b/docs/zh-hant/framework/vue/overview.md new file mode 100644 index 00000000000..2bc729532a3 --- /dev/null +++ b/docs/zh-hant/framework/vue/overview.md @@ -0,0 +1,47 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-08T20:15:53.216Z' +id: overview +title: 概述 +--- + +TanStack Query(前身為 Vue Query)常被稱為網頁應用程式中缺失的資料獲取函式庫,但用更技術性的術語來說,它讓你在網頁應用中**獲取、快取、同步和更新伺服器狀態 (server state)** 變得輕而易舉。 + +## 動機 + +大多數核心網頁框架**並未**提供一套全面的資料獲取或更新方法。因此,開發者最終要麼建立封裝了嚴格資料獲取觀點的元框架 (meta-frameworks),要麼發明自己的資料獲取方式。這通常意味著拼湊基於元件的狀態和副作用,或者使用更通用的狀態管理函式庫來儲存並在應用中提供非同步資料。 + +雖然大多數傳統的狀態管理函式庫非常適合處理客戶端狀態 (client state),但它們**在處理非同步或伺服器狀態 (server state) 時表現不佳**。這是因為**伺服器狀態完全不同**。首先,伺服器狀態: + +- 遠端儲存在你可能無法控制或擁有的位置 +- 需要非同步 API 來獲取和更新 +- 意味著共享所有權,可能在你不知情的情況下被他人更改 +- 如果不小心,可能會在你的應用中變得「過時 (out of date)」 + +一旦你理解了應用中伺服器狀態的本質,**隨著開發進展,更多挑戰會接踵而至**,例如: + +- 快取 (caching)...(可能是程式設計中最難的事情) +- 將對相同資料的多個請求去重 (deduping) 為單一請求 +- 在背景更新「過時」的資料 +- 判斷資料何時「過時」 +- 盡快反映資料的更新 +- 效能優化,如分頁 (pagination) 和懶加載資料 (lazy loading data) +- 管理伺服器狀態的記憶體和垃圾回收 (garbage collection) +- 透過結構共享 (structural sharing) 記憶化 (memoizing) 查詢結果 + +如果你對這份清單不感到頭疼,那可能意味著你已經解決了所有伺服器狀態的問題,值得頒個獎。然而,如果你像大多數人一樣,要麼尚未解決所有或大部分這些挑戰,而我們才剛觸及表面! + +TanStack Query 無疑是管理伺服器狀態的*最佳*函式庫之一。它**開箱即用,無需配置**,並且可以根據應用增長的需求進行客製化。 + +TanStack Query 讓你能夠戰勝並克服*伺服器狀態*的棘手挑戰,在它開始控制你之前掌控你的應用資料。 + +從更技術的角度來看,TanStack Query 可能會: + +- 幫助你從應用中移除**許多**複雜且難以理解的程式碼,並僅用幾行 TanStack Query 邏輯替代 +- 使你的應用更易維護,更容易新增功能,而無需擔心連接新的伺服器狀態資料來源 +- 通過讓你的應用感覺比以往更快、更靈敏,直接影響終端用戶體驗 +- 可能幫助你節省頻寬並提高記憶體效能 + +## 你說服我了,接下來呢? + +- 透過我們詳盡的[入門指南](../installation)和[API 參考](../reference/useQuery),按照自己的步調學習 Vue Query diff --git a/docs/zh-hant/framework/vue/plugins/broadcastQueryClient.md b/docs/zh-hant/framework/vue/plugins/broadcastQueryClient.md new file mode 100644 index 00000000000..8ee317da7a1 --- /dev/null +++ b/docs/zh-hant/framework/vue/plugins/broadcastQueryClient.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:17:24.468Z' +id: broadcastQueryClient +title: broadcastQueryClient (Experimental) +ref: docs/zh-hant/framework/react/plugins/broadcastQueryClient.md +replace: + react-query: vue-query +--- diff --git a/docs/zh-hant/framework/vue/plugins/createPersister.md b/docs/zh-hant/framework/vue/plugins/createPersister.md new file mode 100644 index 00000000000..ca89236ef65 --- /dev/null +++ b/docs/zh-hant/framework/vue/plugins/createPersister.md @@ -0,0 +1,133 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:17:59.002Z' +id: createPersister +title: createPersister (實驗性) +--- + +## 安裝 + +此工具程式以獨立套件形式提供,可透過 `'@tanstack/query-persist-client-core'` 匯入使用。 + +```bash +npm install @tanstack/query-persist-client-core +``` + +或 + +```bash +pnpm add @tanstack/query-persist-client-core +``` + +或 + +```bash +yarn add @tanstack/query-persist-client-core +``` + +或 + +```bash +bun add @tanstack/query-persist-client-core +``` + +## 使用方式 + +- 匯入 `experimental_createPersister` 函式 +- 建立一個新的 `experimental_createPersister` + - 可傳入任何符合 `AsyncStorage` 或 `Storage` 介面的 `storage` +- 將該 `persister` 作為選項傳遞給你的 Query。可透過傳遞至 `QueryClient` 的 `defaultOptions` 或任何 `useQuery` hook 實例來實現。 + - 若將此 `persister` 設為 `defaultOptions`,所有查詢將被持久化至提供的 `storage` 中。你還可透過傳遞 `filters` 進一步縮小範圍。與 `persistClient` 外掛不同,這不會將整個 query client 作為單一項目持久化,而是分別持久化每個查詢。查詢雜湊值 (query hash) 將被用作鍵名。 + - 若將此 `persister` 提供給單一 `useQuery` hook,則僅該查詢會被持久化。 + +如此一來,你無需儲存整個 `QueryClient`,而是選擇應用程式中值得持久化的內容。每個查詢會以懶載入方式還原(當查詢首次被使用時)並持久化(在每次執行 `queryFn` 後),因此無需進行節流處理。還原查詢後也會遵守 `staleTime`,若資料被視為過時 (stale),將在還原後立即重新擷取。若資料為最新 (fresh),則不會執行 `queryFn`。 + +從記憶體中垃圾回收查詢**不會**影響持久化的資料。這意味著查詢可在記憶體中保留較短時間以提升**記憶體效率**。當下次使用時,它們將直接從持久化儲存中再次還原。 + +```tsx +import { QueryClient } from '@tanstack/vue-query' +import { experimental_createPersister } from '@tanstack/query-persist-client-core' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 30, // 30 秒 + persister: experimental_createPersister({ + storage: localStorage, + maxAge: 1000 * 60 * 60 * 12, // 12 小時 + }), + }, + }, +}) +``` + +### 調整後的預設值 + +`createPersister` 外掛在技術上包裝了 `queryFn`,因此若 `queryFn` 未執行則不會進行還原。在此意義上,它扮演著查詢與網路之間的快取層。因此,當使用 persister 時,`networkMode` 預設為 `'offlineFirst'`,以便即使沒有網路連線時也能從持久化儲存中還原資料。 + +## API + +### `experimental_createPersister` + +```tsx +experimental_createPersister(options: StoragePersisterOptions) +``` + +#### `選項` + +```tsx +export interface StoragePersisterOptions { + /** 用於從快取中設定和檢索項目的儲存客戶端。 + * 對於 SSR 請傳入 `undefined`。 + */ + storage: AsyncStorage | Storage | undefined | null + /** + * 如何將資料序列化至儲存。 + * @預設值 `JSON.stringify` + */ + serialize?: (persistedQuery: PersistedQuery) => string + /** + * 如何從儲存中反序列化資料。 + * @預設值 `JSON.parse` + */ + deserialize?: (cachedString: string) => PersistedQuery + /** + * 用於強制使現有快取失效的唯一字串, + * 若它們未共用相同的 buster 字串 + */ + buster?: string + /** + * 快取允許的最大存活時間(毫秒)。 + * 若發現持久化的快取存活時間超過此設定, + * 將被捨棄 + * @預設值 24 小時 + */ + maxAge?: number + /** + * 儲存鍵名的前綴。 + * 儲存鍵名由前綴和查詢雜湊值組成,格式為 `prefix-queryHash`。 + */ + prefix?: string + /** + * 用於篩選應持久化的查詢的過濾條件。 + */ + filters?: QueryFilters +} + +interface AsyncStorage { + getItem: (key: string) => Promise + setItem: (key: string, value: string) => Promise + removeItem: (key: string) => Promise +} +``` + +預設選項為: + +```tsx +{ + prefix = 'tanstack-query', + maxAge = 1000 * 60 * 60 * 24, + serialize = JSON.stringify, + deserialize = JSON.parse, +} +``` diff --git a/docs/zh-hant/framework/vue/quick-start.md b/docs/zh-hant/framework/vue/quick-start.md new file mode 100644 index 00000000000..f81f1af86a9 --- /dev/null +++ b/docs/zh-hant/framework/vue/quick-start.md @@ -0,0 +1,57 @@ +--- +source-updated-at: '2024-04-02T22:46:31.000Z' +translation-updated-at: '2025-05-08T20:15:30.240Z' +id: quick-start +title: 快速開始 +--- + +這段程式碼片段簡要說明了 Vue Query 的 3 個核心概念: + +- [查詢 (Queries)](./guides/queries.md) +- [變更 (Mutations)](./guides/mutations.md) +- [查詢失效 (Query Invalidation)](./guides/query-invalidation.md) + +如果您需要一個完整可運行的範例,請參考我們的 [基本 codesandbox 範例](../examples/basic) + +```vue + + + +``` + +這三個概念構成了 Vue Query 的大部分核心功能。接下來的文件章節將會詳細說明每個核心概念。 diff --git a/docs/zh-hant/framework/vue/reactivity.md b/docs/zh-hant/framework/vue/reactivity.md new file mode 100644 index 00000000000..2e653ee7bf4 --- /dev/null +++ b/docs/zh-hant/framework/vue/reactivity.md @@ -0,0 +1,171 @@ +--- +source-updated-at: '2025-05-06T07:46:04.000Z' +translation-updated-at: '2025-05-08T20:16:21.619Z' +id: reactivity +title: 響應式 +--- + +Vue 採用 [信號模式 (the signals paradigm)](https://vuejs.org/guide/extras/reactivity-in-depth.html#connection-to-signals) 來處理和追蹤響應性。此系統的一個關鍵特性是,響應系統僅在特定監聽的響應屬性上觸發更新。這導致的一個結果是,您還需要確保當查詢所消耗的值更新時,查詢也會隨之更新。 + +# 保持查詢的響應性 + +當為查詢創建一個組合式函數時,您的第一個選擇可能是這樣寫: + +```ts +export function useUserProjects(userId: string) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(userId), + ); +} +``` + +我們可能會這樣使用這個組合式函數: + +```ts +// 響應式的用戶 ID ref。 +const userId = ref('1') +// 獲取用戶 1 的專案。 +const { data: projects } = useUserProjects(userId.value) + +const onChangeUser = (newUserId: string) => { + // 修改 userId,但查詢不會重新獲取數據。 + userId.value = newUserId +} +``` + +這段代碼不會按預期工作。這是因為我們直接從 `userId` ref 中提取了值。Vue-query 沒有追蹤 `userId` `ref`,因此它無法知道值何時發生變化。 + +幸運的是,修復這個問題非常簡單。必須讓查詢鍵中的值可被追蹤。我們可以直接在組合式函數中接受 `ref` 並將其放入查詢鍵中: + +```ts +export function useUserProjects(userId: Ref) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(userId.value), + ); +} +``` + +現在,當 `userId` 變化時,查詢會重新獲取數據。 + +```ts +const onChangeUser = (newUserId: string) => { + // 查詢會用新的用戶 ID 重新獲取數據! + userId.value = newUserId +} +``` + +在 Vue Query 中,查詢鍵中的任何響應屬性都會自動追蹤變化。這使得 Vue Query 能夠在請求參數變化時重新獲取數據。 + +## 處理非響應式查詢 + +雖然可能性較小,但有時故意傳遞非響應式變量是有意為之的。例如,某些實體只需獲取一次,不需要追蹤,或者我們在突變後使查詢選項對象失效。如果我們使用上面定義的自定義組合式函數,這種情況下的使用方式會感覺有些不自然: + +```ts +const { data: projects } = useUserProjects(ref('1')) +``` + +我們必須創建一個中間的 `ref` 以讓參數類型兼容。我們可以做得更好。讓我們更新組合式函數,使其同時接受普通值和響應式值: + +```ts +export function useUserProjects(userId: MaybeRef) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(toValue(userId)), + ); +} +``` + +現在我們可以同時用普通值和 ref 來使用組合式函數: + +```ts +// 獲取用戶 1 的專案,userId 不預期會變化。 +const { data: projects } = useUserProjects('1') + +// 獲取用戶 1 的專案,查詢會響應 userId 的變化。 +const userId = ref('1') + +// 對 userId 做一些修改... + +// 查詢會根據 userId 的任何變化重新獲取數據。 +const { data: projects } = useUserProjects(userId) +``` + +## 在查詢中使用派生狀態 + +從另一個響應式狀態源派生新的響應式狀態是很常見的。通常,這個問題會在處理組件 props 時出現。假設我們的 `userId` 是一個傳遞給組件的 prop: + +```vue + +``` + +您可能會想在查詢中直接使用 prop: + +```ts +// 不會響應 props.userId 的變化。 +const { data: projects } = useUserProjects(props.userId) +``` + +然而,與第一個例子類似,這不是響應式的。對 `reactive` 變量的屬性訪問會導致失去響應性。我們可以通過 `computed` 讓派生狀態變為響應式來解決這個問題: + +```ts +const userId = computed(() => props.userId) + +// 響應 props.userId 的變化。 +const { data: projects } = useUserProjects(userId) +``` + +這可以按預期工作,但這個解決方案並不總是最優的。除了引入中間變量外,我們還創建了一個某種程度上不必要的記憶值。對於簡單屬性訪問的簡單情況,`computed` 是一個沒有實際收益的優化。在這些情況下,更合適的解決方案是使用 [響應式 getter (reactive getters)](https://blog.vuejs.org/posts/vue-3-3#better-getter-support-with-toref-and-tovalue)。響應式 getter 是簡單的函數,根據某些響應式狀態返回一個值,類似於 `computed` 的工作方式。與 `computed` 不同,響應式 getter 不會記憶其值,因此它是簡單屬性訪問的良好候選方案。 + +讓我們再次重構我們的組合式函數,但這次我們將讓它接受 `ref`、普通值或響應式 getter: + +```ts +export function useUserProjects(userId: MaybeRefOrGetter) { + ... +} +``` + +讓我們調整使用方式,現在使用響應式 getter: + +```ts +// 響應 props.userId 的變化。不需要 `computed`! +const { data: projects } = useUserProjects(() => props.userId) +``` + +這為我們提供了一個簡潔的語法和所需的響應性,而沒有任何不必要的記憶開銷。 + +## 其他可追蹤的查詢選項 + +上面,我們只提到了一個可以追蹤響應依賴的查詢選項。然而,除了 `queryKey` 之外,`enabled` 也允許使用響應式值。這在您希望基於某些派生狀態控制查詢的獲取時非常有用: + +```ts +export function useUserProjects(userId: MaybeRef) { + return useQuery( + queryKey: ['userProjects', userId], + queryFn: () => api.fetchUserProjects(toValue(userId)), + enabled: () => userId.value === activeUserId.value, + ); +} +``` + +關於此選項的更多詳細信息可以在 [useQuery 參考](./reference/useQuery.md) 頁面找到。 + +## 不可變性 + +`useQuery` 的結果始終是不可變的。這對於性能和緩存目的是必要的。如果您需要突變從 `useQuery` 返回的值,必須創建數據的副本。 + +這種設計的一個影響是,將 `useQuery` 的值傳遞給雙向綁定(如 `v-model`)將不起作用。您必須在嘗試就地更新之前創建數據的可變副本。 + +# 關鍵要點 + +- `enabled` 和 `queryKey` 是兩個可以接受響應式值的查詢選項。 +- 傳遞查詢選項時,接受 Vue 中的三種類型的值:refs、普通值和響應式 getter。 +- 如果您希望查詢根據其消耗的值變化而響應,請確保這些值是響應式的(例如,直接將 refs 傳遞給查詢,或使用響應式 getter)。 +- 如果您不需要查詢是響應式的,請傳遞普通值。 +- 對於簡單的派生狀態(如屬性訪問),考慮使用響應式 getter 代替 `computed`。 +- `useQuery` 的結果始終是不可變的。 diff --git a/docs/zh-hant/framework/vue/reference/hydration.md b/docs/zh-hant/framework/vue/reference/hydration.md new file mode 100644 index 00000000000..e74729ff102 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/hydration.md @@ -0,0 +1,12 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:17:10.362Z' +id: hydration +title: hydration +ref: docs/zh-hant/framework/react/reference/hydration.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- + +[//]: # 'HydrationBoundary' +[//]: # 'HydrationBoundary' diff --git a/docs/zh-hant/framework/vue/reference/infiniteQueryOptions.md b/docs/zh-hant/framework/vue/reference/infiniteQueryOptions.md new file mode 100644 index 00000000000..088ec5e5c8d --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/infiniteQueryOptions.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:17:10.346Z' +id: infiniteQueryOptions +title: infiniteQueryOptions +ref: docs/zh-hant/framework/react/reference/infiniteQueryOptions.md +--- diff --git a/docs/zh-hant/framework/vue/reference/queryOptions.md b/docs/zh-hant/framework/vue/reference/queryOptions.md new file mode 100644 index 00000000000..fadcb4c3b2a --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/queryOptions.md @@ -0,0 +1,7 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.332Z' +id: queryOptions +title: queryOptions +ref: docs/zh-hant/framework/react/reference/queryOptions.md +--- diff --git a/docs/zh-hant/framework/vue/reference/useInfiniteQuery.md b/docs/zh-hant/framework/vue/reference/useInfiniteQuery.md new file mode 100644 index 00000000000..e8c59f06f7d --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useInfiniteQuery.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.313Z' +id: useInfiniteQuery +title: useInfiniteQuery +ref: docs/zh-hant/framework/react/reference/useInfiniteQuery.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useIsFetching.md b/docs/zh-hant/framework/vue/reference/useIsFetching.md new file mode 100644 index 00000000000..99d346ff63d --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useIsFetching.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.294Z' +id: useIsFetching +title: useIsFetching +ref: docs/zh-hant/framework/react/reference/useIsFetching.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useIsMutating.md b/docs/zh-hant/framework/vue/reference/useIsMutating.md new file mode 100644 index 00000000000..0ec7bfd2ca2 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useIsMutating.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.275Z' +id: useIsMutating +title: useIsMutating +ref: docs/zh-hant/framework/react/reference/useIsMutating.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useMutation.md b/docs/zh-hant/framework/vue/reference/useMutation.md new file mode 100644 index 00000000000..bf7c2f022c8 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useMutation.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.256Z' +id: useMutation +title: useMutation +ref: docs/zh-hant/framework/react/reference/useMutation.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useMutationState.md b/docs/zh-hant/framework/vue/reference/useMutationState.md new file mode 100644 index 00000000000..0dd6a0f17d5 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useMutationState.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.237Z' +id: useMutationState +title: useMutationState +ref: docs/zh-hant/framework/react/reference/useMutationState.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useQueries.md b/docs/zh-hant/framework/vue/reference/useQueries.md new file mode 100644 index 00000000000..e27acf3dd55 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useQueries.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.219Z' +id: useQueries +title: useQueries +ref: docs/zh-hant/framework/react/reference/useQueries.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useQuery.md b/docs/zh-hant/framework/vue/reference/useQuery.md new file mode 100644 index 00000000000..08776c730b7 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useQuery.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.200Z' +id: useQuery +title: useQuery +ref: docs/zh-hant/framework/react/reference/useQuery.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/reference/useQueryClient.md b/docs/zh-hant/framework/vue/reference/useQueryClient.md new file mode 100644 index 00000000000..cad445538f0 --- /dev/null +++ b/docs/zh-hant/framework/vue/reference/useQueryClient.md @@ -0,0 +1,9 @@ +--- +source-updated-at: '2024-01-26T03:31:25.000Z' +translation-updated-at: '2025-05-08T20:17:10.178Z' +id: useQueryClient +title: useQueryClient +ref: docs/zh-hant/framework/react/reference/useQueryClient.md +replace: + '@tanstack/react-query': '@tanstack/vue-query' +--- diff --git a/docs/zh-hant/framework/vue/typescript.md b/docs/zh-hant/framework/vue/typescript.md new file mode 100644 index 00000000000..8f94d6ddd88 --- /dev/null +++ b/docs/zh-hant/framework/vue/typescript.md @@ -0,0 +1,119 @@ +--- +source-updated-at: '2024-05-11T22:31:28.000Z' +translation-updated-at: '2025-05-08T20:16:30.716Z' +id: typescript +title: TypeScript +--- + +Vue Query 現已採用 **TypeScript** 編寫,確保函式庫與您的專案具備型別安全! + +注意事項: + +- 目前型別系統需使用 TypeScript **v4.7** 或更高版本 +- 此儲存庫中的型別變更視為**非破壞性變更**,通常以 **patch** 版號發布(否則每個型別增強都會變成主版本號!) +- **強烈建議將 vue-query 套件版本鎖定至特定 patch 版本**,並在升級時預期型別可能在任何版本間被修正或升級 +- Vue Query 的非型別相關公開 API 仍嚴格遵循語意化版本規範 + +## 型別推論 + +Vue Query 的型別通常能流暢推導,因此您無需自行添加型別註解 + +```tsx +const { data } = useQuery({ + // ^? const data: Ref | Ref + queryKey: ['test'], + queryFn: () => Promise.resolve(5), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUBNYRm8JABN6DInAC8KDNlx4AFAglw4nTocMA9APwG4Q7QGl0eAFxwA2lRjoWVALoAaa1t8ADFGFx0ASjUAPjgABXIQYAwAOigvCAAbbnQdAFYIgPFCCKA) + +```tsx +const { data } = useQuery({ + // ^? const data: Ref | Ref + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + select: (data) => data.toString(), +}) +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUBNYRm8JABN6DInAC8KDNlx4AFAglw4nTodNwAegH4DcIdoDS6PAC44AbSox0LKgF0ANDZ2+ABijK46AJRqAHxwAArkIMAYAHRQ3hAANtzoOgCskYHihhhZ6KwwEYoM0apxNfSpMBAAyjBQwIwA5lHFhJFAA) + +當您的 `queryFn` 有明確定義的返回型別時效果最佳。請注意,多數資料獲取函式庫預設返回 `any`,因此請確保將其提取到正確定型的函式: + +```tsx +const fetchGroups = (): Promise => + axios.get('/groups').then((response) => response.data) + +const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const data: Ref | Ref +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUKEiw49AB7AIqUuUpV5i1GPESYeMOjgBxcsjBwAvIjjAAJgC44jZCABGuIhImsIzeCXQYVgALEwgzZSsACgBKRwAFVWAMAB4wswBtAF0APksciThZBSUAOgBzQKiqTnLTMC0Y0phg9EYoqKh0VEhmdBj8uC6e3wxS23oGGK9xHz9rCYYiSxQMbFw8KKQhDYBpdDxHDKo68IaqLIAaOB38ADFGRwCg0PrlQmnxTk4i37gAPQA-EA) + +## 型別縮窄 + +Vue Query 使用[可區分聯合型別 (discriminated union type)](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) 作為查詢結果,透過 `status` 欄位與衍生的狀態布林標記進行區分。這讓您可以檢查例如 `success` 狀態來確保 `data` 已定義: + +```tsx +const { data, isSuccess } = reactive( + useQuery({ + queryKey: ['test'], + queryFn: () => Promise.resolve(5), + }), +) + +if (isSuccess) { + data + // ^? const data: number +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPQBuOAtAEcc+KgFgAUKEixEcKOnqsYwbuiKlylKr3RUA3BImsIzeEgAm9BgBo4wVAGVkrVulSp1AXjkKlK9AAUaFjCeAEA2lQwbjBUALq2AQCUcJ4AfHAACpr26AB08qgQADaqAQCsSVWGkiRwAfZOLm6oKQgScJ1wlgwSnJydAHoA-BKEEkA) + +## 錯誤欄位型別 + +錯誤型別預設為 `Error`,因這符合大多數使用者的預期。 + +```tsx +const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups }) +// ^? const error: Ref + +if (error.value instanceof Error) { + error.value + // ^? const error: Error +} +``` + +[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA) + +若您想拋出自訂錯誤,或非 `Error` 型別的內容,可指定錯誤欄位的型別: + +然而這會導致 `useQuery` 其他泛型參數的型別推論失效。通常不建議拋出非 `Error` 型別的內容,若您有像 `AxiosError` 這樣的子類別,可使用**型別縮窄**讓錯誤欄位更明確: + +### 註冊全域錯誤型別 + +TanStack Query v5 允許透過擴充 `Register` 介面來設定全域錯誤型別,無需在呼叫處指定泛型參數。這能確保型別推論仍正常運作,同時錯誤欄位會是指定的型別: + +## 查詢與異動鍵的型別定義 + +### 註冊查詢與異動鍵型別 + +類似於註冊[全域錯誤型別](#registering-a-global-error),您也可註冊全域的 `QueryKey` 與 `MutationKey` 型別。這讓您能為鍵值提供更符合應用程式層級的結構化型別,並在函式庫所有介面中保持型別化。請注意註冊的型別必須繼承 `Array` 型別,以確保鍵值維持陣列形式。 + +```ts +import '@tanstack/vue-query' + +type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray] + +declare module '@tanstack/vue-query' { + interface Register { + queryKey: QueryKey + mutationKey: QueryKey + } +} +``` + +## 使用 `skipToken` 實現型別安全的查詢停用 + +若使用 TypeScript,可透過 `skipToken` 停用查詢。這在需要根據條件停用查詢,同時保持查詢型別安全時特別有用。詳情請參閱[停用查詢](./guides/disabling-queries.md)指南。 diff --git a/docs/zh-hant/reference/InfiniteQueryObserver.md b/docs/zh-hant/reference/InfiniteQueryObserver.md new file mode 100644 index 00000000000..e2a80ab2777 --- /dev/null +++ b/docs/zh-hant/reference/InfiniteQueryObserver.md @@ -0,0 +1,28 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-08T20:14:40.009Z' +id: InfiniteQueryObserver +title: InfiniteQueryObserver +--- + +## `InfiniteQueryObserver` + +`InfiniteQueryObserver` 可用於觀察並切換不同的無限查詢 (infinite queries)。 + +```tsx +const observer = new InfiniteQueryObserver(queryClient, { + queryKey: ['posts'], + queryFn: fetchPosts, + getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, + getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor, +}) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +**選項** + +`InfiniteQueryObserver` 的選項與 [`useInfiniteQuery`](../../framework/react/reference/useInfiniteQuery) 完全相同。 diff --git a/docs/zh-hant/reference/MutationCache.md b/docs/zh-hant/reference/MutationCache.md new file mode 100644 index 00000000000..8d5e15ea3e0 --- /dev/null +++ b/docs/zh-hant/reference/MutationCache.md @@ -0,0 +1,100 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-08T20:15:07.964Z' +id: MutationCache +title: MutationCache +--- + +`MutationCache` 是用於儲存 mutations 的儲存空間。 + +**通常情況下,您不會直接與 MutationCache 互動,而是使用 `QueryClient`。** + +```tsx +import { MutationCache } from '@tanstack/react-query' + +const mutationCache = new MutationCache({ + onError: (error) => { + console.log(error) + }, + onSuccess: (data) => { + console.log(data) + }, +}) +``` + +它提供的方法有: + +- [`getAll`](#mutationcachegetall) +- [`subscribe`](#mutationcachesubscribe) +- [`clear`](#mutationcacheclear) + +**選項** + +- `onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` + - 選填 + - 當某個 mutation 遇到錯誤時,此函式將被呼叫。 + - 若您回傳一個 Promise,它將被等待 +- `onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` + - 選填 + - 當某個 mutation 成功時,此函式將被呼叫。 + - 若您回傳一個 Promise,它將被等待 +- `onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise | unknown` + - 選填 + - 當某個 mutation 完成(無論成功或失敗)時,此函式將被呼叫。 + - 若您回傳一個 Promise,它將被等待 +- `onMutate?: (variables: unknown, mutation: Mutation) => Promise | unknown` + - 選填 + - 在某個 mutation 執行前,此函式將被呼叫。 + - 若您回傳一個 Promise,它將被等待 + +## 全域回呼函式 + +`MutationCache` 上的 `onError`、`onSuccess`、`onSettled` 和 `onMutate` 回呼函式可用於在全域層級處理這些事件。它們與提供給 `QueryClient` 的 `defaultOptions` 不同,原因如下: + +- `defaultOptions` 可以被每個 Mutation 覆寫 — 而全域回呼函式 **總是** 會被呼叫。 +- `onMutate` 不允許回傳 context 值。 + +## `mutationCache.getAll` + +`getAll` 會回傳快取中的所有 mutations。 + +> 注意:大多數應用程式通常不需要使用此方法,但在罕見情況下需要獲取有關 mutation 的更多資訊時,它會派上用場。 + +```tsx +const mutations = mutationCache.getAll() +``` + +**回傳值** + +- `Mutation[]` + - 快取中的 Mutation 實例 + +## `mutationCache.subscribe` + +`subscribe` 方法可用於訂閱整個 mutation 快取,並在快取發生安全/已知的更新時(如 mutation 狀態變更或 mutations 被更新、新增或移除)收到通知。 + +```tsx +const callback = (event) => { + console.log(event.type, event.mutation) +} + +const unsubscribe = mutationCache.subscribe(callback) +``` + +**選項** + +- `callback: (mutation?: MutationCacheNotifyEvent) => void` + - 每當 mutation 快取更新時,此函式將被呼叫並傳入相關事件。 + +**回傳值** + +- `unsubscribe: Function => void` + - 此函式將取消訂閱 mutation 快取的回呼函式。 + +## `mutationCache.clear` + +`clear` 方法可用於完全清除快取並重新開始。 + +```tsx +mutationCache.clear() +``` diff --git a/docs/zh-hant/reference/QueriesObserver.md b/docs/zh-hant/reference/QueriesObserver.md new file mode 100644 index 00000000000..b71141263e1 --- /dev/null +++ b/docs/zh-hant/reference/QueriesObserver.md @@ -0,0 +1,26 @@ +--- +source-updated-at: '2024-04-22T08:38:13.000Z' +translation-updated-at: '2025-05-08T20:14:39.034Z' +id: QueriesObserver +title: QueriesObserver +--- + +## `QueriesObserver` + +`QueriesObserver` 可用於觀察多個查詢 (query)。 + +```tsx +const observer = new QueriesObserver(queryClient, [ + { queryKey: ['post', 1], queryFn: fetchPost }, + { queryKey: ['post', 2], queryFn: fetchPost }, +]) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +**選項** + +`QueriesObserver` 的選項與 [`useQueries`](../../framework/react/reference/useQueries) 的選項完全相同。 diff --git a/docs/zh-hant/reference/QueryCache.md b/docs/zh-hant/reference/QueryCache.md new file mode 100644 index 00000000000..87edf842e45 --- /dev/null +++ b/docs/zh-hant/reference/QueryCache.md @@ -0,0 +1,124 @@ +--- +source-updated-at: '2025-04-29T09:18:25.000Z' +translation-updated-at: '2025-05-08T20:15:12.165Z' +id: QueryCache +title: QueryCache +--- + +`QueryCache` 是 TanStack Query 的儲存機制,它儲存了所有查詢的資料、元資訊和狀態。 + +**通常情況下,您不會直接與 QueryCache 互動,而是針對特定快取使用 `QueryClient`。** + +```tsx +import { QueryCache } from '@tanstack/react-query' + +const queryCache = new QueryCache({ + onError: (error) => { + console.log(error) + }, + onSuccess: (data) => { + console.log(data) + }, + onSettled: (data, error) => { + console.log(data, error) + }, +}) + +const query = queryCache.find(['posts']) +``` + +它提供的方法包括: + +- [`find`](#querycachefind) +- [`findAll`](#querycachefindall) +- [`subscribe`](#querycachesubscribe) +- [`clear`](#querycacheclear) + +**選項** + +- `onError?: (error: unknown, query: Query) => void` + - 選填 + - 當某個查詢發生錯誤時,此函式將被呼叫。 +- `onSuccess?: (data: unknown, query: Query) => void` + - 選填 + - 當某個查詢成功時,此函式將被呼叫。 +- `onSettled?: (data: unknown | undefined, error: unknown | null, query: Query) => void` + - 選填 + - 當某個查詢完成(無論成功或失敗)時,此函式將被呼叫。 + +## `queryCache.find` + +`find` 是一個稍微進階的同步方法,可用於從快取中獲取現有的查詢實例。此實例不僅包含查詢的**所有**狀態,還包含所有實例和查詢的底層細節。如果查詢不存在,則會返回 `undefined`。 + +> 注意:大多數應用程式通常不需要使用此方法,但在罕見情況下需要獲取查詢的更多資訊時會很有用(例如:查看 `query.state.dataUpdatedAt` 時間戳來決定查詢是否足夠新鮮,可作為初始值使用) + +```tsx +const query = queryCache.find(queryKey) +``` + +**選項** + +- `filters?: QueryFilters`: [查詢過濾器](../../framework/react/guides/filters#query-filters) + +**回傳值** + +- `Query` + - 快取中的查詢實例 + +## `queryCache.findAll` + +`findAll` 是一個更進階的同步方法,可用於從快取中獲取部分匹配查詢鍵的現有查詢實例。如果查詢不存在,則會返回空陣列。 + +> 注意:大多數應用程式通常不需要使用此方法,但在罕見情況下需要獲取查詢的更多資訊時會很有用 + +```tsx +const queries = queryCache.findAll(queryKey) +``` + +**選項** + +- `queryKey?: QueryKey`: [查詢鍵](../framework/react/guides/query-keys.md) +- `filters?: QueryFilters`: [查詢過濾器](../framework/react/guides/filters.md#query-filters) + +**回傳值** + +- `Query[]` + - 快取中的查詢實例陣列 + +## `queryCache.subscribe` + +`subscribe` 方法可用於訂閱整個查詢快取,並在快取發生安全/已知的更新時收到通知,例如查詢狀態變更或查詢被更新、新增或移除。 + +```tsx +const callback = (event) => { + console.log(event.type, event.query) +} + +const unsubscribe = queryCache.subscribe(callback) +``` + +**選項** + +- `callback: (event: QueryCacheNotifyEvent) => void` + - 當快取透過其追蹤的更新機制(例如 `query.setState`、`queryClient.removeQueries` 等)更新時,此函式將被呼叫。不鼓勵對快取進行超出範圍的變更,這類變更不會觸發訂閱回呼。 + +**回傳值** + +- `unsubscribe: Function => void` + - 此函式將取消回呼對查詢快取的訂閱。 + +## `queryCache.clear` + +`clear` 方法可用於完全清除快取並重新開始。 + +```tsx +queryCache.clear() +``` + +[//]: # 'Materials' + +## 延伸閱讀 + +若要更深入理解 QueryCache 的內部運作原理,請參閱社群資源中的 [#18: Inside React Query](../framework/react/community/tkdodos-blog.md#18-inside-react-query)。 + +[//]: # 'Materials' diff --git a/docs/zh-hant/reference/QueryClient.md b/docs/zh-hant/reference/QueryClient.md new file mode 100644 index 00000000000..7cb5486814f --- /dev/null +++ b/docs/zh-hant/reference/QueryClient.md @@ -0,0 +1,413 @@ +--- +source-updated-at: '2025-03-07T03:31:26.000Z' +translation-updated-at: '2025-05-08T20:17:10.146Z' +id: QueryClient +title: QueryClient +--- + +## `QueryClient` + +`QueryClient` 可用於與快取進行互動: + +```tsx +import { QueryClient } from '@tanstack/react-query' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: Infinity, + }, + }, +}) + +await queryClient.prefetchQuery({ queryKey: ['posts'], queryFn: fetchPosts }) +``` + +其可用的方法包括: + +- [`queryClient.fetchQuery`](#queryclientfetchquery) +- [`queryClient.fetchInfiniteQuery`](#queryclientfetchinfinitequery) +- [`queryClient.prefetchQuery`](#queryclientprefetchquery) +- [`queryClient.prefetchInfiniteQuery`](#queryclientprefetchinfinitequery) +- [`queryClient.getQueryData`](#queryclientgetquerydata) +- [`queryClient.ensureQueryData`](#queryclientensurequerydata) +- [`queryClient.ensureInfiniteQueryData`](#queryclientensureinfinitequerydata) +- [`queryClient.getQueriesData`](#queryclientgetqueriesdata) +- [`queryClient.setQueryData`](#queryclientsetquerydata) +- [`queryClient.getQueryState`](#queryclientgetquerystate) +- [`queryClient.setQueriesData`](#queryclientsetqueriesdata) +- [`queryClient.invalidateQueries`](#queryclientinvalidatequeries) +- [`queryClient.refetchQueries`](#queryclientrefetchqueries) +- [`queryClient.cancelQueries`](#queryclientcancelqueries) +- [`queryClient.removeQueries`](#queryclientremovequeries) +- [`queryClient.resetQueries`](#queryclientresetqueries) +- [`queryClient.isFetching`](#queryclientisfetching) +- [`queryClient.isMutating`](#queryclientismutating) +- [`queryClient.getDefaultOptions`](#queryclientgetdefaultoptions) +- [`queryClient.setDefaultOptions`](#queryclientsetdefaultoptions) +- [`queryClient.getQueryDefaults`](#queryclientgetquerydefaults) +- [`queryClient.setQueryDefaults`](#queryclientsetquerydefaults) +- [`queryClient.getMutationDefaults`](#queryclientgetmutationdefaults) +- [`queryClient.setMutationDefaults`](#queryclientsetmutationdefaults) +- [`queryClient.getQueryCache`](#queryclientgetquerycache) +- [`queryClient.getMutationCache`](#queryclientgetmutationcache) +- [`queryClient.clear`](#queryclientclear) +- [`queryClient.resumePausedMutations`](#queryclientresumepausedmutations) + +**選項** + +- `queryCache?: QueryCache` + - 選填 + - 此 client 連接的查詢快取。 +- `mutationCache?: MutationCache` + - 選填 + - 此 client 連接的變異快取。 +- `defaultOptions?: DefaultOptions` + - 選填 + - 定義使用此 queryClient 的所有查詢和變異的預設選項。 + - 你也可以定義用於 [hydration](../framework/react/reference/hydration) 的預設值。 + +## `queryClient.fetchQuery` + +`fetchQuery` 是一個非同步方法,可用於獲取並快取查詢。它會返回解析後的資料或拋出錯誤。如果你只需要獲取查詢而不需要結果,可以使用 `prefetchQuery` 方法。 + +如果查詢存在且資料未被標記為失效或未超過指定的 `staleTime`,則會返回快取中的資料。否則會嘗試獲取最新的資料。 + +```tsx +try { + const data = await queryClient.fetchQuery({ queryKey, queryFn }) +} catch (error) { + console.log(error) +} +``` + +指定 `staleTime` 以僅在資料超過一定時間後才重新獲取: + +```tsx +try { + const data = await queryClient.fetchQuery({ + queryKey, + queryFn, + staleTime: 10000, + }) +} catch (error) { + console.log(error) +} +``` + +**選項** + +`fetchQuery` 的選項與 [`useQuery`](../framework/react/reference/useQuery) 完全相同,除了以下選項:`enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, refetchOnMount, notifyOnChangeProps, throwOnError, select, suspense, placeholderData`;這些選項僅適用於 useQuery 和 useInfiniteQuery。你可以查看 [原始碼](https://github.com/TanStack/query/blob/7cd2d192e6da3df0b08e334ea1cf04cd70478827/packages/query-core/src/types.ts#L119) 以獲得更清晰的說明。 + +**返回值** + +- `Promise` + +## `queryClient.fetchInfiniteQuery` + +`fetchInfiniteQuery` 與 `fetchQuery` 類似,但可用於獲取並快取無限查詢。 + +```tsx +try { + const data = await queryClient.fetchInfiniteQuery({ queryKey, queryFn }) + console.log(data.pages) +} catch (error) { + console.log(error) +} +``` + +**選項** + +`fetchInfiniteQuery` 的選項與 [`fetchQuery`](#queryclientfetchquery) 完全相同。 + +**返回值** + +- `Promise>` + +## `queryClient.prefetchQuery` + +`prefetchQuery` 是一個非同步方法,可用於在需要或使用 `useQuery` 等渲染之前預先獲取查詢。此方法與 `fetchQuery` 的工作方式相同,只是它不會拋出錯誤或返回任何資料。 + +```tsx +await queryClient.prefetchQuery({ queryKey, queryFn }) +``` + +你甚至可以在配置中使用預設的 queryFn! + +```tsx +await queryClient.prefetchQuery({ queryKey }) +``` + +**選項** + +`prefetchQuery` 的選項與 [`fetchQuery`](#queryclientfetchquery) 完全相同。 + +**返回值** + +- `Promise` + - 返回一個 Promise,如果不需要獲取則會立即解析,或在查詢執行後解析。它不會返回任何資料或拋出任何錯誤。 + +## `queryClient.prefetchInfiniteQuery` + +`prefetchInfiniteQuery` 與 `prefetchQuery` 類似,但可用於預先獲取並快取無限查詢。 + +```tsx +await queryClient.prefetchInfiniteQuery({ queryKey, queryFn }) +``` + +**選項** + +`prefetchInfiniteQuery` 的選項與 [`fetchQuery`](#queryclientfetchquery) 完全相同。 + +**返回值** + +- `Promise` + - 返回一個 Promise,如果不需要獲取則會立即解析,或在查詢執行後解析。它不會返回任何資料或拋出任何錯誤。 + +## `queryClient.getQueryData` + +`getQueryData` 是一個同步函數,可用於獲取現有查詢的快取資料。如果查詢不存在,則返回 `undefined`。 + +```tsx +const data = queryClient.getQueryData(queryKey) +``` + +**選項** + +- `queryKey: QueryKey`: [查詢鍵 (Query Keys)](../framework/react/guides/query-keys) + +**返回值** + +- `data: TQueryFnData | undefined` + - 快取查詢的資料,如果查詢不存在則返回 `undefined`。 + +## `queryClient.ensureQueryData` + +`ensureQueryData` 是一個非同步函數,可用於獲取現有查詢的快取資料。如果查詢不存在,則會調用 `queryClient.fetchQuery` 並返回其結果。 + +```tsx +const data = await queryClient.ensureQueryData({ queryKey, queryFn }) +``` + +**選項** + +- 與 [`fetchQuery`](#queryclientfetchquery) 相同的選項 +- `revalidateIfStale: boolean` + - 選填 + - 預設為 `false` + - 如果設為 `true`,過時的資料會在背景重新獲取,但會立即返回快取資料。 + +**返回值** + +- `Promise` + +## `queryClient.ensureInfiniteQueryData` + +`ensureInfiniteQueryData` 是一個非同步函數,可用於獲取現有無限查詢的快取資料。如果查詢不存在,則會調用 `queryClient.fetchInfiniteQuery` 並返回其結果。 + +```tsx +const data = await queryClient.ensureInfiniteQueryData({ + queryKey, + queryFn, + initialPageParam, + getNextPageParam, +}) +``` + +**選項** + +- 與 [`fetchInfiniteQuery`](#queryclientfetchinfinitequery) 相同的選項 +- `revalidateIfStale: boolean` + - 選填 + - 預設為 `false` + - 如果設為 `true`,過時的資料會在背景重新獲取,但會立即返回快取資料。 + +**返回值** + +- `Promise>` + +## `queryClient.getQueriesData` + +`getQueriesData` 是一個同步函數,可用於獲取多個查詢的快取資料。僅返回符合傳入 queryKey 或 queryFilter 的查詢。如果沒有匹配的查詢,則返回空陣列。 + +```tsx +const data = queryClient.getQueriesData(filters) +``` + +**選項** + +- `filters: QueryFilters`: [查詢過濾器 (Query Filters)](../framework/react/guides/filters#query-filters) + - 如果傳入過濾器,則返回符合過濾條件的 queryKeys 的資料 + +**返回值** + +- `[queryKey: QueryKey, data: TQueryFnData | undefined][]` + - 匹配查詢鍵的元組陣列,如果沒有匹配則返回 `[]`。元組包含查詢鍵及其關聯資料。 + +**注意事項** + +由於每個元組中的返回資料結構可能不同(例如使用過濾器返回「active」查詢可能會返回不同的資料類型),`TData` 泛型預設為 `unknown`。如果你為 `TData` 提供更具體的類型,則假設你確定每個元組的資料條目都是相同類型。 + +這種區別主要是為了解決 TypeScript 開發者的便利性,他們知道將返回哪種結構。 + +## `queryClient.setQueryData` + +`setQueryData` 是一個同步函數,可用於立即更新查詢的快取資料。如果查詢不存在,則會創建它。**如果查詢在預設的 `gcTime` 5 分鐘內未被任何查詢鉤子使用,該查詢將被垃圾回收**。要一次更新多個查詢並部分匹配查詢鍵,你需要使用 [`queryClient.setQueriesData`](#queryclientsetqueriesdata)。 + +> 使用 `setQueryData` 和 `fetchQuery` 的區別在於,`setQueryData` 是同步的,並假設你已經同步擁有可用的資料。如果你需要非同步獲取資料,建議你重新獲取查詢鍵或使用 `fetchQuery` 來處理非同步獲取。 + +```tsx +queryClient.setQueryData(queryKey, updater) +``` + +**選項** + +- `queryKey: QueryKey`: [查詢鍵 (Query Keys)](../framework/react/guides/query-keys) +- `updater: TQueryFnData | undefined | ((oldData: TQueryFnData | undefined) => TQueryFnData | undefined)` + - 如果傳入非函數,資料將更新為此值 + - 如果傳入函數,它將接收舊資料值並預期返回一個新值。 + +**使用更新值** + +```tsx +setQueryData(queryKey, newData) +``` + +如果值為 `undefined`,則查詢資料不會更新。 + +**使用更新函數** + +為了語法方便,你也可以傳入一個更新函數,它接收當前資料值並返回新值: + +```tsx +setQueryData(queryKey, (oldData) => newData) +``` + +如果更新函數返回 `undefined`,則查詢資料不會更新。如果更新函數接收到 `undefined` 作為輸入,你可以返回 `undefined` 以中止更新,從而 _不_ 創建新的快取條目。 + +**不可變性** + +通過 `setQueryData` 進行的更新必須以 _不可變_ 的方式執行。**請勿** 嘗試通過直接修改 `oldData` 或通過 `getQueryData` 檢索到的資料來直接寫入快取。 + +## `queryClient.getQueryState` + +`getQueryState` 是一個同步函數,可用於獲取現有查詢的狀態。如果查詢不存在,則返回 `undefined`。 + +```tsx +const state = queryClient.getQueryState(queryKey) +console.log(state.dataUpdatedAt) +``` + +**選項** + +- `queryKey: QueryKey`: [查詢鍵 (Query Keys)](../framework/react/guides/query-keys) + +## `queryClient.setQueriesData` + +`setQueriesData` 是一個同步函數,可用於通過使用過濾函數或部分匹配查詢鍵來立即更新多個查詢的快取資料。僅更新符合傳入 queryKey 或 queryFilter 的查詢 - 不會創建新的快取條目。在底層,會為每個現有查詢調用 [`setQueryData`](#queryclientsetquerydata)。 + +```tsx +queryClient.setQueriesData(filters, updater) +``` + +**選項** + +- `filters: QueryFilters`: [查詢過濾器 (Query Filters)](../framework/react/guides/filters#query-filters) + - 如果傳入過濾器,則更新符合過濾條件的 queryKeys +- `updater: TQueryFnData | (oldData: TQueryFnData | undefined) => TQueryFnData` + - [`setQueryData`](#queryclientsetquerydata) 的更新函數或新資料,將為每個匹配的 queryKey 調用 + +## `queryClient.invalidateQueries` + +`invalidateQueries` 方法可用於根據查詢鍵或查詢的任何其他功能可訪問的屬性/狀態來使快取中的單個或多個查詢失效並重新獲取。預設情況下,所有匹配的查詢會立即標記為失效,並且活躍的查詢會在背景重新獲取。 + +- 如果你 **不希望活躍的查詢重新獲取**,而僅標記為失效,可以使用 `refetchType: 'none'` 選項。 +- 如果你 **希望非活躍的查詢也重新獲取**,可以使用 `refetchType: 'all'` 選項 + +```tsx +await queryClient.invalidateQueries( + { + queryKey: ['posts'], + exact, + refetchType: 'active', + }, + { throwOnError, cancelRefetch }, +) +``` + +**選項** + +- `filters?: QueryFilters`: [查詢過濾器 (Query Filters)](../framework/react/guides/filters#query-filters) + - `queryKey?: QueryKey`: [查詢鍵 (Query Keys)](../framework/react/guides/query-keys) + - `refetchType?: 'active' | 'inactive' | 'all' | 'none'` + - 預設為 `'active'` + - 設為 `active` 時,僅符合重新獲取條件且正通過 `useQuery` 等渲染的查詢會在背景重新獲取。 + - 設為 `inactive` 時,僅符合重新獲取條件且未通過 `useQuery` 等渲染的查詢會在背景重新獲取。 + - 設為 `all` 時,所有符合重新獲取條件的查詢會在背景重新獲取。 + - 設為 `none` 時,不會重新獲取任何查詢,僅將符合重新獲取條件的查詢標記為失效。 +- `options?: InvalidateOptions`: + - `throwOnError?: boolean` + - 設為 `true` 時,如果任何查詢重新獲取任務失敗,此方法將拋出錯誤。 + - `cancelRefetch?: boolean` + - 預設為 `true` + - 預設情況下,在發起新請求之前會取消當前正在運行的請求 + - 設為 `false` 時,如果已有請求正在運行,則不會進行重新獲取。 + +## `queryClient.refetchQueries` + +`refetchQueries` 方法可用於根據特定條件重新獲取查詢。 + +範例: + +```tsx +// 重新獲取所有查詢: +await queryClient.refetchQueries() + +// 重新獲取所有過時的查詢: +await queryClient.refetchQueries({ stale: true }) + +// 重新獲取所有部分匹配查詢鍵的活躍查詢: +await queryClient.refetchQueries({ queryKey: ['posts'], type: 'active' }) + +// 重新獲取所有完全匹配查詢鍵的活躍查詢: +await queryClient.refetchQueries({ + queryKey: ['posts', 1], + type: 'active', + exact: true, +}) +``` + +**選項** + +- `filters?: QueryFilters`: [查詢過濾器 (Query Filters)](../framework/react/guides/filters#query-filters) +- `options?: RefetchOptions`: + - `throwOnError?: boolean` + - 設為 `true` 時,如果任何查詢重新獲取任務失敗,此方法將拋出錯誤。 + - `cancelRefetch?: boolean` + - 預設為 `true` + - 預設情況下,在發起新請求之前會取消當前正在運行的請求 + - 設為 `false` 時,如果已有請求正在運行,則不會進行重新獲取。 + +**返回值** + +此函數返回一個 Promise,當所有查詢完成重新獲取時會解析。預設情況下,它 **不會** 在這些查詢重新獲取失敗時拋出錯誤,但可以通過將 `throwOnError` 選項設為 `true` 來配置此行為。 + +## `queryClient.cancelQueries` + +`cancelQueries` 方法可用於根據查詢鍵或查詢的任何其他功能可訪問的屬性/狀態來取消正在進行的查詢。 + +這在執行樂觀更新時非常有用,因為你可能需要取消任何正在進行的查詢重新獲取,以免它們在解析時覆蓋你的樂觀更新。 + +```tsx +await queryClient.cancelQueries({ queryKey: ['posts'], exact: true }) +``` + +**選項** + +- `filters?: QueryFilters`: [查詢過濾器 (Query Filters)](../framework/react/guides/filters#query-filters) + +**返回值** + +此方法不返回任何內容 + +## `queryClient.remove diff --git a/docs/zh-hant/reference/QueryObserver.md b/docs/zh-hant/reference/QueryObserver.md new file mode 100644 index 00000000000..c478715617f --- /dev/null +++ b/docs/zh-hant/reference/QueryObserver.md @@ -0,0 +1,21 @@ +--- +source-updated-at: '2025-03-18T11:09:51.000Z' +translation-updated-at: '2025-05-08T20:14:37.846Z' +id: QueryObserver +title: QueryObserver +--- + +`QueryObserver` 可用於觀察並切換不同的查詢 (query)。 + +```tsx +const observer = new QueryObserver(queryClient, { queryKey: ['posts'] }) + +const unsubscribe = observer.subscribe((result) => { + console.log(result) + unsubscribe() +}) +``` + +**選項 (Options)** + +`QueryObserver` 的選項與 [`useQuery`](../../framework/react/reference/useQuery) 的選項完全相同。 diff --git a/docs/zh-hant/reference/focusManager.md b/docs/zh-hant/reference/focusManager.md new file mode 100644 index 00000000000..41d61f80367 --- /dev/null +++ b/docs/zh-hant/reference/focusManager.md @@ -0,0 +1,78 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-08T20:14:50.524Z' +id: FocusManager +title: focusManager +--- + +`FocusManager` 負責管理 TanStack Query 中的焦點狀態。 + +它可用於變更預設的事件監聽器或手動調整焦點狀態。 + +其提供的方法如下: + +- [`setEventListener`](#focusmanagerseteventlistener) +- [`subscribe`](#focusmanagersubscribe) +- [`setFocused`](#focusmanagersetfocused) +- [`isFocused`](#focusmanagerisfocused) + +## `focusManager.setEventListener` + +`setEventListener` 可用於設定自訂事件監聽器: + +```tsx +import { focusManager } from '@tanstack/react-query' + +focusManager.setEventListener((handleFocus) => { + // 監聽 visibilitychange 事件 + if (typeof window !== 'undefined' && window.addEventListener) { + window.addEventListener('visibilitychange', handleFocus, false) + } + + return () => { + // 若設定新的處理函式,務必取消訂閱 + window.removeEventListener('visibilitychange', handleFocus) + } +}) +``` + +## `focusManager.subscribe` + +`subscribe` 可用於訂閱可見度狀態的變更。它會回傳一個取消訂閱的函式: + +```tsx +import { focusManager } from '@tanstack/react-query' + +const unsubscribe = focusManager.subscribe((isVisible) => { + console.log('isVisible', isVisible) +}) +``` + +## `focusManager.setFocused` + +`setFocused` 可用於手動設定焦點狀態。設定為 `undefined` 可回歸預設的焦點檢查機制。 + +```tsx +import { focusManager } from '@tanstack/react-query' + +// 設定為聚焦狀態 +focusManager.setFocused(true) + +// 設定為非聚焦狀態 +focusManager.setFocused(false) + +// 回歸預設的焦點檢查 +focusManager.setFocused(undefined) +``` + +**選項** + +- `focused: boolean | undefined` + +## `focusManager.isFocused` + +`isFocused` 可用於取得當前的焦點狀態。 + +```tsx +const isFocused = focusManager.isFocused() +``` diff --git a/docs/zh-hant/reference/notifyManager.md b/docs/zh-hant/reference/notifyManager.md new file mode 100644 index 00000000000..6e8f87044ec --- /dev/null +++ b/docs/zh-hant/reference/notifyManager.md @@ -0,0 +1,90 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-08T20:14:59.347Z' +id: NotifyManager +title: notifyManager +--- + +`notifyManager` 負責在 Tanstack Query 中處理回調的排程與批次處理。 + +它提供了以下方法: + +- [batch](#notifymanagerbatch) +- [batchCalls](#notifymanagerbatchcalls) +- [schedule](#notifymanagerschedule) +- [setNotifyFunction](#notifymanagersetnotifyfunction) +- [setBatchNotifyFunction](#notifymanagersetbatchnotifyfunction) +- [setScheduler](#notifymanagersetscheduler) + +## `notifyManager.batch` + +`batch` 可用於批次處理傳入回調內所有排定的更新。這主要用於內部優化 queryClient 的更新。 + +```ts +function batch(callback: () => T): T +``` + +## `notifyManager.batchCalls` + +`batchCalls` 是一個高階函式,它接收一個回調並將其包裹起來。所有對包裹後函式的呼叫都會將回調排定在下一個批次中執行。 + +```ts +type BatchCallsCallback> = (...args: T) => void + +function batchCalls>( + callback: BatchCallsCallback, +): BatchCallsCallback +``` + +## `notifyManager.schedule` + +`schedule` 將一個函式排定在下一個批次中執行。預設情況下,批次會透過 setTimeout 執行,但這可以進行配置。 + +```ts +function schedule(callback: () => void): void +``` + +## `notifyManager.setNotifyFunction` + +`setNotifyFunction` 會覆蓋通知函式。此函式會在回調應執行時接收它。預設的 notifyFunction 僅會直接呼叫回調。 + +這可用於例如在執行測試時用 `React.act` 包裹通知: + +```ts +import { notifyManager } from '@tanstack/react-query' +import { act } from 'react-dom/test-utils' + +notifyManager.setNotifyFunction(act) +``` + +## `notifyManager.setBatchNotifyFunction` + +`setBatchNotifyFunction` 設定用於批次更新的函式 + +如果你的框架支援自訂的批次處理函式,可以透過呼叫 notifyManager.setBatchNotifyFunction 讓 TanStack Query 知道。 + +例如,以下是在 solid-query 中設定批次函式的方式: + +```ts +import { notifyManager } from '@tanstack/query-core' +import { batch } from 'solid-js' + +notifyManager.setBatchNotifyFunction(batch) +``` + +## `notifyManager.setScheduler` + +`setScheduler` 配置一個自訂的回調,用於排定下一個批次的執行時機。預設行為是 `setTimeout(callback, 0)`。 + +```ts +import { notifyManager } from '@tanstack/react-query' + +// 在下一個微任務中排定批次 +notifyManager.setScheduler(queueMicrotask) + +// 在下一個畫面渲染前排定批次 +notifyManager.setScheduler(requestAnimationFrame) + +// 在未來的某個時間排定批次 +notifyManager.setScheduler((cb) => setTimeout(cb, 10)) +``` diff --git a/docs/zh-hant/reference/onlineManager.md b/docs/zh-hant/reference/onlineManager.md new file mode 100644 index 00000000000..f69e861449c --- /dev/null +++ b/docs/zh-hant/reference/onlineManager.md @@ -0,0 +1,76 @@ +--- +source-updated-at: '2024-01-26T08:30:21.000Z' +translation-updated-at: '2025-05-08T20:14:58.463Z' +id: OnlineManager +title: onlineManager +--- + +`OnlineManager` 負責管理 TanStack Query 中的線上狀態。它可用於變更預設的事件監聽器或手動調整線上狀態。 + +> 預設情況下,`onlineManager` 會假設網路連線為活動狀態,並透過監聽 `window` 物件上的 `online` 和 `offline` 事件來偵測狀態變化。 + +> 在舊版中,系統使用 `navigator.onLine` 來判斷網路狀態。但此方法在基於 Chromium 的瀏覽器中運作不佳。[許多問題](https://bugs.chromium.org/p/chromium/issues/list?q=navigator.online) 會導致誤判為離線狀態,進而錯誤地將查詢標記為 `offline`。 + +> 為解決此問題,現在我們會始終以 `online: true` 作為初始狀態,僅透過監聽 `online` 和 `offline` 事件來更新狀態。 + +> 這應能降低誤判為離線的機率,但對於透過 serviceWorker 載入的離線應用程式,可能會出現誤判為在線的情況,因為這類應用即使沒有網路連線仍可運作。 + +可用的方法包括: + +- [`setEventListener`](#onlinemanagerseteventlistener) +- [`subscribe`](#onlinemanagersubscribe) +- [`setOnline`](#onlinemanagersetonline) +- [`isOnline`](#onlinemanagerisonline) + +## `onlineManager.setEventListener` + +`setEventListener` 可用於設定自訂事件監聽器: + +```tsx +import NetInfo from '@react-native-community/netinfo' +import { onlineManager } from '@tanstack/react-query' + +onlineManager.setEventListener((setOnline) => { + return NetInfo.addEventListener((state) => { + setOnline(!!state.isConnected) + }) +}) +``` + +## `onlineManager.subscribe` + +`subscribe` 可用於訂閱線上狀態的變更。它會回傳一個取消訂閱的函式: + +```tsx +import { onlineManager } from '@tanstack/react-query' + +const unsubscribe = onlineManager.subscribe((isOnline) => { + console.log('isOnline', isOnline) +}) +``` + +## `onlineManager.setOnline` + +`setOnline` 可用於手動設定線上狀態。 + +```tsx +import { onlineManager } from '@tanstack/react-query' + +// 設定為線上 +onlineManager.setOnline(true) + +// 設定為離線 +onlineManager.setOnline(false) +``` + +**選項** + +- `online: boolean` + +## `onlineManager.isOnline` + +`isOnline` 可用於取得當前的線上狀態。 + +```tsx +const isOnline = onlineManager.isOnline() +``` diff --git a/docs/zh-hant/reference/streamedQuery.md b/docs/zh-hant/reference/streamedQuery.md new file mode 100644 index 00000000000..ececb889929 --- /dev/null +++ b/docs/zh-hant/reference/streamedQuery.md @@ -0,0 +1,37 @@ +--- +source-updated-at: '2025-05-01T13:24:59.000Z' +translation-updated-at: '2025-05-08T20:14:52.262Z' +id: streamedQuery +title: streamedQuery +--- + +`streamedQuery` 是一個輔助函式,用於建立一個從 [AsyncIterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) 串流資料的查詢函式。資料會是一個包含所有接收到的區塊的陣列。在接收到第一個資料區塊之前,查詢會處於 `pending` 狀態,但之後會轉為 `success`。查詢會持續保持 `fetchStatus` 為 `fetching`,直到串流結束。 + +若要查看 `streamedQuery` 的實際應用,請參考我們的 [聊天範例](../framework/react/examples/chat)。 + +```tsx +import { experimental_streamedQuery as streamedQuery } from '@tanstack/react-query' + +const query = queryOptions({ + queryKey: ['data'], + queryFn: streamedQuery({ + queryFn: fetchDataInChunks, + }), +}) +``` + +> 注意:`streamedQuery` 目前標記為 `experimental`,因為我們希望收集社群的意見回饋。如果您已試用此 API 並有建議,請在此 [GitHub 討論串](https://github.com/TanStack/query/discussions/9065) 中提供。 + +**選項** + +- `queryFn: (context: QueryFunctionContext) => Promise>` + - **必填** + - 此函式需回傳一個 Promise,其解析值為要串流輸入的 AsyncIterable 資料。 + - 接收一個 [QueryFunctionContext](../guides/query-functions.md#queryfunctioncontext) 參數。 +- `refetchMode?: 'append' | 'reset' | 'replace'` + - 選填 + - 定義重新取得資料時的處理方式。 + - 預設為 `'reset'` + - 設為 `'reset'` 時,查詢會清除所有資料並回到 `pending` 狀態。 + - 設為 `'append'` 時,資料會附加到現有資料之後。 + - 設為 `'replace'` 時,資料會在串流結束時寫入快取。