Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save simonespa/18fe6cf2821f108e17d41fde840116a5 to your computer and use it in GitHub Desktop.
Save simonespa/18fe6cf2821f108e17d41fde840116a5 to your computer and use it in GitHub Desktop.
Data Fetching with React Server Components

Data Fetching with React Server Components

Notes taken from https://www.youtube.com/watch?v=TQQPAU21ZUw

Properties

Good User Experience Cheap maintenance Fast performance

Example:

function ArtistPage({ artistId }) {
	return (
		<ArtistDetails artistId={artistId}>
			<TopTracks artistId={artistId} />
			<Discography artistId={artistId} />
		</ArtistDetails>
	)
}

Approach 1.

Compromise on the maintenance

  • Good user experience
  • Fast performance (because it fetches only once)

but we loose the Cheap element because the component are not responsible for their own data requirements.

function ArtistPage({ artistId }) {
	const stuff = fetchAllTheStuff();
	return (
		<ArtistDetails artistId={artistId} details={stuff.details}>
			<TopTracks artistId={artistId} topTracks={stuff.topTracks} />
			<Discography artistId={artistId} discography={stuff.discography} />
		</ArtistDetails>
	)
}

Need to pass data down, this is how React work.

  • There is a problem. The API response is coupled with the component.
  • If you want to remove a component, e.g. TopTracks, you may forget to remove its related data from the API response.
  • Also, if I later add a new property to one of the components, I'll need to update the data fetching part to pull that in, and this for every single time I use this pattern everywhere in the app.

Approach 2.

Compromise in user experience

  • Cheap
  • Fast

We go back with each component fetching their own data, but when we go to the artist page we may see Discography first, then the artist page with the furniture and then TopTracks pushes the layout down.

function ArtistPage({ artistId }) {
	return (
		<ArtistDetails artistId={artistId}>
			<TopTracks artistId={artistId} />
			<Discography artistId={artistId} />
		</ArtistDetails>
	)
}

with

function ArtistDetails({ artistId, children }) {
	const details = fetchDetails(artistId);
	// ...
}

function TopTracks({ artistId }) {
	const topTracks = fetchTopTracks(artistId);
	// ...
}

function Discography({ artistId }) {
	const discography = fetchDiscography(artistId);
	// ...
}

There are subtle things with co-locating the data fetching part with the component that uses it.

It gives a way to specify and clarify where do they get the data from without having to track it back from the root, passing via the chain of props.

Relay and GraphQL

Each component that wants some data specify which data needs through graphql fragment and Relay is this thing that composes these fragments together so that when we talk to the server we get all of them in one go.

Of course, this is not a solution that works for every cases. For example, you may already have a REST API, that's ok. It's not necessarily for all cases.

How can we then solve the Good, Cheap and Fast constraints issue?

The problem is the waterfall of requests amplified by the fact that there is a certain amount of latency between the server and the client.

One way to fix the problem is to fix the waterfall part as GraphQL does, but there is also another solution, which is to move our components to the server.

  • Server components were originally designed to solve the waterfall problem, but it was later found that they also solves many other problems.
  • Server components cannot have any interactivity (no event listeners or state hooks)
  • Server components can import client components.
  • Server components can pass props to the imported client components, but only if they are serialisable over the network (e.g. JSON and JSX components). This means that functions can't be passed because can't be serialised. React would throw an error.
  • When we pass server components as props (i.e. childrens) we are not actually passing the component itself, but the serialised JSX that is defined within the return statement. This means that the rendering happens on the server and the client only receives the stringified fragment of it

Server components are different from SSR.

SSR

SSR takes the client-side application javascript, renders on the server into HTML and serve it to the client really quickly and the client can already see something while the JS is still downloading.

Server Components

With server components the client-side state is preserved.

Server components don't render to HTML, they render in a special format.

SC and SSR are complementary, they can be used together. SC we are able to preserve the Client-side state even when the SC tree is fetched. For example, if the expanded notes are stored client side, and then we refetch the server component tree, the client-side state doesn't reset loosing the data. We'll still see the expanded notes and the "expanded state" will be preserved too.

Example of client-side state being preserved

In this example, the SidebarNote.client.js component is responsible for the interactivity in the sidebar. When we click on the button we want the ability to tell our server to re-render the server component tree, but this time with the selected note rendered into the preview.

The infrastructure can refetch the server component tree with the new visual. In the demo the following function is used:

setLocation((loc) => ({
	...loc,
	selectedId: id
}))

within a "startTransition" function (23:48)

That function triggers a re-fetch of the new server component tree with the properties being passed to the server, the server receives those props, re-renders the server component tree starting from the root (in this demo is the App component).

What happens is the re-rendered server component tree being sent to the client.

Shared components

Shared components only have the ".js" extension (unlike "server.js" and "client.js") Shared components can be used on both the server and the client. Shared components can be code splitted and downloaded on demand when needed. Example in this demo is the "PreviewEdit" component

Transitions

Transitions allow us to respond immediately while the server sends the server component tree down to the client.

Pros of Server Components

Server components have zero effect on the bundle size. Server components let you access the backend resources directly (e.g. file system, db access, etc.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment