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

add RFC-229 based way with use and Suspense for data fetching #62

Merged
merged 6 commits into from
Apr 2, 2023
Merged
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
"eslint-config-next": "13.2.4",
"next": "13.2.4",
"prettier": "2.8.7",
"react": "18.2.0",
"react-dom": "18.2.0",
"react": "^18.3.0-next",
"react-dom": "^18.3.0-next",
"typescript": "4.9.5"
},
"packageManager": "yarn@3.5.0"
Expand Down
21 changes: 18 additions & 3 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Heading } from '@chakra-ui/react';
import { Box, Heading, UnorderedList } from '@chakra-ui/react';
import Head from 'next/head';

import InternalLink from '../components/InternalLink';
Expand All @@ -14,7 +14,22 @@ export default function Index() {

<Heading>What is it?</Heading>

<ul>
<Box>This is a demo of React.Suspense in data fetching scenarios.</Box>
<Box>
Main use case is typically showing a loading spinner.
<br />
Suspense helps to simplify the code structure for handling asynchronously data fetched even when it is
done incrementally.
</Box>

<Heading size="medium">Choose one of these different scenarios:</Heading>

<UnorderedList>
<li>
🆕<InternalLink href={'/next'}>Experimental, RFC-229</InternalLink>{' '}
<strong>use(await fetch)</strong> Fetching all data and use latest support of async data fetching
via `const data = use(await fetch)`
</li>
<li>
<InternalLink href={'/restful'}>Old way</InternalLink> <strong>All or nothing:</strong> Fetching all
data in a top-level `useEffect()` + props-drilling
Expand All @@ -31,7 +46,7 @@ export default function Index() {
<InternalLink href={'/side-by-side'}>Side-by-Side</InternalLink> <strong>Comparison:</strong> Show
incrementally loading and wait-for-all
</li>
</ul>
</UnorderedList>
</div>
);
}
69 changes: 69 additions & 0 deletions pages/next/[userName]/[repoName].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client';
import React, { use, Suspense, cache } from 'react';

import { fetchRepoBranchesWithCommitStatusesAndPullRequests, fetchUser, User } from '../../../restinpeace/github';
import { UserRepoFromUrlProvider, useUserRepo } from '../../../components/useUserRepoFromRoute';
import UserRepo from '../../../container/UserRepo';
import RichErrorBoundary from '../../../components/RichErrorBoundary';
import InternalLink from '../../../components/InternalLink';
import { Spinner } from '../../../components/Spinner';
import { RepoType } from '../../../components/Repo';

function delay(timeout) {
return new Promise((resolve) => {
setTimeout(resolve, timeout);
});
}

export default function ReactNextDrivenPage() {
return (
<UserRepoFromUrlProvider>
<ReactNextWithUrlContext />
</UserRepoFromUrlProvider>
);
}

function ReactNextWithUrlContext() {
const { userName, repoName } = useUserRepo();
const userData: Promise<User> = fetchUserPromise(userName);
const repoData: Promise<RepoType> = fetchRepoBranches({ userName, repoName });
return (
<UserRepoFromUrlProvider>
<RichErrorBoundary>
<InternalLink href={'/next'}>back to repos</InternalLink>
<Suspense fallback={<Spinner />}>
<ReactNext userData={userData} repoData={repoData} />
</Suspense>
</RichErrorBoundary>
</UserRepoFromUrlProvider>
);
}

const fetchUserPromise: (id) => Promise<User> = cache(async (id) => {
await delay(2000);
return fetchUser(id);
});
const fetchRepoBranches: ({ userName, repoName }) => Promise<RepoType> = cache(async ({ userName, repoName }) => {
await delay(2000);
return fetchRepoBranchesWithCommitStatusesAndPullRequestsProm({ userName, repoName });
});

interface Props {
userData: Promise<User>;
repoData: Promise<RepoType>;
}

function ReactNext({ userData, repoData }: Props) {
const user = use(userData);
const repo: RepoType = use(repoData);

return <UserRepo user={user} repo={repo} />;
}

const fetchRepoBranchesWithCommitStatusesAndPullRequestsProm = cache(async ({ userName, repoName }) =>
fetchRepoBranchesWithCommitStatusesAndPullRequests({ userName, repoName }).then((branchesWithCommit) => ({
name: repoName,
owner: { login: userName },
branches: branchesWithCommit.branches,
}))
);
27 changes: 27 additions & 0 deletions pages/next/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client'; // this directive should be at top of the file, before any imports.

import React from 'react';
import { Box, Heading, Link } from '@chakra-ui/react';

import InternalLink from '../../components/InternalLink';
import { LinkList } from '../../components/LinkList';

export default function Shortcuts() {
return (
<div>
<InternalLink href={'/'}>back to main page</InternalLink>

<Heading>Experimental, RFC-229</Heading>
<Box mb={6}>
<strong>use(await fetch)</strong> Fetching all data and use latest support of async data fetching via
`const data = use(await fetch)`
</Box>
<Box mb={6}>
See <Link href="https://github.com/reactjs/rfcs/pull/229">GH/reactjs/rfcs/pull/229</Link> for mor
details
</Box>

<LinkList rootPath="/next" />
</div>
);
}
32 changes: 16 additions & 16 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4318,15 +4318,15 @@ __metadata:
languageName: node
linkType: hard

"react-dom@npm:18.2.0":
version: 18.2.0
resolution: "react-dom@npm:18.2.0"
"react-dom@npm:^18.3.0-next":
version: 18.3.0-next-fecc288b7-20221025
resolution: "react-dom@npm:18.3.0-next-fecc288b7-20221025"
dependencies:
loose-envify: ^1.1.0
scheduler: ^0.23.0
scheduler: 0.24.0-next-fecc288b7-20221025
peerDependencies:
react: ^18.2.0
checksum: 7d323310bea3a91be2965f9468d552f201b1c27891e45ddc2d6b8f717680c95a75ae0bc1e3f5cf41472446a2589a75aed4483aee8169287909fcd59ad149e8cc
react: 18.3.0-next-fecc288b7-20221025
checksum: bee2c161c37498974816abaf191767186473422d7642f7b333c039a046a0c1d724bc7a1482d714a9fff0f95287ee62909f2e903837a966a2fde1bca50fbf0c45
languageName: node
linkType: hard

Expand Down Expand Up @@ -4454,19 +4454,19 @@ __metadata:
hitchcock: 0.5.0
next: 13.2.4
prettier: 2.8.7
react: 18.2.0
react: ^18.3.0-next
react-cache: latest
react-dom: 18.2.0
react-dom: ^18.3.0-next
typescript: 4.9.5
languageName: unknown
linkType: soft

"react@npm:18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
"react@npm:^18.3.0-next":
version: 18.3.0-next-fecc288b7-20221025
resolution: "react@npm:18.3.0-next-fecc288b7-20221025"
dependencies:
loose-envify: ^1.1.0
checksum: 88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b
checksum: 860e2a595be0f3c75707ae4850490b367f1e6fb4275c1af9a33f234a9d47d9202ad49c51366a3b9d43d145c6fe106dde1089d93d7227efaadcaa4b8432122cb7
languageName: node
linkType: hard

Expand Down Expand Up @@ -4592,12 +4592,12 @@ __metadata:
languageName: node
linkType: hard

"scheduler@npm:^0.23.0":
version: 0.23.0
resolution: "scheduler@npm:0.23.0"
"scheduler@npm:0.24.0-next-fecc288b7-20221025":
version: 0.24.0-next-fecc288b7-20221025
resolution: "scheduler@npm:0.24.0-next-fecc288b7-20221025"
dependencies:
loose-envify: ^1.1.0
checksum: d79192eeaa12abef860c195ea45d37cbf2bbf5f66e3c4dcd16f54a7da53b17788a70d109ee3d3dde1a0fd50e6a8fc171f4300356c5aee4fc0171de526bf35f8a
checksum: c51ecea03ce23c2cb27bfd6c0bb350722a6d7d9d692e43d195f0397fe29ce19f87e9f1a58939eab89b2535f1b87f45c52b898f3cfe2b5eeb1c78a6a97d53c117
languageName: node
linkType: hard

Expand Down