GraphQL is a technology that is being used by many companies today (like Facebook, for example). It is becoming an important alternative for building reliable, scalable, and high-performance APIs.
Facebook released a new version a few months ago and is making heavy use of React, GraphQL, and the newest version of Relay in their application. Facebook has been using Relay in production for a few years and it is helping them to have a more scalable, stable, and maintainable application.
In this post, we’ll cover the new version of Relay. We will explore how the newest version is working and how we can create more reliable and scalable React and GraphQL applications.
Relay is a powerful JavaScript framework for working with GraphQL in React applications. Relay is different from other GraphQL clients because it’s more structured and opinionated.
Relay helps to build more scalable, structured, and high-performance React and GraphQL applications. It makes data-fetching in GraphQL easy, relying on GraphQL best practices such as fragments, connections, global object identification, etc.
The newest version of Relay released on March 9th, 2021 and has a more developer-friendly API. The new version supports React Suspense, although it’s an experimental feature.
Relay Hooks are a new set of APIs for fetching and managing GraphQL data in React applications using React Hooks.
To start with the newest version of Relay, let’s first create a new React application using create-react-app and set up Relay:
npx create-react-app graphql-relay-example --template typescript
After creating our React application, we need to install a few packages to get started with Relay:
yarn add react-relay relay-runtime isomorphic-fetch yarn add --dev @types/react-relay @types/relay-runtime graphql relay-compiler relay-compiler-language-typescript
Now, we create a file called environment.tsx
, which is where we’re going to create our Relay environment. The best way to use Relay is to use a GraphQL API that’s compatible with Relay, it will follow the best practices and make it easier to implement some things such as pagination.
Inside our environment.tsx
file, put the following code:
import { Environment, Network, RecordSource, Store, RequestParameters, Variables } from "relay-runtime"; import fetch from "isomorphic-fetch"; function fetchQuery(operation: RequestParameters, variables: Variables) { return fetch('https://podhouse-server.herokuapp.com/graphql', { method: "POST", headers: { Accept: "application/json", "Content-type": "application/json", }, body: JSON.stringify({ query: operation.text, variables, }), }).then((response: any) => { return response.json() }) } const network = Network.create(fetchQuery); const env = new Environment({ network, store: new Store(new RecordSource(), { gcReleaseBufferSize: 10, }), }); export default env;
Inside our index.tsx
, we import the RelayEnvironmentProvider
and pass our environment
to it:
import React from "react";
import ReactDOM from "react-dom";
import { RelayEnvironmentProvider } from "react-relay/hooks";
import environment from "./environment";
ReactDOM.render(
<RelayEnvironmentProvider environment={environment}>
<React.StrictMode>
<App />
</React.StrictMode>
</RelayEnvironmentProvider>,
document.getElementById('root')
);
Now, we have Relay set up on our project and we can start to use the newest version and see how it works.
useLazyLoadQuery
The easiest way of fetching data with Relay is by using the useLazyLoadQuery
. This hook will fetch data during render, turning it into not the most efficient way of fetching data but the simplest:
import { useLazyLoadQuery } from "react-relay/hooks"; useLazyLoadQuery(query, variables, options);
Here’s how the useLazyLoadQuery
works:
query
— You need this to pass your GraphQL query template literalvariables
— Is an object containing values to fetch the query. For example, when you want to authenticate a user inside your applicationoptions
— Is an object that you define a few properties. fetchPolicy
is used to determine if the data should be cached or not. fetchKey
is used to force the reevaluation of the querynetworkCacheConfig
is an object that you can define to your cache config optionsThe useLazyLoadQuery
should always be used inside a RelayEnvironmentProvider
. The useLazyLoadQuery
may suspend your data when a network request is in flight depending on which fetchPolicy
you choose. Depending on the fetchPolicy
selected you should make use of React Suspense for loading states in your application.
Check out an example of the useLazyLoadQuery
hook:
import React from "react"; import graphql from "babel-plugin-relay/macro"; import { useLazyLoadQuery } from "react-relay/hooks"; const query = graphql` query SettingsQuery { currentUser { id _id email } } `; const Component = () => { const data = useLazyLoadQuery( query, {}, { fetchPolicy: "store-and-network", } ); return ( <div> <h1>{data.currentUser.email}</h1> </div> ); }; export default Component;
usePreloadedQuery
The usePreloadedQuery
is the most recommended hook for fetching data with Relay. It implements the render-as-you-fetch pattern, a pattern that allows us to load the data that we need and render our component in parallel.
The usePreloadedQuery
can be a bit confusing, so let’s clear up how this hook works:
usePreloadedQuery
makes use of the useQueryLoader
, which is another hook available on the new version of RelayuseQueryLoader
is a hook for safely load queries. It will keep a query reference stored and dispose of it when the component is disposeduseQueryLoader
is designed to be used with usePreloadedQuery
useQueryLoader
returns a queryReference
, a loadQuery
callback, and a disposeQuery
callbackloadQuery
callback from useQueryLoader
first, which will store a query reference in React statequeryReference
to our usePreloadedQuery
, which will allow us to fetch data earlier while not blocking rendering on our componentThe usePreloadedQuery
is the most powerful and recommended way of fetching data with Relay.
Check out an example of usage of the usePreloadedQuery
hook:
import React, { useEffect } from "react"; import graphql from "babel-plugin-relay/macro"; import { useQueryLoader, usePreloadedQuery } from "react-relay/hooks"; const query = graphql` query UserQuery($_id: ID!) { user(_id: $_id) { id _id name } } `; const Component = () => { const [queryReference, loadQuery, disposeQuery] = useQueryLoader(query); useEffect(() => { loadQuery({ _id: _id }, { fetchPolicy: "store-or-network" }); return () => { disposeQuery(); }; }, [loadQuery, disposeQuery, _id]); return ( <React.Suspense fallback="Loading user..."> {queryReference != null ? <UserComponent queryReference={queryReference} /> : null } </React.Suspense> ); }; const UserComponent = ({ queryReference }) => { const data = usePreloadedQuery(query, queryReference); return <h1>{data.user?.name}</h1>; } export default Component;
usePaginationFragment
One of the advantages of using Relay, making your GraphQL API compatible with Relay, and following the GraphQL specification is that it makes it very easy to implement some features such as pagination.
The usePaginationFragment
is a hook that can be used for rendering a fragment and paginate over it:
import { usePaginationFragment } from "react-relay/hooks"; usePaginationFragment(query, variables, options);
Here’s how the usePaginationFragment
works:
fragment
– A GraphQL fragment template literal. The GraphQL fragment must have a @connection
and @refetchable
directive, otherwise, it will throw an errorfragmentReference
– A fragment reference that Relay uses from read data for the fragment from the storeWe can use the usePaginationFragment
along with the usePreloadedQuery
. Check out an example of usage of the usePreloadedQuery
hook. First, we create our query and fragment:
const query = graphql` query ProductsQuery($name: String!) { ...SearchProducts_products @arguments(name: $name) } `; const fragment = graphql` fragment SearchProducts_products on Query @argumentDefinitions( name: { type: "String" } after: { type: "String" } first: { type: "Int", defaultValue: 30 } before: { type: "String" } last: { type: "Int" } ) @refetchable(queryName: "SearchProductsPaginationQuery") { products( name: $name after: $after first: $first before: $before last: $last ) @connection(key: "SearchProducts_products", filters: ["name"]) { edges { node { _id name image } } } } `;
Now, inside our component, we pass our query to the useQueryLoader
hook and inside the child component we use the usePreloadedQuery
:
import React, { useEffect } from "react"; import graphql from "babel-plugin-relay/macro"; import { useQueryLoader, usePreloadedQuery } from "react-relay/hooks"; const Component = () => { const [queryReference, loadQuery, disposeQuery] = useQueryLoader(query); useEffect(() => { loadQuery({ name: name }, { fetchPolicy: "store-or-network" }); return () => { disposeQuery(); }; }, [loadQuery, disposeQuery, name]); return ( <React.Suspense fallback="Loading products..."> {queryReference != null ? (<ProductComponent queryReference={queryReference} />) : null} </React.Suspense> ); }; const ProductComponent = ({ queryReference }) => { const query = usePreloadedQuery(query, queryReference); const { data } = usePaginationFragment(fragment, query); return ( <div> {data.friends?.edges.map(({ node }) => ( <div>{node.name}</div> ))} </div> ); }; export default Component;
useMutation
Mutations are a very important part of GraphQL. It is what allows us to create, update, and delete data.
useMutation
is the new hook for executing mutations with Relay. It is a very simple and straightforward hook, with only two parameters:
mutation
— A GraphQL mutation template literalcommitMutationFn
— A function that will be called in instead. This function is optional, most of the time you are not going to need ituseMutation
returns only two values:
commitMutation
— A function that will execute the mutationisInFlight
— A value to check if the mutation is still in flight. You can use the commitMutation
as many times as you want, so usually you can use multiple mutations in flight at onceCheck out an example of the usePreloadedQuery
hook:
import React from "react"; import graphql from "babel-plugin-relay/macro"; import { useMutation } from "react-relay/hooks"; const mutation = graphql` mutation SignInWithEmail($input: SignInWithEmailInput!) { SignInWithEmail(input: $input) { token success error } } `; const Component = () => { const [commitMutation, isInFlight] = useMutation(mutation); const onSubmit = () => { commitMutation({ variables: { input: { email: email, password: password, }, }, onCompleted: ({ SignInWithEmail }) => { if (SignInWithEmail?.error) { return SignInWithEmail?.error; } updateToken(SignInWithEmail?.token); }, }); }; return ( <form onSubmit={onSubmit}> <input type="text" value={email} /> <input type="password" value={password} /> <button type="submit">Submit</button> </form> ); }; export default Component;
The newest version of Relay brought a set of new APIs that will help us to build more scalable React and GraphQL applications. The usage of React Hooks can help us to build more modular and high-performance applications, making our code easier to understand and free of unexpected side effects.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]