跳到主要內容
版本:v18.0.0

查詢

GraphQL 查詢是用於描述您想從 GraphQL 伺服器查詢的資料。它由一組欄位(以及可能有的片段)組成,我們想要從 GraphQL 伺服器請求這些欄位。我們可以查詢的內容將取決於伺服器上公開的GraphQL Schema,該 Schema 描述了可供查詢的資料。

查詢可以作為網路請求發送,同時還可以選擇性地附加查詢使用的變數集合,以便擷取資料。伺服器的回應將是一個符合我們發送的查詢形狀的 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 Hook。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 Schema 的類型。例如,上述 data 的類型將為:{ user: ?{ name: ?string } }
  • 在嘗試渲染查詢之前,請確保您使用Relay Environment Provider 在應用程式的根目錄中提供 Relay 環境。

擷取用於渲染的查詢

除了渲染查詢外,我們還需要從伺服器擷取查詢。通常,我們希望在應用程式的根目錄某處擷取查詢,並且只有一個或少數幾個查詢累積渲染螢幕所需的所有資料。理想情況下,我們應該盡可能早地擷取它們,甚至在我們開始渲染應用程式之前。

為了擷取用於稍後渲染的查詢,您可以使用 useQueryLoader Hook

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 (我們在上一個範例中宣告的查詢),我們可以透過 require 自動產生檔案來取得:'HomeTabQuery.graphql'
    • 它會接收一個可選的初始 PreloadedQuery,作為儲存在狀態中並由 useQueryLoader 傳回的 homeTabQueryRef 的初始值。
    • 此外,它還會接收一個 Flow 類型參數,該參數對應於查詢的 Flow 類型,在此案例中為 HomeTabQueryType,您也可以從自動產生的檔案中取得:'HomeTabQuery.graphql'
  • 呼叫 useQueryLoader 可讓我們取得 2 個東西
    • homeTabQueryRef:一個 ?PreloadedQuery,它是一個物件,描述並參考我們正在 (或曾經) 擷取的查詢的實例。如果我們尚未擷取查詢,也就是如果我們尚未呼叫 loadHomeTabQuery,則此值將為 null。
    • loadHomeTabQuery:一個函數,將從伺服器擷取此查詢的資料(如果尚未快取),並給定一個包含查詢期望的變數的物件,在此案例中為 {id: '4'}(我們將在重複使用快取資料進行渲染章節中詳細介紹 Relay 如何使用快取資料)。呼叫此函數也會將 homeTabQueryRef 的值更新為 PreloadedQuery 的實例。
      • 請注意,我們傳遞給此函數的 variables 將由 Flow 檢查,以確保您傳遞的值與 GraphQL 查詢期望的值相符。
      • 另請注意,我們正在導致渲染 HomeTab 的事件處理常式中呼叫此函數。這讓我們可以盡可能早地開始擷取螢幕的資料,甚至在新標籤開始渲染之前。
        • 事實上,如果在 React 的渲染階段呼叫 loadQuery,它將會擲回錯誤!
  • 請注意,當元件卸載時,useQueryLoader 將自動處置所有已載入的查詢。處置查詢表示 Relay 將不再保留該查詢特定實例的資料在其快取中(我們將在重複使用快取資料進行渲染章節中介紹查詢資料的生命週期)。此外,如果查詢的請求在處置發生時仍在傳輸中,則會取消該請求。
  • 我們的 AppTabs 元件會渲染上一個範例中的 HomeTab 元件,並將對應的查詢參考傳遞給它。請注意,這個父元件擁有該查詢的資料生命週期,這表示當它卸載時,它將如上所述處置該查詢。
  • 最後,在嘗試使用 useQueryLoader 之前,請確保您使用Relay Environment Provider 在應用程式的根目錄中提供 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 元件會管理查詢參考的生命週期,並在適當的時候處置它,如果有的話。
  • 我們在此範例中模糊地描述了「應用程式初始化」的細節,因為這會因應用程式而異。這裡需要注意的重點是,我們應該在開始渲染根元件之前取得查詢參考。事實上,如果在 React 的渲染階段呼叫 loadQuery,它將會擲回錯誤!

邊擷取邊渲染

上面的範例說明如何將擷取資料與渲染資料分開,以便盡可能早地開始擷取(而不是等到元件渲染後才開始擷取),並讓我們能夠更快地向使用者顯示內容。它還有助於防止瀑布式的往返,並讓我們對擷取發生的時間有更多的控制和可預測性,而如果我們在渲染期間擷取,則更難確定擷取何時(或應該)發生。這與「邊擷取邊渲染」模式和React Suspense 完美契合。

這是使用 Relay 擷取資料的慣用模式,它適用於多種情況,例如應用程式的初始載入、後續導航期間,或通常當使用最初隱藏並在互動時稍後顯示的 UI 元素(例如選單、彈出視窗、對話方塊等),並且這些元素也需要擷取其他資料時。

在渲染期間延遲擷取查詢

擷取查詢的另一種方法是在渲染元件時延遲擷取查詢。然而,正如我們之前提到的,慣用模式是在渲染之前開始擷取查詢。如果沒有小心使用延遲擷取,則可能會觸發巢狀或瀑布式的往返,並降低效能。

若要延遲擷取查詢,您可以使用 useLazyLoadQuery Hook

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 查詢期望的值相符。
    • 請注意,資料已經正確地使用 Flow 類型標註,無需顯式註解,並且是基於 GraphQL schema 中的類型。例如,上面 data 的類型會是:{ user: ?{ name: ?string } }
  • 預設情況下,當元件渲染時,Relay 會提取此查詢的資料(如果尚未快取),並將其作為 useLazyLoadQuery 呼叫的結果返回。我們將在使用 Suspense 的載入狀態部分中更詳細地介紹如何顯示載入狀態,以及在重複使用快取資料進行渲染部分中說明 Relay 如何使用快取資料。
  • 請注意,如果您重新渲染您的元件並傳遞與最初使用的不同的查詢變數,它將導致使用新的變數重新提取查詢,並可能使用不同的資料重新渲染。
  • 最後,在嘗試渲染查詢之前,請確保您在應用程式的根部使用 Relay Environment Provider 提供 Relay 環境。

這個頁面有用嗎?

請幫我們回答幾個快速問題,讓網站變得更好 回答幾個快速問題.