跳至主要內容
版本: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 中建立訂閱,我們可以使用 useSubscriptionrequestSubscription 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 編譯器產生的檔案匯出的。
    • 如果提供此類型,GraphQLSubscriptionConfig 也會變成靜態類型。最佳做法是一律提供此類型。
  • 現在,當 useFeedbackSubscription hook 提交時,Relay 將建立訂閱。
    • useLazyLoadQuery 等 API 不同,Relay 在轉譯階段中不會嘗試建立此訂閱。
  • 一旦建立訂閱,每當發生事件時,後端將選取更新後的 Feedback 物件,並從中選取 like_count 欄位。
    • 由於 Feedback 類型包含 id 欄位,Relay 編譯器將自動新增 id 欄位的選取。
  • 收到訂閱回應時,Relay 會在商店中找到具有相符 id 的 feedback 物件,並使用新收到的 like_count 值進行更新。
  • 如果這些值因此而變更,則選取 feedback 物件上這些欄位的任何元件都會重新轉譯。或者,通俗地說,任何依賴更新資料的元件都會重新轉譯。
注意

參數 FeedbackLikeSubscribeData 的類型名稱衍生自最上層變更欄位的名稱,即 feedback_like_subscribe。此類型也會從產生的 graphql.js 檔案匯出。

注意

傳遞至 useSubscriptionGraphQLSubscriptionConfig 物件應進行記憶化!否則,useSubscription 將會處置訂閱,並在每次轉譯時重新建立!

回應訂閱事件重新整理元件

在先前的範例中,我們手動選取了 like_count。如果我們收到更新的值,選取此欄位的元件將會重新轉譯。

但是,一般而言,最好散布對應於我們想要回應變更而重新整理的元件的片段。這是因為元件選取的資料可能會變更。

要求開發人員知道所有可能會擷取其元件資料的訂閱(並保持最新狀態)是 Relay 想要避免要求的全域推理類型範例。

例如,我們可能會將訂閱重新撰寫如下

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

現在,每當發生 feedback_like_subscribe 事件串流中的事件時,FeedbackDisplayFeedbackDetail 元件選取的資料將會重新擷取,而這些元件將保持一致的狀態。

注意

一般來說,散布片段比回應訂閱事件重新擷取資料更好,因為可以在單次往返中擷取更新的資料。

在訂閱觸發、發生錯誤或由伺服器關閉時執行回呼

除了將更新的資料寫入 Relay 商店之外,我們可能還想在收到訂閱酬載時執行回呼。如果收到錯誤,或者如果收到錯誤或伺服器結束訂閱,我們可能想要執行回呼。GraphQLSubscriptionConfig 可以包含下列欄位來處理這些情況

  • onNext,在收到訂閱酬載時執行的回呼。它會傳遞訂閱回應(停在片段散布邊界)。
  • onError,在訂閱發生錯誤時執行的回呼。它會傳遞發生的錯誤。
  • onCompleted,在伺服器結束訂閱時執行的回呼。

宣告式變更指令

宣告式變更指令@deleteRecord 也適用於訂閱。

回應訂閱事件操控連線

Relay 可讓您輕鬆回應訂閱事件,方法是在連線(即清單)中新增或移除項目。例如,您可能想要將新建立的使用者附加至指定的連線。如需詳細資訊,請參閱使用宣告式指令

回應變更刪除項目

此外,您可能想要回應變更而從商店中刪除項目。為了執行此操作,您會將 @deleteRecord 指令新增至已刪除的 ID。例如

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://localhost: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://localhost: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);

此頁面是否實用?

請協助我們讓網站變得更好,方法是 回答幾個快速問題.