Skip to content

Commit

Permalink
Add deferred Episodes section in the character's route
Browse files Browse the repository at this point in the history
  • Loading branch information
juanpprieto committed Nov 14, 2023
1 parent 45fd944 commit b811cfe
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 18 deletions.
5 changes: 4 additions & 1 deletion app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {useLoaderData, Link} from '@remix-run/react';
import {CacheShort} from '@shopify/hydrogen';

export async function loader({context}: LoaderFunctionArgs) {
// NOTE: We're using a short cache here to avoid re-fetching the characters
const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, {
cache: CacheShort(),
});
Expand All @@ -24,7 +25,9 @@ export default function Homepage() {
{(characters.results || []).map(
(character: Character, index: number) => (
<li key={character.name + index}>
<Link to={'/characters/' + character.id}>{character.name}</Link>
<Link prefetch="intent" to={'/characters/' + character.id}>
{character.name}
</Link>
</li>
),
)}
Expand Down
138 changes: 132 additions & 6 deletions app/routes/characters.$id.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, Link} from '@remix-run/react';
import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, Link, Await} from '@remix-run/react';
import {Suspense} from 'react';
import {CacheNone} from '@shopify/hydrogen';
import pMinDelay from 'p-min-delay';

export async function loader({context, params}: LoaderFunctionArgs) {
// Fetch character data
// Initialize a promise to fetch episodes data with a delay to simulate
// a slow connection or a slow GraphQL server
const episodesPromise = pMinDelay(
context.rickAndMorty.query(CHARACTER_EPISODES_QUERY, {
variables: {
id: params.id,
},
cache: CacheNone(),
}),
2000,
);

// Await fetch the "critical" above the fold character data
const {character} = await context.rickAndMorty.query(CHARACTER_QUERY, {
variables: {
id: params.id,
},
cache: CacheNone(),
});
return json({character});

return defer({character, episodesPromise});
}

export default function Character() {
const {character} = useLoaderData<typeof loader>();
const {character, episodesPromise} = useLoaderData<typeof loader>();
return (
<div>
<Link to="/">Back to all characters</Link>
<Link prefetch="intent" to="/">
Back to all characters
</Link>
<h1>{character.name}</h1>
<img src={character.image} alt={character.name} />
<ul>
Expand All @@ -44,6 +61,98 @@ export default function Character() {
{character.location.name}
</li>
</ul>

<hr />

<Suspense fallback={<EpisodesGrid />}>
<Await
resolve={episodesPromise}
errorElement={<p>There was an error while fetching the episodes</p>}
>
{(data) => {
if (!data?.character?.episode) {
return <NoEpisodes />;
}
return (
<EpisodesGrid
episodes={data.character.episode as Array<Episode>}
/>
);
}}
</Await>
</Suspense>
</div>
);
}

type Episode = {
name: string;
air_date: string;
id: string;
characters: {
name: string;
image: string;
status: string;
}[];
};

function EpisodesGrid({episodes}: {episodes?: Episode[]}) {
const placeholderEpisode = {
name: 'Loading...',
air_date: '',
id: '',
characters: [{}, {}, {}],
};

let episodeNodes = [];

if (!episodes?.length) {
episodeNodes = new Array(4).fill(placeholderEpisode);
} else {
episodeNodes = episodes;
}

return (
<div>
<br />
<h2>Episodes</h2>
<p>
Here we demonstrate Remix's deferred data fetching and streaming. With
the help of <code>defer</code>, <code>Suspense</code> and{' '}
<code>Await</code> we can render a placeholder while the request for
episodes is ready without blocking the rendering process of the page
</p>
<br />
<ul
style={{
display: 'grid',
gridGap: '1rem',
gridTemplateColumns: 'repeat(auto-fill, 200px) 20%',
}}
>
{episodeNodes.slice(0, 4).map((episode, index) => (
<li
style={{
padding: '.5rem',
border: '1px solid black',
minHeight: '100px',
backgroundColor: episode.id ? 'white' : '#eee',
}}
key={episode.id + index}
>
{episode.name}
</li>
))}
</ul>
</div>
);
}

export function NoEpisodes() {
return (
<div>
<h2>Episodes</h2>
<p>Whoops, no episodes found!</p>
</div>
);
}
Expand All @@ -67,3 +176,20 @@ const CHARACTER_QUERY = `#graphql:rickAndMorty
}
}
`;

const CHARACTER_EPISODES_QUERY = `#graphql:rickAndMorty
query ($id: ID!) {
character(id: $id) {
episode {
id
name
air_date
characters {
name
image
status
}
}
}
}
`;
Loading

0 comments on commit b811cfe

Please sign in to comment.