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

GraphQL 订阅

GraphQL 订阅是一种机制,允许客户端根据服务器端事件流查询数据。

GraphQL 订阅看起来非常像查询,除了它使用 `subscription` 关键字

subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {
feedback_like_subscribe(data: $input) {
feedback {
like_count
}
}
}
  • 使用此 GraphQL 片段建立订阅将导致应用程序在 `feedback_like_subscribe` 流发出事件时收到通知。
  • `feedback_like_subscribe` 是一个 *订阅根字段*(或简称 *订阅字段*),它在后端设置订阅。
  • 与变异类似,订阅分两个独立的步骤处理。首先,发生服务器端事件。然后,执行查询。
注意

请注意,事件流可以完全任意,并且可能与所选字段无关。换句话说,不能保证在通知之间订阅中所选的值会发生变化。

  • `feedback_like_subscribe` 返回一个特定的 GraphQL 类型,它公开我们可以在响应服务器端事件时查询的数据。在本例中,我们正在查询 Feedback 对象及其更新的 `like_count`。这使我们能够实时显示点赞次数。

客户端收到的订阅负载示例可能如下所示

{
"feedback_like_subscribe": {
"feedback": {
"id": "feedback-id",
"like_count": 321,
}
}
}

在 Relay 中,我们也可以使用 `graphql` 标签声明 GraphQL 订阅

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

const feedbackLikeSubscription = graphql`
subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {
feedback_like_subscribe(data: $input) {
feedback {
like_count
}
}
}
`;
  • 请注意,订阅也可以像查询或片段一样引用 GraphQL 变量

使用 `useSubscription` 创建订阅

为了在 Relay 中创建订阅,我们可以使用 `useSubscription` 和 `requestSubscription` API。让我们看看使用 `useSubscription` API 的示例

import type {Environment} from 'react-relay';
import type {FeedbackLikeSubscribeData} from 'FeedbackLikeSubscription.graphql';

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

function useFeedbackSubscription(
input: FeedbackLikeSubscribeData,
) {
const config = useMemo(() => ({
subscription: graphql`
subscription FeedbackLikeSubscription(
$input: FeedbackLikeSubscribeData!
) {
feedback_like_subscribe(data: $input) {
feedback {
like_count
}
}
}
`,
variables: {input},
}), [input]);

return useSubscription(config);
}

让我们提炼一下这里发生了什么。

  • `useSubscription` 接受一个 `GraphQLSubscriptionConfig` 对象,其中包含以下字段
    • `subscription`: 包含订阅的 GraphQL 字面量,以及
    • `variables`: 用于建立订阅的变量。
  • 此外, `useSubscription` 接受一个 Flow 类型参数。与查询一样,订阅的 Flow 类型从 Relay 编译器生成的.js 文件导出。
    • 如果提供了此类型, `GraphQLSubscriptionConfig` 也会变成静态类型。**始终提供此类型是一个最佳实践。**
  • 现在,当 `useFeedbackSubscription` 钩子提交时,Relay 将建立一个订阅。
    • 与 `useLazyLoadQuery` 等 API 不同,Relay **不会** 尝试在渲染阶段建立此订阅。
  • 一旦建立,只要发生事件,后端将选择更新的 Feedback 对象并从中选择 `like_count` 字段。
    • 由于 `Feedback` 类型包含一个 `id` 字段,Relay 编译器将自动添加对 `id` 字段的选择。
  • 当收到订阅响应时,Relay 将在存储中找到具有匹配 `id` 的反馈对象,并使用新收到的 `like_count` 值对其进行更新。
  • 如果这些值因此发生变化,任何从反馈对象中选择这些字段的组件都将重新渲染。或者,通俗地说,任何依赖于更新数据的组件都将重新渲染。
注意

参数 `FeedbackLikeSubscribeData` 的类型的名称来自顶级变异字段的名称,即来自 `feedback_like_subscribe`。此类型也从生成的 `graphql.js` 文件导出。

注意

传递给 `useSubscription` 的 `GraphQLSubscriptionConfig` 对象应该被记忆化!否则,`useSubscription` 将在每次渲染时处置订阅并重新建立它!

响应订阅事件刷新组件

在前面的示例中,我们手动选择了 `like_count`。如果我们收到更新的值,选择此字段的组件将重新渲染。

但是,通常最好是扩展与我们希望响应变异进行刷新的组件相对应的片段。这是因为组件选择的数据可能会发生变化。

要求开发人员了解可能获取其组件数据的全部订阅(并保持它们更新)是 Relay 希望避免要求的全局推理的示例。

例如,我们可以将订阅改写如下

subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {
feedback_like_subscribe(data: $input) {
feedback {
...FeedbackDisplay_feedback
...FeedbackDetail_feedback
}
}
}

现在,只要 `feedback_like_subscribe` 事件流中发生事件,`FeedbackDisplay` 和 `FeedbackDetail` 组件选择的数据就会被重新获取,并且这些组件将保持一致的状态。

注意

扩展片段通常比响应订阅事件重新获取数据更可取,因为更新的数据可以在一次往返中获取。

当订阅触发、发生错误或被服务器关闭时执行回调

除了将更新后的数据写入 Relay 存储外,我们可能还希望在接收到订阅负载时执行回调。我们可能希望在接收到错误或服务器结束订阅时执行回调。 `GraphQLSubscriptionConfig` 可以包含以下字段来处理此类情况

  • `onNext`,当收到订阅负载时执行的回调。它传递订阅响应(停止在片段扩展边界处)。
  • `onError`,当订阅发生错误时执行的回调。它传递发生的错误。
  • `onCompleted`,当服务器结束订阅时执行的回调。

声明式变异指令

声明式变异指令 `@deleteRecord` 也适用于订阅。

响应订阅事件操作连接

Relay 使通过向连接(即列表)添加项目或从中删除项目来轻松响应订阅事件变得容易。例如,您可能希望将新创建的用户附加到给定的连接。有关更多信息,请参阅 使用声明式指令

响应变异删除项目

此外,您可能希望响应变异从存储中删除项目。为了做到这一点,您将在已删除的 ID 中添加 `@deleteRecord` 指令。例如

subscription DeletePostSubscription($input: DeletePostSubscribeData!) {
delete_post_subscribe(data: $input) {
deleted_post {
id @deleteRecord
}
}
}

命令式修改本地数据

有时,您希望执行的更新比仅更新字段的值更复杂,无法通过声明式变异指令处理。对于这种情况, `GraphQLSubscriptionConfig` 接受一个 `updater` 函数,它使您可以完全控制如何更新存储。

这将在有关 命令式更新存储数据 的部分中详细讨论。

配置网络层

您需要配置 网络层 来处理订阅。

通常 GraphQL 订阅通过 WebSockets 传输,以下是一个使用 graphql-ws 的示例

import {
...
Network,
Observable
} from 'relay-runtime';
import { createClient } from 'graphql-ws';

const wsClient = createClient({
url:'ws://#:3000',
});

const subscribe = (operation, variables) => {
return Observable.create((sink) => {
return wsClient.subscribe(
{
operationName: operation.name,
query: operation.text,
variables,
},
sink,
);
});
}

const network = Network.create(fetchQuery, subscribe);

或者,也可以使用传统的 subscriptions-transport-ws

import {
...
Network,
Observable
} from 'relay-runtime';
import { SubscriptionClient } from 'subscriptions-transport-ws';

const subscriptionClient = new SubscriptionClient('ws://#:3000', {
reconnect: true,
});

const subscribe = (request, variables) => {
const subscribeObservable = subscriptionClient.request({
query: request.text,
operationName: request.name,
variables,
});
// Important: Convert subscriptions-transport-ws observable type to Relay's
return Observable.from(subscribeObservable);
};

const network = Network.create(fetchQuery, subscribe);

此页面是否有用?

通过以下方式帮助我们使网站更好 回答几个快速问题.