Queries: Loading Links

Preparing the React components

The first piece of functionality that you’ll implement in the app is loading and displaying a list of Link elements. You’ll walk up our way in the React component hierarchy and start with the component that’ll render a single link.

This is a simple React component that expects a link in its props and renders the link’s description and url. Easy as pie! 🍰

Next, you’ll implement the component that renders a list of links.

Here, you’re using mock data for now to make sure the component setup works. You’ll soon replace this with some actual data that’s loaded from the server - patience, young Padawan!

Note: The project that was generated by create-react-app uses semicolons and double quotes for strings. All the code that you’re going to write in this tutorial will use no semicolons and single quotes.

Run the app by calling yarn start to check if everything works so far! The app should now display the two links from the linksToRender array:

Run the app by calling `yarn start`

Loading Data from the Server

In this section, you’ll learn how you can actually load data from the server instead of rendering the links simply from a local array.

But before you go and make the required changes, a bit of theory!

Colocation and GraphQL Fragments

One of the most powerful concepts of Relay is called colocation. This means that a React component declares its data dependencies right next to (i.e. in the same file) where it’s defined. This happens in the form of GraphQL fragments.

This effectively means that you’ll never write any actual GraphQL queries yourself. This is unlike the approach that’s taken in Apollo, where you’re also able to colocate data dependencies and React components - but are most commonly doing so by writing actual queries instead of fragments.

But if you’re never writing any queries in Relay, how can the GraphQL server respond with sensible data?

That’s the cool part about Relay! Under the hood, it will figure out the most efficient way for your React components to fetch the data that’s required for them to render, based on the data dependencies they declared in their fragments.

You don’t have to worry about fetching the data one bit - all networking and caching logic is abstracted away and you can focus on writing your React components and what data they need! Declarative data fetching ftw 😎

Fragment Containers

The way to declare the data dependencies alongside your React components is by using the FragmentContainer API.

The function createFragmentContainer is a higher-order component that takes in two arguments:

  1. A React component for which you want to declare some data dependencies
  2. Data dependencies written as a GraphQL fragment and wrapper using the graphql function

Go ahead and write the fragment containers for the two components that you added before.

All that’s done there is importing the required Relay modules that you need to create the fragment container.

Here’s where it gets interesting! Let’s examine this part step-by-step:

You’re using the createFragmentContainer higher-order component and pass in two arguments - exactly as we said before. The first argument is simply the React component, here that’s the Link. The second argument are its data requirements in the form of a GraphQL fragment tagged with the graphql function. The Link component needs access to the description and url of a link item. The id is required so that Relay can uniquely identify the link items when storing and retrieving them in the cache.

One important note here is that there is a naming convention for the fragments you’re creating! Each fragment should be named according to the file and the prop that will get injected into the component: <FileName>_<propName>

In your case, the file is called Link.js and the prop in the component should be called link. So you end up with Link_link for the name of the fragment.

Great work so far! Go and add the fragment container for LinkList as well.

Similar to the Link component, you’re passing the LinkList component along with its data requirements into createFragmentContainer. The LinkList needs access to a list of links - here you’re simply asking for the last 100 links to display. In the last chapter of this tutorial, you’ll implement a proper pagination approach.

Note: In Relay, lists are represented with the concept of connections. This facilitates the implementation of a cursor-based pagination approach on the client. Relay also requires you to always specify a limit of items that you want to fetch from the server, so you have to pass the first or last argument when fetching items from a connection.

Notice that you’re again following the same naming convention and name the fragment LinkList_viewer. LinkList.js is the name of the file and viewer is the prop that you expect in the component.

You’re also reusing the Link_link fragment that you wrote in Link.js. That’s because the LinkList is higher in the React component (and Relay container) tree, so it needs to include all the fragments of its children!

The @connection directive is required for updating the cache later on - you need it so you can refer to that particular connection (identified by the key LinkList_allLinks) in the cache.

Finally, you also need to delete the mock data and use the data that’ll be injected by Relay to render the Link elements.

Rendering Queries

Now it starts to get interesting! What happens with these fragments? When are they used and what’s the query Relay actually sends to the server?

Meet the QueryRenderer:

QueryRenderer is the root of a Relay tree. It takes a query, fetches the data and calls the render callback with the data.

So, here is where it all adds up. React components are wrapped with GraphQL fragments to become Relay containers. When doing so, they retain the same hierarchical structure as the pure React components and form a tree. At the root of that tree there’s the QueryRenderer, which also is a higher-order component that will take care of composing the actual query.

So, go and add the QueryRenderer in a new component!

A QueryRenderer needs at least three things when being instantiated:

  1. A Relay environment which is why you’re importing it here.
  2. A root query which will be the basis for the query that gets sent to the server.
  3. A render function that specifies what should be rendered in loading, error and success cases.

You’ll write the root query first.

Notice how you’re now actually reusing the fragment LinkList_viewer from the LinkList component.

As discussed before, you’re using the QueryRenderer and provide the three required props. The render function receives the result that’s returned by the server and passes it down to its children.

Lastly, you need to make sure that the LinkListPage is rendered on the root of your component hierarchy.

Running the App

If you’re just running the app now, you’ll be disappointed that it throws some errors:

Failed to compile.
./src/components/LinkListPage.js
Module not found: Can't resolve './__generated__/LinkListPageQuery.graphql' in '.../hackernews-react-relay/src/components'

That’s because we’ve skipped the compilation of the GraphQL code that makes for much of Relay’s actual power! You already installed the relay-compiler as a dev dependency, this allows you to add it as a script to package.json as explained here. However, to keep things a bit more simple in this tutorial you’ll just install it globally for now (feel free to choose the other setup described in the Relay docs).

The compiler can be invoked using the relay-compiler command in the terminal where you have to provide two arguments:

  1. --src: The path to all your files that contain graphql code
  2. --schema: The path to your full GraphQL schema that you already downloaded in the previous chapter

Now - when running the relay-compiler you’ll actually see another error message. That’s disappointing, but don’t worry - it’s not your fault this time. This happens because of a bug in the Relay Compiler that breaks the compilation when there are non-nullable types on the connection types in the schema. You can work around the issue by manually adjusting it, which is not something you should be doing under normal circumstances but for the purpose of this tutorial it will be fine.

Now you can run the Relay Compiler again!

The relay-compiler will now scan all files in src and look for graphql code. It then takes this code and generates corresponding Javascript representations for it (which again will be the input for the Babel compilation step). These Javascript representations are stored in ./src/__generated__.

Here’s what the terminal output will look like:

$ relay-compiler --src ./src --schema ./schema.graphql
HINT: pass --watch to keep watching for changes.
Parsed default in 0.06s

Writing default
Writer time: 0.27s [0.08s compiling, 0.19s generating, 0.00s extra]
Created:
 - Link_link.flow.js
 - Link_link.graphql.js
 - LinkList_viewer.flow.js
 - LinkList_viewer.graphql.js
 - LinkListPageQuery.graphql.js
Unchanged: 0 files
Written default in 0.33s

You’ll also notice that the __generated__ directory was now created and contains all the files that were generated by the compiler:

.
└── src
    └── components
        └── __generated__
            ├── LinkListPageQuery.graphql.js
            ├── LinkList_viewer.flow.js
            ├── LinkList_viewer.graphql.js
            ├── Link_link.flow.js
            └── Link_link.graphql.js

That’s it, you can now run the app and you’ll see the same links that you initially added with the two mutations in the Playground rendered in the app!

By the way, if you’re curious to see what the actual query looked like that the QueryRenderer composed for you and that was sent over to the server, you can inspect the Networking-tab of your browser’s dev tools:

The query in QueryRenderer

Awesome! You can now move on to learn about mutations in Relay.

Unlock the next chapter
What part of Relay is responsible to put the fragments that you define alongside your React components into an actual GraphQL query?
The Relay Environment
The Relay Compiler
The QueryRender
None, with Relay you always write the full queries yourself