What I Tried to Learn at Work But Had To Wait For The Break This Week: React Relay Fragments

Mike Diaz
6 min readDec 28, 2023
Photo by Alexander Grey: https://www.pexels.com/photo/multicolored-broken-mirror-decor-1407278/

Extremely loyal readers will remember that the company I work for has a winter break. This year, we get *five* extra days off around the holiday season where the whole company shuts down. This doesn’t include the national holidays on December 25th and January 1st, so it’s pretty sweet. I highly recommend it to any company that wants to promote “work-life balance” or “mental health for our employees.” Taking time off is always helpful, but it’s not helpful to come back to an overflowing inbox reminding you of the work you didn’t do while you were traveling, or relaxing, or taking care of some personal business that couldn’t wait.

When the whole company is off, I don’t get slack DMs “for when you get back” or @channels that the channel is waiting for me to answer. The whole company is off! And that, in my experience, is the only way that I can truly relax when I’m off (I realize this also says something about my mental state, which of course I’m working on). So kudos to my company, and I hope you, dear reader, will get to experience something similar in your future, if you aren’t already.

Because our society so often conflates relaxation with complacency or laziness, I can only spend so much time on it until I feel a deep compulsion to…do some work. During the break, I’ve spent a couple of hours each day going through the React Relay tutorial on relay.dev. I’ve gotten much better at writing back end code this year, but working with Relay and GraphQL has been a struggle for me lately. This felt like the perfect time to get back to basics and see if I can finally understand the fundamental concepts of these frameworks. That led me to GraphQL query construction, which is fairly straightforward, and then to fragments, which…isn’t.

GraphQL

From the tutorial:

GraphQL is a language for querying and modifying data on servers. The unique thing about GraphQL is that rather than having a fixed set of API endpoints, your server provides a palette of options that the client can use to request any combination of data that it may need.

In other words, an API is sort of an all-or-nothing proposition. If the endpoint provides a user’s ID, first name, last name, age, and location, that’s the info I’m going to get every time I query that endpoint. GraphQL gives us more flexibility with what we’re querying, how often queries are made, and how the data is stored, which allows us to develop more efficiently on the front end.

The tutorial provides this query in their first example:

query {
person(id: "24601") {
name
occupation
location {
name
population
}
}
}

It explains that GraphQL organizes data into a graph of nodes and edges. In this example, the first node we see is a person. The person node has name and occupation properties. Next comes location, which is an edge that connects the person to another node. That node includes name and population. If you think in terms of SQL like I do, it’s like person and location are different tables, but we don’t have to write a JOIN to bring them together.

Relay

Even though GraphQL is so much better than using an API, it’s still preferred that we limit the number of queries we make for efficiency’s sake. This means making one request per screen instead of one query per component, I think because there are fewer screens than components. The downside, though, would be updating the request on each screen whenever a component changed. Add a field to the component? Better find every implementation and update the screen’s request. That’s where Relay comes in, per the tutorial:

Relay’s unique strength is to avoid this tradeoff by letting each component declare its own data requirements locally, but then stitching those requirements together into larger queries. That way you get both performance and maintainability.

Relay does this with a compiler that scans your JavaScript code for fragments of GraphQL, and then stitches those fragments together into complete queries.

And there’s that word: fragments. Earlier, we saw a fully-formed query, but what if each component had a partial query that would be put together, turned into a request, and stored in a cache (another benefit of Relay) for each screen? Perfect!

A Query And A Fragment

Let’s continue with the tutorial and check out the query they give us for the sample code:

import { graphql } from 'relay-runtime';

const NewsfeedQuery = graphql`
query NewsfeedQuery {
topStory {
title
summary
poster {
name
profilePicture {
url
}
}
thumbnail {
url
}
}
}
`;

graphql is a tag that Relay uses to embed the query into JavaScript. Next, we declare the query with a keyword (`query`) and a name that follows the format {Name}Query.

The tutorial refers to fields that return basic values like strings as scalar fields. title and summary in this case are scalar. poster, as we saw earlier, is an edge that connects to a new part of the graph and returns another scalar (name) and another edge (profilePicture).

Once this is saved in one of our components, we can run the Relay compiler and see a generated file with the actual GraphQL query syntax. As our program gets more complicated, that generated query will get larger, and we’ll be more grateful that Relay is compiling it so we don’t have to. Relay also provides us with a function to execute the query:

import { useLazyLoadQuery } from "react-relay";

export default function Newsfeed({}) {
const data = useLazyLoadQuery(
NewsfeedQuery,
{},
);
const story = data.topStory;
// As before:
return (
<div className="newsfeed">
<Story story={story} />
</div>
);
}

In our Newsfeed component, we run useLazyLoadQuery and then parse the results to access the data we want to display. The display itself is specified in the Story component, so it’s kind of strange that the query itself would exist in Newsfeed. Maybe in the future, Newsfeed will execute components other than Story, but it’s still counterintuitive that, if the Story component changes, we’d have to alter the NewsfeedQuery. And that’s what fragments are for.

Fragments

Again linking to the tutorial, we’re given this code for our Story fragment:

import { graphql } from 'relay-runtime';

const StoryFragment = graphql`
fragment StoryFragment on Story {
title
summary
createdAt
poster {
name
profilePicture {
url
}
}
thumbnail {
url
}
}
`;

This looks a lot like the previous query we wrote, except:

  • we use the fragment keyword instead of the query keyword
  • we wrote on Story after the fragment name. This indicates that this fragment can be used wherever we have a story node in the graph.
  • We don’t wrap the fields in topStory, but start with the scalar values.

That’s because the topStory field still exists in the original NewsfeedQuery. We’re going to alter that query by spreading our new fragment:

const NewsfeedQuery = graphql`
query NewsfeedQuery {
topStory {
...StoryFragment
}
}
`;

The Relay compiler can make the connection between the spread notation and the fragment declared in Story, but we can no longer simply pass those results down the component chain. Instead, we have to alter the Story component to pull the relevant data out of the object returned by useLazyLoadQuery:

import { useFragment } from 'react-relay';

export default function Story({story}: Props) {
const data = useFragment(
StoryFragment,
story,
);
return (
<Card>
<Heading>{data.title}</Heading>
<PosterByline person={data.poster} />
<Timestamp time={data.createdAt} />
<Image image={data.image} />
<StorySummary summary={data.summary} />
</Card>
);
}

The useFragment hook accepts the fragment we defined in the component and the data passed down from the parent component. It un-masks the relevant fields from that data, which helps create a separation of concerns where unnecessary data isn’t being passed around to various components. Now, if we decide to change what’s collected in StoryFragment, we don’t have to worry that some other child of Newsfeed isn’t also using it.

The Basics

This post took me about two days and 2.5 read-throughs of the relay.dev tutorial, and this is the most basic implementation of the principles! I’m grateful that I had the time to dig into it and re-read things until I understood them, and I hope that the readers get similar flexibility from their managers or their instructors. Remember that it takes time for information to sink in and that not everyone’s brain works the same way. If you’re ever struggling with a concept, don’t hesitate to take a break. Sometimes that’s the only strategy that will work.

Sources

--

--