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);
此页面是否有用?
通过以下方式帮助我们使网站更好 回答几个快速问题.