加入我们,于10月8日至10日在纽约市,了解最新关于 GraphQL 联邦和 API 平台工程的技巧、趋势和新闻。参加纽约市的 GraphQL Summit 2024
文档
免费开始

查询

使用 useQuery 钩子获取数据


本文展示了如何使用在 React 中获取数据,并使用useQuery钩子将结果附加到您的 UI。您还将了解为什么 通过跟踪错误和加载状态来简化数据管理代码。

先决条件

本文假设您熟悉构建基本的 GraphQL查询。如果您需要复习,我们建议阅读 此指南。您还可以针对 Apollo 的 全栈教程服务器构建示例查询。

本文还假设您已经设置好了 Apollo Client 并将 React 应用包裹在 ApolloProvider 组件中。有关更多信息,请参阅 入门指南

要跟随下面的示例,请打开我们 入门项目示例 GraphQL 服务器 在 CodeSandbox 上。您可以在 此处 查看应用的完整版本。

执行查询

在 Apollo 应用中,执行查询的主要 API 是 useQuery React 钩子。要在 React 组件中运行查询,请调用 useQuery 并传入一个 GraphQL 查询 字符串。当组件渲染时,useQuery 会从 Apollo Client 返回一个对象,其中包含 Apollo Clientloadingerrordata 属性,您可以使用这些属性来渲染您的 UI。

注意:在 Apollo Client >= 3.8 版本中,Suspense 数据获取钩子可用于数据,在 <Suspense />边界内使用 React 18 的新并发渲染模型。有关更多信息,请参阅 Apollo Client 的 Suspense 文档

让我们来看一个例子。首先,我们将创建一个名为 GraphQL 查询的 GET_DOGS。请记住,使用 gql 函数将包含查询的 query 字符串包装在查询文档中:

index.js
import { gql, useQuery } from '@apollo/client';
const GET_DOGS = gql`
query GetDogs {
dogs {
id
breed
}
}
`;

接下来,我们将创建一个名为Dogs的组件。在其中,我们将把我们的GET_DOGS查询传递给useQuery钩子:

index.js
function Dogs({ onDogSelected }) {
const { loading, error, data } = useQuery(GET_DOGS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return (
<select name='dog' onChange={onDogSelected}>
{data.dogs.map((dog) => (
<option key={dog.id} value={dog.breed}>
{dog.breed}
</option>
))}
</select>
);
}

随着query的执行以及loadingerrordata的值变化,Dogs组件可以智能地根据query的状态渲染不同的UI元素:

  • 只要loadingtrue(表示query仍在进行中),组件就会显示一个Loading...通知。
  • loadingfalse且没有error时,查询已完成。该组件将渲染一个下拉菜单,菜单中填充了服务器返回的狗品种列表。

当用户从填充的下拉菜单中选择一个狗品种时,选择内容将通过提供的onDogSelected函数发送到父组件。

在下一步中,我们将下拉菜单与一个更复杂的query关联,该query使用GraphQL

缓存查询结果

每次Apollo Client从您的服务器获取query结果时,它都会自动将那些结果本地缓存起来。这使得随后执行的相同query变得非常快。

为了查看此缓存效果,让我们构建一个新的组件,命名为DogPhotoDogPhoto接受一个名为breed的属性,它反映了我们的Dogs组件中下拉菜单的当前值:

index.js
const GET_DOG_PHOTO = gql`
query Dog($breed: String!) {
dog(breed: $breed) {
id
displayImage
}
}
`;
function DogPhoto({ breed }) {
const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
variables: { breed },
});
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
);
}

请注意,我们这次为useQuery钩子提供了配置选项(variables)。variables选项是一个对象,包含了我们想要传递给我们的GraphQL queryvariables。在这种情况下,我们希望传递当前选择的下拉菜单中的breed

从下拉列表中选择 bulldog 以查看其照片。然后切换到另一种犬种,然后再切换 bulldog。您将注意到,第二次时,bulldog 的照片会立即加载。这是缓存的作用!

接下来,让我们学习一些确保我们缓存的驻留数据是新鲜的技术。

更新缓存的查询结果

有时,您想确保您的 查询's 缓存数据与您的 服务器 数据保持最新。 Apollo 客户端支持两种策略: 轮询重新获取

轮询

轮询通过定期(以指定的时间间隔)执行您的 查询 来提供与服务器近乎实时同步。要启用查询的轮询,请将 pollInterval 配置选项传递给 useQuery 钩子,使用毫秒表示间隔:

index.js
function DogPhoto({ breed }) {
const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
variables: { breed },
pollInterval: 500,
});
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
);
}

pollInterval 设置为 500,我们将每 0.5 秒从服务器获取当前犬种的照片。请注意,如果您将 pollInterval 设置为 0,该 查询 将不会轮询。

您还可以使用 startPollingstopPolling 函数(由 useQuery 钩子返回)动态开始和停止轮询。使用这些函数时,请将 pollInterval 配置选项作为 startPolling 函数的参数。

重新获取

重新获取允许您在响应特定用户操作时刷新 查询 结果,而不是使用固定的时间间隔。

让我们向我们的 DogPhoto 组件添加一个按钮,该按钮在点击时调用我们的 查询refetch 函数。

您可以可选地向 refetch 函数提供一个新的 variables 对象。如果您避免传递 variables 对象,仅使用 refetch(),则查询将使用其在上次执行中使用的相同的 variables

index.js
function DogPhoto({ breed }) {
const { loading, error, data, refetch } = useQuery(GET_DOG_PHOTO, {
variables: { breed },
});
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<div>
<img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
<button onClick={() => refetch()}>
Refetch new breed!
</button>
</div>
);
}

点击按钮并注意 UI 使用新的犬种照片更新。重新获取是确保新鲜数据的一种优秀方式,但它在加载状态中引入了一些复杂度。在下一节中,我们将介绍处理复杂加载和错误状态的战略。

refetch 提供新的变量

您可以这样调用 refetch 并提供一个新的一组变量:

<button
onClick={() =>
refetch({
breed: 'dalmatian', // Always refetches a dalmatian instead of original breed
})
}
>
Refetch!
</button>

如果您为原始 查询 的某些变量提供了新值,但不是所有的变量,则 refetch 使用省略的每个变量的原始值。

检查加载状态

我们已经看到 useQuery 钩子揭示了我们的 query 当前加载状态。这对于查询首次加载时很有用,但当我们正在重新抓取或轮询时,我们的加载状态会发生什么变化呢?

让我们回到上一节中的重新抓取示例。如果您点击重新抓取按钮,您会看到组件直到新的数据到来之前不会重新渲染。如果我们想向用户表明我们正在重新抓取照片,怎么办?

通过 networkStatus 属性,useQuery 钩子的结果对象提供了关于 query 状态的详细信息的粒度。为了利用这些信息,我们将 notifyOnNetworkStatusChange 选项设置为 true,这样在我们的 查询组件在正在进行的重新抓取期间重新渲染:

index.js
import { NetworkStatus } from '@apollo/client';
function DogPhoto({ breed }) {
const { loading, error, data, refetch, networkStatus } = useQuery(
GET_DOG_PHOTO,
{
variables: { breed },
notifyOnNetworkStatusChange: true,
}
);
if (networkStatus === NetworkStatus.refetch) return 'Refetching!';
if (loading) return null;
if (error) return `Error! ${error}`;
return (
<div>
<img src={data.dog.displayImage} style={{ height: 100, width: 100 }} />
<button onClick={() => refetch()}>
Refetch!
</button>
</div>
);
}

启用此选项还可以确保 loading 的值相应更新,即使您不想使用 networkStatus 属性提供的更细粒度的信息。

networkStatus 属性是一个代表不同加载状态的 NetworkStatus 枚举。重新抓取由 NetworkStatus.refetch 表示,而对于轮询和分页也有相应的值。有关所有可能的加载状态的完整列表,请查看 源代码

要查看我们刚刚构建的完整版本的 app,请查看 CodeSandbox 这里

检测错误状态

您可以通过将 errorPolicy 配置选项提供给 useQuery 钩子来自定义 query 错误处理。默认值是 none,这告诉 Apollo Client 将所有 GraphQL 错误视为运行时错误。在这种情况下,Apollo Client 会丢弃服务器返回的任何 query 响应数据,并将 error 属性设置为 useQuery 结果对象的

如果您将 errorPolicy 设置为 alluseQuery 丢弃查询响应数据,从而使您能够渲染部分结果。

有关更多信息,请参阅 处理操作错误

使用 useLazyQuery 手动执行

以下是一个示例

index.js
import React from 'react';
import { useLazyQuery } from '@apollo/client';
function DelayedQuery() {
const [getDog, { loading, error, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (error) return `Error! ${error}`;
return (
<div>
{data?.dog && <img src={data.dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
Click me!
</button>
</div>
);
}

注意

设置获取策略

const { loading, error, data } = useQuery(GET_DOGS, {
fetchPolicy: 'network-only', // Doesn't check cache before making a network request
});

nextFetchPolicy
3.1

const { loading, error, data } = useQuery(GET_DOGS, {
fetchPolicy: 'network-only', // Used for first execution
nextFetchPolicy: 'cache-first', // Used for subsequent executions
});

nextFetchPolicy 函数

如果您想默认应用单个 nextFetchPolicy,因为您发现自己经常手动为大多数查询提供 nextFetchPolicy,您可以在创建您的 ApolloClient 实例时配置 defaultOptions.watchQuery.nextFetchPolicy:

new ApolloClient({
link,
client,
defaultOptions: {
watchQuery: {
nextFetchPolicy: 'cache-only',
},
},
});

此配置适用于所有 client.watchQuery 调用以及未配置 nextFetchPolicyuseQuery 调用。

如果您想更精细地控制 nextFetchPolicy 的行为,您可以提供一个函数而不是 WatchQueryFetchPolicy 字符串:

new ApolloClient({
link,
client,
defaultOptions: {
watchQuery: {
nextFetchPolicy(currentFetchPolicy) {
if (
currentFetchPolicy === 'network-only' ||
currentFetchPolicy === 'cache-and-network'
) {
// Demote the network policies (except "no-cache") to "cache-first"
// after the first request.
return 'cache-first';
}
// Leave all other fetch policies unchanged.
return currentFetchPolicy;
},
},
},
});

nextFetchPolicy 函数会在每次请求后被调用,并使用 currentFetchPolicy 参数决定如何修改抓取策略。

除了每次请求后会被调用,您的 nextFetchPolicy 函数也会在变量变化时被调用,这通常会导致 fetchPolicy 重置为其初始值,这对于触发以 cache-and-networknetwork-only 抓取策略开始的查询的新网络请求非常重要。

为了自己拦截和处理 variables-changed 的情况,您可以使用传递给您的 nextFetchPolicy 函数的第二个参数的 NextFetchPolicyContext 对象:

new ApolloClient({
link,
client,
defaultOptions: {
watchQuery: {
nextFetchPolicy(
currentFetchPolicy,
{
// Either "after-fetch" or "variables-changed", indicating why the
// nextFetchPolicy function was invoked.
reason,
// The rest of the options (currentFetchPolicy === options.fetchPolicy).
options,
// The original value of options.fetchPolicy, before nextFetchPolicy was
// applied for the first time.
initialFetchPolicy,
// The ObservableQuery associated with this client.watchQuery call.
observable,
}
) {
// When variables change, the default behavior is to reset
// options.fetchPolicy to context.initialFetchPolicy. If you omit this logic,
// your nextFetchPolicy function can override this default behavior to
// prevent options.fetchPolicy from changing in this case.
if (reason === 'variables-changed') {
return initialFetchPolicy;
}
if (
currentFetchPolicy === 'network-only' ||
currentFetchPolicy === 'cache-and-network'
) {
// Demote the network policies (except "no-cache") to "cache-first"
// after the first request.
return 'cache-first';
}
// Leave all other fetch policies unchanged.
return currentFetchPolicy;
},
},
},
});

为了调试这些 nextFetchPolicy 转换,在函数体内添加 console.logdebugger 语句可能很有用,以便了解函数被调用的时间和原因。

支持的抓取策略

名称描述
cache-first

Apollo 客户端首先对缓存执行 query。如果所有请求的数据都存在于缓存中,则返回该数据。否则,Apollo 客户端对您的 GraphQL 服务器执行查询,并在将其缓存后返回数据。

优先最小化应用程序发送的网络请求数量。

这是默认的抓取策略。

cache-only

Apollo 客户端仅对缓存执行 query。在这种情况下,永远不会向服务器查询。

如果缓存不包含所有请求的字段数据,则 cache-only 查询会抛出错误。

cache-and-network

Apollo 客户端同时对缓存和您的 GraphQL 服务器执行完整的 query。如果服务器端查询更改了缓存的字段,则会自动更新。

提供快速响应的同时,还有助于确保缓存的数據與服務器數據一致性。

仅网络

Apollo Client将对您的GraphQL服務器执行完整的查询但在查询之前不先檢查緩存。query's結果'將存儲在緩存中。

優先保持與服務器數據的一致性,但當緩存數據可用時不能提供幾乎瞬時的響應。

无缓存

network-only相似,但除了查询的結果不存儲在緩存中。

备用

使用與cache-first相同的邏輯,但這個query不會在基礎值更改時自動更新。您仍然可以雲手更新這個query,使用refetchupdateQueries

useQuery API

以下列出了useQuery鉤子支持的可選參數和結果字段。

大部分的useQuery調用可以省略大多數這些參數,但了解它們的存在是有用的。要學習更多有關含使用範例的useQuery鉤子API,請參考API參考

參數

以下列出useQuery鉤子接受的參數:

操作參數

client(可選)

ApolloClient<any>

使用此ApolloClient實例來執行query。

默認情況下,使用從上下文傳遞的實例,但您可以這裡提供不同的實例。

指定query如何處理返回了GraphQL errors和部分結果的响应。

詳見GraphQL error policies

默認值為none,意味著query結果包含錯誤詳情但不含部分結果。

(data: TData) => void

query無錯誤成功完成(或當errorPolicyignore並返回部分數據時)調用的回調函數。

此函數將接受query的結果data

(error: ApolloError) => void

query遇到一個或多個錯誤(除非errorPolicyignore)時,將調用的回調函數。

此函数接受一个ApolloError对象,该对象包含一个networkError对象或一个graphQLErrors数组,取决于发生的错误。

如果为真,则不执行query

默认值为false

一个包含所有您查询需要执行的GraphQL变量query的对象。

对象中的每个键对应一个变量名,该键的值对应变量值。

网络选项
DefaultContext

如果您正在使用Apollo Link,此对象是传递到您的链中的context对象

如果true,则相关组件在query中断开始或出现网络错误时重新渲染。

默认值为false

指定query轮询更新的结果的时间间隔(以毫秒为单位)。

默认值是0(无轮询)。

每当在轮询期间发生重试时,都会调用回调函数。如果该函数返回true,则跳过重新尝试重试,直到下一个轮询间隔。

传递false跳过在服务端渲染期间执行query

缓存选项
WatchQueryFetchPolicy

指定query在执行期间如何与 Apollo Client 缓存交互(例如,是否在向服务器发送请求之前检查缓存)。

有关详细信息,请参阅设置获取策略

默认值是cache-first

WatchQueryFetchPolicy

默认为 options.fetchPolicy 的初始值,但可以显式配置以指定要重新查看FetchPolicy,以便在变量变化时(除非 nextFetchPolicy 干预)。

WatchQueryFetchPolicy | ((this: WatchQueryOptions<TVariables, TData>, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext<TData, TVariables>) => WatchQueryFetchPolicy)

指定查询完成后所使用的 FetchPolicy

指定是否将 NetworkStatus.refetch 与传入的字段数据合并到现有数据中,还是覆盖现有数据。覆盖可能是更优选的,但合并是目前默认行为,以与 Apollo Client 3.x 的向后兼容。

如果 true,查询在缓存中不包含查询的所有字段的查询结果时可以返回部分结果。

默认值为false

其他

⚠️ 已弃用

使用 canonizeResults 可能会导致内存泄漏,所以我们通常不建议再使用此选项。Apollo Client 的未来版本将包含一个类似功能,但不存在内存泄漏的风险。

在返回之前对缓存结果进行规范化的选项。规范化会占用一些额外的时间,但可以加快未来的深度比较速度。默认为 false。

⚠️ 已弃用

由于更一致地应用获取策略,在 Apollo Client 3 中设置此选项是不必要的。它可能在未来的版本中删除。

如果 true,则导致如果检测到的查询结果为部分结果,则在查询时进行重新获取。

默认值为false

结果

调用 useQuery 钩子后返回一个结果对象,该对象具有以下属性。此对象包含您的查询结果,以及一些用于重新获取、动态轮询和分页的辅助函数。

操作数据
TData | undefined

一个对象,包含 GraphQL 查询完成后的结果。

此值可能为 undefined,如果查询产生一个或多个错误(取决于查询的 errorPolicy)。

如果查询产生了一个或多个错误,此对象包含一个或多个 graphQLErrors 的数组或单个 networkError。否则,此值是 undefined

有关更多信息,请参阅 处理操作错误.

包含此查询最近一次执行的结果对象。

如果是查询的第一个执行,则此值未定义

TVariables | undefined

包含为查询提供的变量的对象。

网络信息
布尔值

如果true,则相关延迟查询已执行。

此字段仅在useLazyQuery返回的

ApolloClient<any>

执行查询的Apollo Client实例。可用于手动执行后续查询或向缓存写入数据。

布尔值

如果true,则查询还在进行中,结果尚未返回。

NetworkStatus

表示查询相关请求当前网络状态的数字。查看可能的值。

notifyOnNetworkStatusChange选项一起使用。

辅助函数
<TFetchData = TData, TFetchVars extends OperationVariables = TVariables>(fetchMoreOptions: FetchMoreQueryOptions<TFetchVars, TFetchData> & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; variables: TFetchVars; }) => TData; }) => Promise<ApolloQueryResult<TFetchData>>

一个帮助您获取分页列表字段的下一组结果的函数

(variables?: Partial<TVariables>) => Promise<ApolloQueryResult<TData>>

一个使您能够重新执行查询的函数,可选地传入新的变量

为了保证fetchPolicy设置为network-only(除非原始查询的fetchPolicyno-cachecache-and-network,后者也会保证进行网络请求)。

另请参阅重新查询

(pollInterval: 数字) => 无

一个函数,指示查询在指定的时间间隔(以毫秒为单位)重新执行。

() => 无

一个函数,指示查询停止轮询,在先前的startPolling调用后。

<TSubscriptionData = TData, TSubscriptionVariables 扩展 OperationVariables = TVariables>(options: SubscribeToMoreOptions<TData, TSubscriptionVariables, TSubscriptionData>) => () => 无

一个函数,允许你执行一个订阅,通常用于订阅查询中包含的特定字段

此函数返回另一个函数,你可以调用它来终止

<TVars 扩展 OperationVariables = TVariables>(mapFn: (previousQueryResult: TData, options: Pick<WatchQueryOptions<TVars, TData>, "variables">) => TData) => 无

一个函数,允许你在不执行后续GraphQL操作的情况下更新查询的缓存结果。

有关更多信息,请参见使用updateQuery和updateFragment

其他
ObservableQuery<TData, TVariables>

hook所使用的内部ObservableQuery的引用。

ReadonlyArray<GraphQLFormattedError>

⚠️ 已弃用

该属性将在Apollo Client的下一个版本中被移除。请使用error.graphQLErrors代替。

下一步

现在你已经了解了如何使用useQuery hook获取数据,了解如何使用useMutation hook更新你的数据!

之后,了解一些其他有用的Apollo Client特性:

  • 本地状态管理:了解如何查询本地数据。
  • 分页:了解如何从前端列表字段中增量获取数据。
上一步
开始
下一步
延迟
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,即Apollo GraphQL。

隐私政策

公司