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:
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!
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 😎
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:
graphql
functionGo 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
orlast
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.
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 therender
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:
environment
which is why you’re importing it here.query
which will be the basis for the query that gets sent to the server.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.
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:
--src
: The path to all your files that contain graphql
code--schema
: The path to your full GraphQL schema that you already downloaded in the previous chapterNow - 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:
Awesome! You can now move on to learn about mutations in Relay.