Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next 13 example: Refactor to simplify #294

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions issue-tracker-next-v13/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ next-env.d.ts

# relay
/__generated__

.tern-port
5 changes: 2 additions & 3 deletions issue-tracker-next-v13/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 0 additions & 26 deletions issue-tracker-next-v13/app/MainViewClientComponent.tsx

This file was deleted.

This file was deleted.

20 changes: 10 additions & 10 deletions issue-tracker-next-v13/app/issues/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -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<IssueQuery>(
IssueQueryNode,
{
owner: "facebook",
name: "relay",
issueNumber: parseInt(params.id, 10),
}
);

return <IssueViewClientComponent preloadedQuery={preloadedQuery} />;
return <Issue preloadedQuery={preloadedQuery} />;
}

export const revalidate = 0;
18 changes: 9 additions & 9 deletions issue-tracker-next-v13/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<MainViewQuery>(
MainViewQueryNode,
{
owner: "facebook",
name: "relay",
}
);

return <MainViewClientComponent preloadedQuery={preloadedQuery} />;
return <MainView preloadedQuery={preloadedQuery} />;
};

export default Page;
Expand Down
17 changes: 11 additions & 6 deletions issue-tracker-next-v13/src/components/Issue.tsx
Original file line number Diff line number Diff line change
@@ -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<IssueQuery> }) {
export default function Issue(props: {
preloadedQuery: SerializablePreloadedQuery<IssueQuery>;
}) {
const queryRef = useSerializablePreloadedQuery(props.preloadedQuery);
const data = usePreloadedQuery(
graphql`
query IssueQuery($owner: String!, $name: String!, $issueNumber: Int!) {
Expand All @@ -17,14 +22,14 @@ export default function Issue(props: { queryRef: PreloadedQuery<IssueQuery> }) {
}
}
`,
props.queryRef
queryRef
);

return (
<Suspense fallback="Loading (client side)...">
<div>
<h1>{data.repository?.issue?.title}</h1>
<p>{data.repository?.issue?.bodyText}</p>
<p>Author: {data.repository?.issue?.author?.login}</p>
</Suspense>
</div>
);
}
16 changes: 10 additions & 6 deletions issue-tracker-next-v13/src/components/MainView.tsx
Original file line number Diff line number Diff line change
@@ -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<MainViewQuery>;
preloadedQuery: SerializablePreloadedQuery<MainViewQuery>;
}) {
const queryRef = useSerializablePreloadedQuery(props.preloadedQuery);
const data = usePreloadedQuery(
graphql`
query MainViewQuery($owner: String!, $name: String!) {
Expand All @@ -18,15 +22,15 @@ export default function MainView(props: {
}
}
`,
props.queryRef
queryRef
);

return (
<Suspense fallback="Loading (client side)...">
<div>
<h1>
{data.repository?.owner.login}/{data.repository?.name}
</h1>
<Issues repository={data.repository} />
</Suspense>
</div>
);
}
18 changes: 7 additions & 11 deletions issue-tracker-next-v13/src/relay/loadSerializableQuery.ts
Original file line number Diff line number Diff line change
@@ -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<TQuery extends OperationType> {
params: RequestParameters;
variables: VariablesOf<TQuery>;
response: GraphQLResponse;
}
Expand All @@ -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<TQuery>
): Promise<SerializablePreloadedQuery<TRequest, TQuery>> {
const response = await networkFetch(params, variables);
): Promise<SerializablePreloadedQuery<TQuery>> {
const response = await networkFetch(query.params, variables);
return {
params,
params: query.params,
variables,
response,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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<TRequest, TQuery>,
preloadQuery: SerializablePreloadedQuery<TQuery>,
fetchPolicy: PreloadFetchPolicy = "store-or-network"
): PreloadedQuery<TQuery> {
useMemo(() => {
writePreloadedQueryToCache(preloadQuery);
}, [preloadQuery]);
const environment = useRelayEnvironment();

return {
environment,
Expand All @@ -40,9 +39,8 @@ export default function useSerializablePreloadedQuery<
}

function writePreloadedQueryToCache<
TRequest extends ConcreteRequest,
TQuery extends OperationType
>(preloadedQueryObject: SerializablePreloadedQuery<TRequest, TQuery>) {
>(preloadedQueryObject: SerializablePreloadedQuery<TQuery>) {
const cacheKey =
preloadedQueryObject.params.id ?? preloadedQueryObject.params.cacheID;
responseCache?.set(
Expand Down