跳至主要内容
版本:v18.0.0

查询

一个 GraphQL 查询 是您想从 GraphQL 服务器查询数据的描述。它包含一组字段(以及可能的 片段),我们希望从 GraphQL 服务器请求这些字段。我们可以查询的内容将取决于服务器上公开的 GraphQL 架构,它描述了可用于查询的数据。

查询可以作为请求通过网络发送,并附带可选的 变量 集合,查询使用这些变量来获取数据。服务器响应将是一个与我们发送的查询形状匹配的 JSON 对象。

query UserQuery($id: ID!) {
user(id: $id) {
id
name
...UserFragment
}
viewer {
actor {
name
}
}
}

fragment UserFragment on User {
username
}

示例响应

{
"data": {
"user": {
"id": "4",
"name": "Mark Zuckerberg",
"username": "zuck"
},
"viewer": {
"actor": {
"name": "Your Name"
}
}
}
}

渲染查询

渲染 Relay 中的查询,您可以使用 usePreloadedQuery 钩子。usePreloadedQuery 接受一个查询定义和一个查询引用,并返回该查询和引用的相应数据。

import type {HomeTabQuery} from 'HomeTabQuery.graphql';
import type {PreloadedQuery} from 'react-relay';

const React = require('React');
const {graphql, usePreloadedQuery} = require('react-relay');

type Props = {
queryRef: PreloadedQuery<HomeTabQuery>,
};

function HomeTab(props: Props) {
const data = usePreloadedQuery(
graphql`
query HomeTabQuery($id: ID!) {
user(id: $id) {
name
}
}
`,
props.queryRef,
);

return (
<h1>{data.user?.name}</h1>
);
}

让我们看看这里发生了什么

  • usePreloadedQuery 接受一个 graphql 查询和一个 PreloadedQuery 引用,并返回为该查询获取的数据。
    • PreloadedQuery(在本例中为 queryRef)是一个描述和引用正在(或已经)获取的查询实例的对象。
      • 我们将在下一节中介绍如何实际获取查询,并在 带有 Suspense 的加载状态 部分中介绍如何在尝试渲染查询时显示加载状态。
  • 片段 类似,组件会自动订阅查询数据更新:如果此查询的数据在应用程序中的任何位置更新,则组件将自动使用最新更新的数据重新渲染。
  • usePreloadedQuery 还接受一个 Flow 类型参数,它对应于查询的 Flow 类型,在本例中为 HomeTabQuery
    • Relay 编译器会自动为任何声明的查询生成 Flow 类型,您可以从以下名称格式的生成文件中导入这些类型:<query_name>.graphql.js
    • 请注意,data 已经正确地使用 Flow 类型化,无需显式注释,并且基于 GraphQL 架构中的类型。例如,上面 data 的类型将是:{ user: ?{ name: ?string } }
  • 确保您在尝试渲染查询之前,使用 Relay 环境提供程序 在应用程序的根目录提供 Relay 环境。

获取用于渲染的查询

除了渲染查询之外,我们还需要从服务器获取它。通常情况下,我们希望在应用程序的根目录获取查询,并且只拥有一个或几个查询 累积 渲染屏幕所需的所有数据。理想情况下,我们应该尽早获取它们,甚至在开始渲染应用程序之前就获取它们。

为了获取查询以便稍后渲染它,您可以使用 useQueryLoader 钩子

import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql';
import type {PreloadedQuery} from 'react-relay';

const HomeTabQuery = require('HomeTabQuery.graphql')
const {useQueryLoader} = require('react-relay');


type Props = {
initialQueryRef: PreloadedQuery<HomeTabQueryType>,
};

function AppTabs(props) {
const [
homeTabQueryRef,
loadHomeTabQuery,
] = useQueryLoader(
HomeTabQuery,
props.initialQueryRef, /* e.g. provided by router */
);

const onSelectHomeTab = () => {
// Start loading query for HomeTab immediately in the event handler
// that triggers navigation to that tab, *before* we even start
// rendering the target tab.
// Calling this function will update the value of homeTabQueryRef.
loadHomeTabQuery({id: '4'});

// ...
}

// ...

return (
screen === 'HomeTab' && homeTabQueryRef != null ?
// Pass to component that uses usePreloadedQuery
<HomeTab queryRef={homeTabQueryRef} /> :
// ...
);
}

上面的例子有点牵强,但让我们提炼一下正在发生的事情

  • 我们在 AppTabs 组件中调用 useQueryLoader
    • 它接受一个查询,在本例中为 HomeTabQuery(我们在前面的示例中声明的查询),我们可以通过以下方式获取它:'HomeTabQuery.graphql'
    • 它接受一个可选的初始 PreloadedQuery,作为存储在状态中并由 useQueryLoader 返回的 homeTabQueryRef 的初始值使用。
    • 它还额外接受一个 Flow 类型参数,它对应于查询的 Flow 类型,在本例中为 HomeTabQueryType,您也可以从自动生成的文件中获取它:'HomeTabQuery.graphql'
  • 调用 useQueryLoader 使我们能够获得两件事
    • homeTabQueryRef:一个 ?PreloadedQuery,它是一个描述和引用正在(或已经)获取的查询实例的对象。如果我们还没有获取查询,即如果我们还没有调用 loadHomeTabQuery,则此值将为 null。
    • loadHomeTabQuery:一个将获取此查询数据(如果它尚未缓存)并提供包含查询期望的 变量 的对象的函数,在本例中为 {id: '4'}(我们将在 重用缓存数据进行渲染 部分详细介绍 Relay 如何使用缓存数据)。调用此函数也将更新 homeTabQueryRef 的值,使其成为 PreloadedQuery 的实例。
      • 请注意,我们传递给此函数的 variables 将由 Flow 检查,以确保您传递的值与 GraphQL 查询期望的值匹配。
      • 还要注意,我们是在导致渲染 HomeTab 的事件处理程序中调用此函数的。这使我们能够尽早开始获取屏幕数据,甚至在新的选项卡开始渲染之前就开始获取数据。
        • 事实上,loadQuery 如果在 React 的渲染阶段调用,将会抛出错误!
  • 请注意,useQueryLoader 将在组件卸载时自动处理所有已加载的查询。处理查询意味着 Relay 将不再在缓存中保存该特定查询实例的数据(我们将在 重用缓存数据进行渲染 部分介绍查询数据的生命周期)。此外,如果查询的请求在处理时仍在进行中,则该请求将被取消。
  • 我们的 AppTabs 组件渲染前面示例中的 HomeTab 组件,并将相应的查询引用传递给它。请注意,此父组件拥有该查询数据的生命周期,这意味着当它卸载时,它将处理该查询,如上所述。
  • 最后,确保您在尝试使用 useQueryLoader 之前,使用 Relay 环境提供程序 在应用程序的根目录提供 Relay 环境。

有时,您希望在父组件的上下文中之外启动获取操作,例如获取应用程序初始加载所需的数据。对于这些情况,您可以直接使用 loadQuery API,而不使用 useQueryLoader

import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql';

const HomeTabQuery = require('HomeTabQuery.graphql')
const {loadQuery} = require('react-relay');


const environment = createEnvironment(...);

// At some point during app initialization
const initialQueryRef = loadQuery<HomeTabQueryType>(
environment,
HomeTabQuery,
{id: '4'},
);

// ...

// E.g. passing the initialQueryRef to the root component
render(<AppTabs initialQueryRef={initialQueryRef} initialTab={...} />)
  • 在这个例子中,我们直接调用 loadQuery 函数来获取一个 PreloadedQuery 实例,我们稍后可以将其传递给使用 usePreloadedQuery 的组件。
  • 在本例中,我们希望根 AppTabs 组件管理查询引用的生命周期,并在适当的时候处理它,如果需要的话。
  • 我们在这个例子中对“应用程序初始化”的细节含糊其词,因为这在不同的应用程序中会有所不同。这里需要注意的重要一点是,我们应该在开始渲染根组件之前获取查询引用。事实上,loadQuery 如果在 React 的渲染阶段调用,将会抛出错误!

边获取边渲染

上面的例子说明了如何将获取数据与渲染数据分开,以便尽早开始获取操作(而不是等到组件渲染完成才开始获取操作),并使我们能够更早地向用户显示内容。它还有助于防止瀑布式往返操作,并且让我们可以更好地控制和预测获取操作何时发生,而如果我们在渲染期间进行获取操作,则更难确定获取操作何时会(或应该)发生。这与 “边获取边渲染” 模式与 React Suspense 非常吻合。

这是使用 Relay 获取数据的首选模式,它适用于多种情况,例如应用程序的初始加载、后续导航,或者通常情况下使用最初隐藏并在交互后显示的 UI 元素(如菜单、弹出窗口、对话框等),这些元素也需要获取其他数据。

在渲染期间延迟获取查询

获取查询的另一种选择是在组件渲染时延迟获取查询。但是,正如我们之前提到的,首选模式是在渲染之前开始获取查询。如果在没有谨慎的情况下使用延迟获取,它可能会触发嵌套或瀑布式往返操作,并可能降低性能。

要延迟获取查询,您可以使用 useLazyLoadQuery 钩子

const React = require('React');
const {graphql, useLazyLoadQuery} = require('react-relay');

function App() {
const data = useLazyLoadQuery(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
}
}
`,
{id: '4'},
);

return (
<h1>{data.user?.name}</h1>
);
}

让我们看看这里发生了什么

  • useLazyLoadQuery 接受一个 graphql 查询和一些该查询的变量,并返回为该查询获取的数据。变量是一个包含 GraphQL 查询中引用的 变量 值的对象。
  • 片段 类似,组件会自动订阅查询数据更新:如果此查询的数据在应用程序中的任何位置更新,则组件将自动使用最新更新的数据重新渲染。
  • useLazyLoadQuery 还额外接受一个 Flow 类型参数,它对应于查询的 Flow 类型,在本例中为 AppQuery。
    • 请记住,Relay 会自动为任何声明的查询生成 Flow 类型,您可以使用 useLazyLoadQuery 导入和使用这些类型。这些类型可以在以下名称格式的生成文件中找到:<query_name>.graphql.js
    • 请注意,variables 将由 Flow 检查,以确保您传递的值与 GraphQL 查询期望的值匹配。
    • 请注意,data 已经正确地使用 Flow 类型化,无需显式注释,并且基于 GraphQL 架构中的类型。例如,上面 data 的类型将是:{ user: ?{ name: ?string } }
  • 默认情况下,当组件渲染时,Relay 会获取此查询的数据(如果它还没有被缓存),并将其作为useLazyLoadQuery调用的结果返回。我们将在使用 Suspense 的加载状态部分详细介绍如何显示加载状态,以及 Relay 如何在重用缓存数据进行渲染部分中使用缓存数据。
  • 请注意,如果您重新渲染组件并传递不同的查询变量,与最初使用的变量不同,它将导致查询使用新的变量再次被获取,并且可能使用不同的数据重新渲染。
  • 最后,在尝试渲染查询之前,请确保您在应用程序的根目录中使用Relay 环境提供程序提供了一个 Relay 环境。

此页面是否有用?

通过以下方式帮助我们改进网站: 回答几个简短的问题.