diff --git a/issue-tracker-next-v13/.gitignore b/issue-tracker-next-v13/.gitignore index 8c8cd356..63e9ed83 100644 --- a/issue-tracker-next-v13/.gitignore +++ b/issue-tracker-next-v13/.gitignore @@ -37,3 +37,5 @@ next-env.d.ts # relay /__generated__ + +.tern-port diff --git a/issue-tracker-next-v13/README.md b/issue-tracker-next-v13/README.md index 48c1249e..c1f17c49 100644 --- a/issue-tracker-next-v13/README.md +++ b/issue-tracker-next-v13/README.md @@ -13,12 +13,11 @@ This is a simplified example of the [Issue Tracker Example](https://github.com/r ## Workflow -The query fetching is happening in the `page.tsx` async server component that is calling `loadSerializableQuery`. Then, preloaded results are passed to the root client component (`MainViewClientComponent`, for example). These preloaded results are converted into Relay's `PreloadedQuery` object, which is passed to the root Relay component that renders the query with the `usePreloadedQuery` hook. +The query fetching is happening in the `page.tsx` async server component that is calling `loadSerializableQuery`. Then, preloaded results are passed to the client component (`MainView`, for example). These preloaded results are converted into Relay's `PreloadedQuery` object, which is passed to the `usePreloadedQuery` hook. ```mermaid flowchart LR; - RSC(Root React Server Component)--Serialized Query Results-->RCC(Root Client Component); - RCC(Root Client Component)--Preloaded Query-->RRC(Root Relay Component); + RSC(Root React Server Component)--Serialized Query Results-->RCC(Relay Client Component); ``` ## Installation diff --git a/issue-tracker-next-v13/app/MainViewClientComponent.tsx b/issue-tracker-next-v13/app/MainViewClientComponent.tsx deleted file mode 100644 index a79f827d..00000000 --- a/issue-tracker-next-v13/app/MainViewClientComponent.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; - -import MainView from "src/components/MainView"; -import { useRelayEnvironment } from "react-relay"; -import { SerializablePreloadedQuery } from "src/relay/loadSerializableQuery"; -import MainViewQueryNode, { - MainViewQuery, -} from "__generated__/MainViewQuery.graphql"; -import useSerializablePreloadedQuery from "src/relay/useSerializablePreloadedQuery"; - -const MainViewClientComponent = (props: { - preloadedQuery: SerializablePreloadedQuery< - typeof MainViewQueryNode, - MainViewQuery - >; -}) => { - const environment = useRelayEnvironment(); - const queryRef = useSerializablePreloadedQuery( - environment, - props.preloadedQuery - ); - - return ; -}; - -export default MainViewClientComponent; diff --git a/issue-tracker-next-v13/app/issues/[id]/IssueViewClientComponent.tsx b/issue-tracker-next-v13/app/issues/[id]/IssueViewClientComponent.tsx deleted file mode 100644 index ae0bebc6..00000000 --- a/issue-tracker-next-v13/app/issues/[id]/IssueViewClientComponent.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import { useRelayEnvironment } from "react-relay"; -import Issue from "src/components/Issue"; -import IssueQueryNode, { IssueQuery } from "__generated__/IssueQuery.graphql"; -import { SerializablePreloadedQuery } from "src/relay/loadSerializableQuery"; -import useSerializablePreloadedQuery from "src/relay/useSerializablePreloadedQuery"; - -const Root = (props: { - preloadedQuery: SerializablePreloadedQuery; -}) => { - const environment = useRelayEnvironment(); - const queryRef = useSerializablePreloadedQuery( - environment, - props.preloadedQuery - ); - - return ; -}; - -export default Root; diff --git a/issue-tracker-next-v13/app/issues/[id]/page.tsx b/issue-tracker-next-v13/app/issues/[id]/page.tsx index 49516ff2..14ffce90 100644 --- a/issue-tracker-next-v13/app/issues/[id]/page.tsx +++ b/issue-tracker-next-v13/app/issues/[id]/page.tsx @@ -1,22 +1,22 @@ import loadSerializableQuery from "src/relay/loadSerializableQuery"; import IssueQueryNode, { IssueQuery } from "__generated__/IssueQuery.graphql"; -import IssueViewClientComponent from "./IssueViewClientComponent"; +import Issue from "src/components/Issue"; export default async function IssuePage({ params, }: { params: { id: string }; }) { - const preloadedQuery = await loadSerializableQuery< - typeof IssueQueryNode, - IssueQuery - >(IssueQueryNode.params, { - owner: "facebook", - name: "relay", - issueNumber: parseInt(params.id, 10), - }); + const preloadedQuery = await loadSerializableQuery( + IssueQueryNode, + { + owner: "facebook", + name: "relay", + issueNumber: parseInt(params.id, 10), + } + ); - return ; + return ; } export const revalidate = 0; diff --git a/issue-tracker-next-v13/app/page.tsx b/issue-tracker-next-v13/app/page.tsx index a8909071..7feeaa84 100644 --- a/issue-tracker-next-v13/app/page.tsx +++ b/issue-tracker-next-v13/app/page.tsx @@ -2,18 +2,18 @@ import loadSerializableQuery from "src/relay/loadSerializableQuery"; import MainViewQueryNode, { MainViewQuery, } from "__generated__/MainViewQuery.graphql"; -import MainViewClientComponent from "./MainViewClientComponent"; +import MainView from "src/components/MainView"; const Page = async () => { - const preloadedQuery = await loadSerializableQuery< - typeof MainViewQueryNode, - MainViewQuery - >(MainViewQueryNode.params, { - owner: "facebook", - name: "relay", - }); + const preloadedQuery = await loadSerializableQuery( + MainViewQueryNode, + { + owner: "facebook", + name: "relay", + } + ); - return ; + return ; }; export default Page; diff --git a/issue-tracker-next-v13/src/components/Issue.tsx b/issue-tracker-next-v13/src/components/Issue.tsx index 21352bd4..a8a1e3f8 100644 --- a/issue-tracker-next-v13/src/components/Issue.tsx +++ b/issue-tracker-next-v13/src/components/Issue.tsx @@ -1,8 +1,13 @@ -import { Suspense } from "react"; -import { graphql, PreloadedQuery, usePreloadedQuery } from "react-relay"; +"use client"; +import { graphql, usePreloadedQuery } from "react-relay"; +import { SerializablePreloadedQuery } from "src/relay/loadSerializableQuery"; +import useSerializablePreloadedQuery from "src/relay/useSerializablePreloadedQuery"; import { IssueQuery } from "__generated__/IssueQuery.graphql"; -export default function Issue(props: { queryRef: PreloadedQuery }) { +export default function Issue(props: { + preloadedQuery: SerializablePreloadedQuery; +}) { + const queryRef = useSerializablePreloadedQuery(props.preloadedQuery); const data = usePreloadedQuery( graphql` query IssueQuery($owner: String!, $name: String!, $issueNumber: Int!) { @@ -17,14 +22,14 @@ export default function Issue(props: { queryRef: PreloadedQuery }) { } } `, - props.queryRef + queryRef ); return ( - + {data.repository?.issue?.title} {data.repository?.issue?.bodyText} Author: {data.repository?.issue?.author?.login} - + ); } diff --git a/issue-tracker-next-v13/src/components/MainView.tsx b/issue-tracker-next-v13/src/components/MainView.tsx index 055f962d..ee5a4306 100644 --- a/issue-tracker-next-v13/src/components/MainView.tsx +++ b/issue-tracker-next-v13/src/components/MainView.tsx @@ -1,11 +1,15 @@ -import { Suspense } from "react"; -import { graphql, PreloadedQuery, usePreloadedQuery } from "react-relay"; +"use client"; + +import { graphql, usePreloadedQuery } from "react-relay"; +import { SerializablePreloadedQuery } from "src/relay/loadSerializableQuery"; +import useSerializablePreloadedQuery from "src/relay/useSerializablePreloadedQuery"; import { MainViewQuery } from "__generated__/MainViewQuery.graphql"; import Issues from "./Issues"; export default function MainView(props: { - queryRef: PreloadedQuery; + preloadedQuery: SerializablePreloadedQuery; }) { + const queryRef = useSerializablePreloadedQuery(props.preloadedQuery); const data = usePreloadedQuery( graphql` query MainViewQuery($owner: String!, $name: String!) { @@ -18,15 +22,15 @@ export default function MainView(props: { } } `, - props.queryRef + queryRef ); return ( - + {data.repository?.owner.login}/{data.repository?.name} - + ); } diff --git a/issue-tracker-next-v13/src/relay/loadSerializableQuery.ts b/issue-tracker-next-v13/src/relay/loadSerializableQuery.ts index df582723..3d4e5989 100644 --- a/issue-tracker-next-v13/src/relay/loadSerializableQuery.ts +++ b/issue-tracker-next-v13/src/relay/loadSerializableQuery.ts @@ -1,17 +1,14 @@ import { + ConcreteRequest, GraphQLResponse, OperationType, RequestParameters, VariablesOf, } from "relay-runtime"; -import { ConcreteRequest } from "relay-runtime/lib/util/RelayConcreteNode"; import { networkFetch } from "./environment"; -export interface SerializablePreloadedQuery< - TRequest extends ConcreteRequest, - TQuery extends OperationType -> { - params: TRequest["params"]; +export interface SerializablePreloadedQuery { + params: RequestParameters; variables: VariablesOf; response: GraphQLResponse; } @@ -20,15 +17,14 @@ export interface SerializablePreloadedQuery< // This response will be sent to the client to "warm" the QueryResponseCache // to avoid the client fetches. export default async function loadSerializableQuery< - TRequest extends ConcreteRequest, TQuery extends OperationType >( - params: RequestParameters, + query: ConcreteRequest, variables: VariablesOf -): Promise> { - const response = await networkFetch(params, variables); +): Promise> { + const response = await networkFetch(query.params, variables); return { - params, + params: query.params, variables, response, }; diff --git a/issue-tracker-next-v13/src/relay/useSerializablePreloadedQuery.ts b/issue-tracker-next-v13/src/relay/useSerializablePreloadedQuery.ts index 75a8beff..199ff9ed 100644 --- a/issue-tracker-next-v13/src/relay/useSerializablePreloadedQuery.ts +++ b/issue-tracker-next-v13/src/relay/useSerializablePreloadedQuery.ts @@ -2,8 +2,8 @@ // Relay's PreloadedQuery. import { useMemo } from "react"; -import { PreloadedQuery, PreloadFetchPolicy } from "react-relay"; -import { ConcreteRequest, IEnvironment, OperationType } from "relay-runtime"; +import { PreloadedQuery, PreloadFetchPolicy, useRelayEnvironment } from "react-relay"; +import { OperationType } from "relay-runtime"; import { responseCache } from "./environment"; import { SerializablePreloadedQuery } from "./loadSerializableQuery"; @@ -14,16 +14,15 @@ import { SerializablePreloadedQuery } from "./loadSerializableQuery"; // can use these cache results when fetching data // in `usePreloadedQuery`. export default function useSerializablePreloadedQuery< - TRequest extends ConcreteRequest, TQuery extends OperationType >( - environment: IEnvironment, - preloadQuery: SerializablePreloadedQuery, + preloadQuery: SerializablePreloadedQuery, fetchPolicy: PreloadFetchPolicy = "store-or-network" ): PreloadedQuery { useMemo(() => { writePreloadedQueryToCache(preloadQuery); }, [preloadQuery]); + const environment = useRelayEnvironment(); return { environment, @@ -40,9 +39,8 @@ export default function useSerializablePreloadedQuery< } function writePreloadedQueryToCache< - TRequest extends ConcreteRequest, TQuery extends OperationType ->(preloadedQueryObject: SerializablePreloadedQuery) { + >(preloadedQueryObject: SerializablePreloadedQuery) { const cacheKey = preloadedQueryObject.params.id ?? preloadedQueryObject.params.cacheID; responseCache?.set(
{data.repository?.issue?.bodyText}
Author: {data.repository?.issue?.author?.login}