GraphQL On The Front-End (React And Apollo)

One of the main benefits of GraphQL is the client’s ability to request what they need from the server and receive that data exactly and predictably. Without much effort, one can easily pull nested data by just adding more properties to our queries instead of adding multiple endpoints. This prevents issues like over-fetching that can impact performance.

Usually, to handle GraphQL on the client-side, we make use of the Apollo Client. It allows developers to define, handle, and make queries/mutations available within our application. It can also act as a state management tool with your client-side application.

In this article, we’re going to learn how to handle real-time updates on the client-side using GraphQL. We’ll be learning how to do this with GraphQL Features like Cache Update, Subscriptions, and Optimistic UI. We’ll also be touching on how to use Apollo as a state-management tool, possibly replacing redux. Plus, we’ll look at how to create usuable GraphQL queries with Fragments, and how to use Apollo directives to write more complex queries.

Installation

Before we begin, let’s just go through installation and setting up our project. Let’s get right into the code. To create a React app, make sure you have Node.js installed on your computer. If you haven’t built a React app before, you can check to see if you have Node.js installed by typing the following into your terminal:

node -v

If not, just go to the Node.js website to download the latest version.

Once that’s done, we can get started with our React app by running this command:

npx create-react-app react-graphql

Next, let’s navigate into our project folder on the terminal:

cd react-graphql

Or better still, you could just go on and clone the repo. The repo contains both the client-side and server, so we have some other dependencies that’s needed.

Once that’s done, we’ll install Apollo using this line:

npm i @apollo/client

We’ll install those dependencies by running:

npm install

Just before we start, this is the repo containing the code demonstrating everything under Real-time update on GraphQL, using Apollo as a state management tool, Fragments, and Apollo directives. Also, here’s the repo containing the code demonstrating subscription on the the client-side.

Real-time Update On GraphQL

The ability to create a real-time update on the client-side helps improve the user experience of the site, making everything seem smoother. Just imagine a situation where a user adds a new item by filling a form, and that item updates instantly by been added to the list of items on the same page. Although, this real-time update could sync with a server directly through subscriptions, or it might be manipulated on the frontend through things like Optimistic UI, or using the update function on the useMutation. So let’s get to the technical implementation. Here’s the repo containing the code demonstrating everything under Real-time update On Graphql, using Apollo as a state management tool, Fragments, and Apollo directives.

Updating the cache directly using update function on the useMutation

useMutations are imported directly from the @apollo/client library, and it helps us make mutations to the data on our server.

Usually, we can create mutations with Apollo using useMutations, but beyond that, what we’ll be doing is using the update function to update our apollo-client cache directly through useMutation.

In this sample below, we send queries to the server to get a list of pets using useQuery and make a mutation by having a form to add more pets to our server using useMutation. The problem we’ll have is that when a new pet is added to the server, it doesn’t get added to the list of pets(on the browser) immediately, unless the page is refreshed. This makes the user experience of this section of the app feel broken, especially since the list of pets and the form are on the same page.

import React, { useState } from "react";
import gql from "graphql-tag";
import { useQuery, useMutation } from "@apollo/client";
import Loader from "../components/Loader";
import PetSection from "../components/PetSection";

//ALL_PETS uses gql from @apollo/client to allow us send nested queries 
const ALL_PETS = gql`
  query AllPets {
    pets {
      id
      name
      type
      img
    }
  }
`;

// NEW_PET uses gql from @apollo/client to create mutations
const NEW_PET = gql`
  mutation CreateAPet($newPet: NewPetInput!) {
    addedPet(input: $newPet) {
      id
      name
      type
      img
    }
  }
`;
function Pets() {
  const initialCount = 0;
  const [count, setCount] = useState(initialCount);
  const pets = useQuery(ALL_PETS);
  const [createPet, newPet] = useMutation(NEW_PET);
  const [name, setName] = useState("");
  const type = `DOG`;

  const onSubmit = (input) => {
    createPet({
      variables: { newPet: input },
    });
  };

  // this function triggers the submit action by calling the onSubmit function above it
  const submit = (e) => {
    e.preventDefault();
    onSubmit({ name, type });
  };

//If the data is loading we display the <Loader/> component instead
  if (pets.loading || newPet.loading) {
    return <Loader />;
  }

//loops through the pets data in order to get each pet and display them with props using the <PetSection> component
  const petsList = pets.data.pets.map((pet) => (
    <div className="col-xs-12 col-md-4 col" key={pet.id}>
      <div className="box">
        <PetSection pet={pet} />
      </div>
    </div>
  ));

  return (
    <div>
      <form onSubmit={submit}>
        <input
          className="input"
          type="text"
          placeholder="pet name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          required
        />
        <button type="submit" name="submit">
          add pet
        </button>
      </form>
      <div>
        {petsList}
      </div>

    </div>
  );
}
export default Pets;

Using update function in the useMutation hook allows us to directly update our cache by reading and writing our ALL_PETS. Immediately we hit the submit button, the data is added to the list of pets in the cache by altering ALL_PETS. This lets us update our client-side cache immediately with consistent data.

import React, { useState } from "react";
import gql from "graphql-tag";
import { useQuery, useMutation } from "@apollo/client";
import Loader from "../components/Loader";
import PetSection from "../components/PetSection";

//ALL_PETS uses gql from @apollo/client to allow us send nested queries 
const ALL_PETS = gql`
  query AllPets {
    pets {
      id
      name
      type
      img
    }
  }
`;

// NEW_PET uses gql from @apollo/client to create mutations
const NEW_PET = gql`
  mutation CreateAPet($newPet: NewPetInput!) {
    addedPet(input: $newPet) {
      id
      name
      type
      img
    }
  }
`;

function ThePets() {
  const initialCount = 0;
  const [count, setCount] = useState(initialCount);
  const pets = useQuery(ALL_PETS);

  //We then make use of useMutation and update() to update our ALL_PET

  const [createPet, newPet] = useMutation(NEW_PET, {
    update(cache, {data: {addedPet}}) {
      const allPets = cache.readQuery({query: ALL_PETS})
      cache.writeQuery({
        query: ALL_PETS,
        data: {pets: [addedPet, ...allPets.pets]}
      })
    }
  });
  const [name, setName] = useState("");
  const type = `DOG`;

  const onSubmit = (input) => {
    createPet({
      variables: { newPet: input },
    });
  };

  //Handles the submission of Pets that eventually triggers createPet through onSumit

  const submit = (e) => {
    e.preventDefault();
    onSubmit({ name, type });
  };

  //If the data is loading we display the <Loader/> component instead

  if (pets.loading || newPet.loading) {
    return <Loader />;
  }

//loops through the pets data in order to get each pet and display them with props using the <PetSection> component

  const petsList = pets.data.pets.map((pet) => (
    <div className="col-xs-12 col-md-4 col" key={pet.id}>
      <div className="box">
        <PetSection pet={pet} />
      </div>
    </div>
  ));
  return (
    <div>
      <form onSubmit={submit}>
        <input
          className="input"
          type="text"
          placeholder="pet name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          required
        />
        <button type="submit" name="submit">
          add pet
        </button>
      </form>
      <div>
        {petsList}
      </div>

    </div>
  );
}
export default ThePets;
Subscriptions In GraphQL

Based on functionalities, subscription in GraphQL is similar to queries. The major difference is that while Queries is done just once, subscriptions are connected to the server, and automatically updates when there’s any change to that particular subscription. Here’s the repo containing the code demonstrating subscription on the the client-side.

First, we have to install:

npm install subscriptions-transport-ws

Then we go to our index.js to import and use it.

 import { WebSocketLink } from "@apollo/client/link/ws";

//setting up our web sockets using WebSocketLink
const link = new WebSocketLink({
  uri: `ws://localhost:4000/`,
  options: {
    reconnect: true,
  },
});
const client = new ApolloClient({
  link,
  uri: "http://localhost:4000",
  cache: new InMemoryCache(),
});

Note: uri in the code block directly above is for our endpoint.

Then we go into our component and instead of query like we have above, we’ll use this subscription instead:

import {  useMutation, useSubscription } from "@apollo/client";
//initiate our subscription on the client-side
const ALL_PETS = gql`
  subscription AllPets {
    pets {
      id
      name
      type
      img
    }
  }
`;

And instead of using useQuery, we would access our data using useSubscription.

 const getMessages = useSubscription(ALL_PETS);
Optimistic UI

Optimistic UI is a little different in the sense that it’s not syncing with the server, like a subscription. When we make a mutation, instead of waiting for another server request, it automatically uses the already inputted data to update the list of pets immediately. Then, once the original data from the server arrives, it will replace the optimistic response. This is also different from “Updating the cache directly using update function on the useMutation”, even though we are still going to update the cache in this process.

import React, { useState } from "react";
import gql from "graphql-tag";
import { useQuery, useMutation } from "@apollo/client";
import Loader from "./Loader";
import PetSection from "./PetSection";

//We use ALL_PET to send our nested queries to the server
const ALL_PETS = gql`
  query AllPets {
    pets {
      id
      name
      type
      img
    }
  }
`;

//We use NEW_PET to handle our mutations
const NEW_PET = gql`
  mutation CreateAPet($newPet: NewPetInput!) {
    addPet(input: $newPet) {
      id
      name
      type
      img
    }
  }
`;

function OptimisticPets() {
//We use useQuery to handle the ALL_PETS response and assign it to pets
  const pets = useQuery(ALL_PETS);
//We use useMutation to handle mutations and updating ALL_PETS.
  const [createPet, newPet] = useMutation(NEW_PET
    , {
    update(cache, {data: {addPet}}) {
      const allPets = cache.readQuery({query: ALL_PETS})
      cache.writeQuery({
        query: ALL_PETS,
        data: {pets: [addPet, ...allPets.pets]}
      })
    }
  });;
  const [name, setName] = useState("");
  const type = `DOG`;
 //Handles mutation and creates the optimistic response
  const onSubmit = (input) => {
    createPet({
      variables: { newPet: input },
      optimisticResponse: {
        __typename: 'Mutation',
        addPet: {
          __typename: 'Pet',
          id: Math.floor(Math.random() * 1000000) + '',
          type: "CAT",
          name: input.name,
          img: 'https://via.placeholder.com/300',
        }
      }
    });
  };

//Here's our submit triggers the onSubmit function
  const submit = (e) => {
    e.preventDefault();
    onSubmit({ name, type });
  };
//returns the loading the component when the data is still loading
  if (pets.loading ) {
    return <Loader />;
  }
//loops through the pets and displays them in the PetSection component 
  const petsList = pets.data.pets.map((pet) => (
    <div className="col-xs-12 col-md-4 col" key={pet.id}>
      <div className="box">
        <PetSection pet={pet} />
      </div>
    </div>
  ));
  return (
    <div>
      <form onSubmit={submit}>
        <input
          className="input"
          type="text"
          placeholder="pet name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          required
        />
        <button type="submit" name="submit">
          add pet
        </button>
      </form>
      <div>
        {petsList}
      </div>

    </div>
  );
}
export default OptimisticPets;

When the code above calls onSubmit, the Apollo Client cache stores an addPet object with the field values specified in optimisticResponse. However, it does not overwrite the main cached pets(ALL_PETS) with the same cache identifier. Instead, it stores a separate, optimistic version of the object. This ensures that our cached data remains accurate if our optimisticResponse is wrong.

Apollo Client notifies all active queries that include the modified pets(ALL_PETS). Those queries automatically update, and their associated components re-render to show our optimistic data. This doesn’t require any network requests, so it displays instantly to the user.

Eventually, our server responds to the mutation’s actual to get the correct addPet object. Then, Apollo Client cache discards our optimistic version of the addPet object. It also overwrites the cached version with values returned from the server.

Apollo Client immediately notifies all affected queries again. The concerned components re-render, but if the server’s response matches our optimisticResponse, this is entire process is invisible to the user.

Using Apollo As A State Management Tool On The Client-side

When we think of state management tools or libraries concerning react, redux comes to mind. Interestingly, Apollo can also act as a management tool for our local state. Similar to what we’ve been doing with our API.

Client-side Schemas And Resolvers

To achieve this, we’ll have to write schemas on the client-side to define the type of data we want and how we want it to be structured. To do this, we’ll create Client.js where we’ll define the schemas and resolvers, after which, we’ll make it globally accessible in our project with the Apollo client.

For this example, I’ll be extending the User type that exists already to add height as an integer. The resolvers is also added to populate the height field in our schema.

import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import gql from 'graphql-tag'

//Extending the User type
const typeDefs = gql`
  extend type User {
    height: Int
  }
`

//Declaring our height inside our resolvers within the client-side
const resolvers = {
  User : {
    height() {
      return 35
    }
  }
}
const cache = new InMemoryCache()
const http = new HttpLink({
  uri: 'http://localhost:4000/'
})
const link = ApolloLink.from([
  http
])

const client = new ApolloClient({
  link,
  cache,
  typeDefs,
  resolvers
})
export default client

client.js

We can then import the client into our index.js:

import client from "./client"
import {
  ApolloProvider,
} from "@apollo/client";

//importing our client.js file into ApolloProvider
ReactDOM.render(
  <ApolloProvider client={client}>
    <Routing />
  </ApolloProvider>,
  document.getElementById("root")
);

index.js

Within the component, it will use it just like this. We add @client to indicate that the query is from the client-side, and it should not try to pull it from the server.

const ALL_PETS = gql`
  query AllPets {
    pets {
      id
      name
      type
      img
      owner {
        id
        height @client
      }
    }
  }
`;

So we’re pulling data from both the server and the client within the same query, and it’ll be accessible through the useQuery hook.

Fragments-Creating Reusable Queries

Sometimes we might need to pull the same query in different components. So instead of hardcoding it multiple times, we assign that query to some sort of variable, and use that variable instead.

In our component we just define the fragment as PetFields on Pet(which is the Type). That way we can just use it in both our query and mutation.

const DUPLICATE_FIELD = gql`
  fragment PetFields on Pet {
      id
      name
      type
      img
  }
`
const ALL_PETS = gql`
  query AllPets {
    pets {
      ...PetFields
    }
  }
  ${DUPLICATE_FIELD}
`;
const NEW_PET = gql`
  mutation CreateAPet($newPet: NewPetInput!) {
    addPet(input: $newPet) {
        ...PetFields
    }
  }
  ${DUPLICATE_FIELD}
`;
Apollo Directives

When making queries, we might want to have some conditionals that remove or include a field or fragment if a particular condition is fulfilled or not. The default directives include:

@skip: Indicates that a field/fragment should be skipped if a condition is fulfilled.

const ALL_PETS = gql`
  query AllPets($name: Boolean!){
    pets {
      id
      name @skip: (if: $name)
      type
      img
    }
  }
`;

Here $name is a boolean that’s added as a variable when we are calling this query. Which is then used with @skip to determine when to display the field name. If true, it skips, and if falses it resolves that field.

@includes also work in a similar manner. If the condition is true, that field is resolved and added, and if it’s false, it’s not resolved.

We also have @deprecated that can be used in schemas to retire fields, where you can even add reasons.

We also have libraries that allow us to add even more directives, they could prove useful when building somewhat complicated stuff with GraphQL.

Tips And Tricks With Using GraphQL Lodash Inside Your Queries

GraphQL Lodash is a library that can help us a query in a more efficient way, more like an advanced form of the Apollo directives.

It can help you query your server in a way that returns data more neatly and compactly. For instance, you’re querying the title of films like this:

films {
  title
}

And it returns the title of movies as objects in an array.

"films": [
    {
      "title" : "Prremier English"
    },
    {
      "title" : "There was a country"
    },
    {
      "title" : "Fast and Furious"
    }
    {
      "title" : "Beauty and the beast"
    }
]

But, when we use lodash’s map directive, when can sort of loop through the films array to have a single array with all the titles as direct children. We would send a query our server that looks like this:

films @_(map: "title") {
  title
}

You’ll get this response which one might consider relatively neater than the previous one.

"films": [  
  "Premier English",
  "There was a country",
  "Fast and Furious",
  "Beauty and the beast"
]

Another one that proves useful is the is keyby directive. You can send a simple query like this:

people {
  name
  age
  gender
}

Response:

"people" : [
  {
    "name":  "James Walker",
    "age": "19",
    "gender": "male"
  },
  {
    "name":  "Alexa Walker",
    "age": "19",
    "gender": "female"
  }, 
]

Let’s use @_keyup directive in our query:

people @_(keyBy: "name") {
  name
  age
  gender
}

The response will look just like this:

"people" : [
  "James Walker" : {
     "name":  "James Walker",
     "age": "19",
     "gender": "male"    
  }
  "Alexa Walker" : {
     "name":  "Alexa Walker",
     "age": "19",
     "gender": "female"
  }
]

So in this case each response has a key, that’s the name of the person.

Conclusion

In this article, we covered advanced topics to achieve real-time update of data using the update() function, subscription, and Optimistic UI. All in a bit to improve user experience.

We also touched upon using GraphQL to manage state on the client-side, and creating resuable queries with GrahQL fragments. The latter allows us to use the same queries in different components where it’s needed without having to repeat the entire thing every time.

In the end, we went through Apollo directives and Grahql Lodash to help us query our servers in a faster and better way. You can also check out Scott Moss’s tutorial if you’re looking to cover Graphql and react from scratch.

Reactive Variables In GraphQL Apollo Client

In this article, we will look at how to set up reactive variables, how the GraphQL cache polices come into place in defining read and writes to the cache, and provide the ability for developers to add types that exist on the client-side alone so that we can structure queries for client-side variables same way we can for remote GraphQL data. After learning more about the fundamentals of reactive variables, we will build a simple app that switches the theme of our application to either dark mode or light mode based on the value of our reactive variable. We will be looking at how to query a reactive variable, how to update the value stored in a reactive variable, and how the change in value triggers updates in components that depend on the reactive variable for certain actions to occur.

The target audience for this article would include software developers who already use GraphqQL with state management tools like Context API or Redux and willing to explore a new pattern of handling state management in GraphQL, or GraphQL beginners who are looking for effective ways to handle globally shared local state within GraphQL without Making things too complicated with external tooling. To follow along with this, you should have an existing knowledge of ReactJS and CSS too.

A Quick Introduction To GraphQL

With GraphQL, you get exactly what you need, and also get the data returned as well as structured how you need it.

“GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.”

GraphQL website

What Is Apollo Client In GraphQL?

Apollo Client helps you avoid manually tracking loading and error states. It also provides the ability to use GraphQL with modern React patterns like hooks, and so on.

“Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Use it to fetch, cache, and modify application data, all while automatically updating your UI.”

— “Introduction to Apollo Client,” Apollo Docs

Let’s define some terms here that you will need to understand to move forward:

  • Variable
    A variable is a name you give to an assigned memory location where a value is stored. The variable name is used as a reference to the value stored in it when you need to make use of it.
  • Reactivity
    We will explain reactivity as something that triggers change on its dependents when an update is passed to it. Like the local state in React triggers component updates, the reactive variables in Apollo GraphQL also automatically trigger component updates based on changes.

State management is a really important part of building a modern application. Having a global state is important when different components or screens require access to the same state value and possibly trigger changes when that particular state is changed.

In the next section, we will look at how to set up a reactive variable.

Writing Our First Reactive Variable

Here’s what a reactive variable looks like:

import { makeVar } from '@apollo/client';

const myReactiveVariable = makeVar(/** An initial value can be passed in here.**/)

The makeVar is imported from Apollo Client and is used to declare our a reactive variable. The makeVar takes an initial value that the reactive variable would hold. The ease of constructing a reactive variable is amazing.

There are two ways to read data from our created reactive variable. The easiest way is to call our declared reactive variable which we have created above, as a function without an argument:

const variable = myReactiveVariable();

Getting the value of a reactive variable is that easy. In the code block above, we declared a variable that holds our reactive variable which was called without an argument to read the data it already holds.

We can also get the value of a reactive variable with the useQuery syntax we normally would use to fetch remote data in GraphQL. To explain how we can do this, let’s look at the Cache type and field policies.

Type And Field Policies

The cache type and field policies help you define how a specific field in your Apollo Client cache is read and written to. You do this by providing field policies to the constructor of inMemoryCache. Each field policy is defined inside the typePolicy that corresponds to the type which contains the field. Let’s define a typePolicy called Query and define a field policy for accessing a field called myReactiveVariable.

import { InMemoryCache } from '@apollo/client';

// Here we import our reactive variable which we declared in another
// component
import { myReactiveVariable } from './reactivities/variable.js';

// The field policies hold the initial cached state of a field.
export default new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        myReactiveVariable: {
          read() {
            return myReactiveVariable();
          }
        }
      }
    }
  }
})

In the code snippet above, we declared a type called Query and defined a field called myReactiveVariable. Next, we added a read function that specifies what happens when the field’s cached value is read. Here’s what happens when the myReactiveVariable field cached value is being read:

We pass in the reactive variable we had declared in another component and imported here as the value the field returns.

Now that we have defined our typePolicies and fieldPolicies, let us go ahead and write our query to get the value store in our reactive variable. Here’s what the query would look like:

import { gql } from "@apollo/client";

export const GET_REACTIVE_VARIABLE = gql`
  query getReractiveVariable{
    myReactiveVariable @client
  }
`

The gql template literal tag we imported from Apollo Client above is used to write a GraphQL query in Apollo client.

The query name myReactiveVariable should match the field name declared in the field policy. If you have been using GraphQL, you will notice that this querying pattern is identical to the normal query you would write if it were to be a remote GraphQL API we were querying. The only difference is the @client placed after the field name. This instructs Apollo to resolve this particular query on the client and not on any external API.

That’s it! We have successfully set up our first reactive variable. The process looks a little bit lengthy initially but subsequently, you can declare a new reactive variable by simply declaring the reactive variable and adding a field policy for it.

To fetch the reactive variable, you can use the useQuery hook in any component where you need it. Here’s an example.

import { useQuery } from '@apollo/client';
import { GET_REACTIVE_VARIABLE } from 'FILE_PATH_TO_YOUR_QUERY_FILE';

const {loading, error, data} = useQeury(GET_DARK_MODE);

// you can track loading, error states, and data the same way with a normal query in Apollo

In the above code, we imported useQuery from @apollo/client. Next, we imported the GET_REACTIVE_VARIABLE query from the file it was exported from.

Lastly, we pass on to the useQuery hook in our query, and destructure loading, error, and data from it.

Modifying A reactive variable

Apollo client provides a beautiful way to modify a reactive variable — calling the function returned by makeVar and pass in a single argument to the function. The argument passed in is the new value the reactive variable will hold. Let us look at an example below where we modify our reactive variable which we declared above:

import { myReactiveVariable } from 'PATH_TO_OUR_REACTIVE_VARIABLE_FILE'

myReactiveVariable("A new value is in!");

In the above code, we import myReactiveVariable and we update it by calling the variable and placing the new value inside of it.

It is so easy to update the values of a reactive variable. Once the value in a reactive variable is updated, corresponding actions are triggered in components that depend on the variable and the user-interface is adjusted automatically.

In the next section, we will build out a simple theme-changing application that switches themes from dark mode to light mode with a click of a button. The button changes itself based on the value of the current theme. This will help us put all that we have learned together by building out something that fully and simply illustrates the concept of reactive variables and show how the user interface is automatically triggered when the reactive variable is updated.

Here’s what our result will look like:

(Large preview)

Let’s begin.

Setup

First, we create a new React app.

npx create-react-app theme_toggle

Next, let’s install the necessary libraries we need for Apollo and GraphQL including the react-feather library to get our icons and react-router-dom to setup routing

npm install @apollo/client graphql react-feather react-router-dom

Once we are done with all the installations, let’s go ahead and set up our graphQL, including defining our darkMode reactive variable.

Create a folder called graphql inside the src folder and then create a sub-folder called reactivities to house all the reactive variables. Here’s how the folder tree would look like: src > graphql > reactivities > themeVariable.js

I decided to arrange our file and folder structure simulating a real-world use case so follow along. Let’s go ahead to declare our reactive variable in the themeVariable.js file we just created:

import { makeVar, gql } from "@apollo/client";
export const darkMode = makeVar(false);

Next, inside the same file let’s construct our query to get our reactive variable and specify that the query should be resolved on the client-side. We can decide to create a separate folder to house all our query, especially when we have many queries in our application, but for the sake of this tutorial, we will write the query inside the same file as the reactive variable and export them individually:

import { makeVar, gql } from "@apollo/client";

export const darkMode = makeVar(false);

// This is the query to get the darkMode reactive variable.
export const GET_DARK_MODE = gql`
  query getDarkMode{
    darkMode @client
  }
`

In the above piece of code, we see how straightforward it was to declare a reactive variable with the makeVar() and passed in an initial value of false for our new variable. Next, we imported gql from Apollo client and used it in writing our query.

Next, let’s create our cache.js file and define our type and field policies to control how variables will be queried and structured:

Create a file called cache.js inside the graphql folder. Inside cache.js here’s how we declare our policies:

import { InMemoryCache } from '@apollo/client';
import { darkMode } from './reactivities/themeVariable';

export default new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        darkMode: {
          read() {
            return darkMode();
          }
        }
      }
    }
  }
})

In the above code, first, we imported inMemoryCache from Apollo client, and we imported our reactive variable from the file path where we stored it. Next, we created a new instance of inMemoryCache and our field policy is defined inside of the typePolicy object. The code above defines a field policy for the darkMode field on the Query type.

There’s one final step to complete our setup for Apollo for our React app, we need to create a client.js file. The client.js file is a file you’re already familiar with if you use GraphQL before now. It holds the ApolloClient constructor which would finally get passed into the ApolloProvider on a top-level file (usually the index.js file). Our client.js file should be located directly inside the src folder.

src > client.js

import { ApolloClient } from '@apollo/client';
import cache from './graphql/cache';
const client = new ApolloClient({
  cache,
  connectToDevTools: true,
});
export default client;

Here’s what we did above. We imported ApolloClient. Next, we imported our cache from where it was previously declared. Inside our ApolloClient constructor, we passed in our cache which we imported and set connectToDevTools as true to enable us to use the Apollo Dev Tools in our browser.

Finally, we need to pass in the new ApolloClient instance which we exported as client into ApolloProvider in our top-level index.js file inside the src folder. Open the index.js file and replace the code there with this.

import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import './index.css';
import App from './App';
import client from './client';
ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);

In the above code block, we wrapped our App component with the ApolloProvider and passed client (which we imported) to the Apollo provider. We did this in the top-level scope so that our entire app can access the ApolloProvider and the client.

We have successfully finished everything in the setup of Apollo and the reactive variable. You’ll notice that many things we did here were related to setting up Apollo which you would still have done even if you were using Apollo with other external API for managing context.

Since we are done with everything we need to set up Apollo and create our reactive variable, let’s now go ahead and set up our page and routing.

We would only have one route to a page called landingPage.jsx. Inside the src folder, create a folder called pages to house all the pages (in our case, we have just one page) and create a file called landingPage.jsx in it.

src > pages > landingPage.jsx

Inside our newly created page, let’s create a functional component with a h1 tag containing or heading. Here’s what will be in it.

import React from 'react';

const LandingPage = () => {
  return (
    <div
      style={{
        height: '100vh',
        backgroundColor: 'white',
        }}
    >
      <h1>Welcome to Theme Toggle Appliation!</h1>
    </div>
  )
}
export default LandingPage

Next, let’s create our button component. Inside src, create a folder called components, and create a button.jsx file. src > components > button.jsx

Inside our button component, here are the things we should import icons from react-feather, the useQuery hook from apollo/client, our query and reactive variable from the file it was exported from.

import React from 'react'
import { Moon, Sun } from 'react-feather';
import {  useQuery } from '@apollo/client';
import { GET_DARK_MODE, darkMode as reactiveDarkMode } from '../graphql/reactivities/themeVariable';

Inside the button component, let’s query our GraphQL client with the GET_DARK_MODE query like how we would normally query in GraphQL with Apollo.

...

const ButtonComponent = () => {

  {loading, error, data} = useQuery(GET_DARK_MODE);

  return (...)
}

export default ButtonComponent;

Next, we want to change the buttons based on the boolean value of our reactive variable that will be returned from data. To do this, we will create two buttons and use a ternary operator to display them conditionally based on the boolean value of our reactive variable:

...

const ButtonComponent = () => {

  const {loading, error, data} = useQuery(GET_DARK_MODE);

  return (
    <div>
      {
        data.darkMode ? (
          <button
            style={{
              backgroundColor: '#00008B',
              border: 'none',
              padding: '2%',
              height: '120px',
              borderRadius: '15px',
              color: 'white',
              fontSize: '18px',
              marginTop: '5%',
              cursor: 'pointer'
            }}
            onClick={toggleMode}
          >
            <Sun />
            <p>Switch To Light Mood</p>
          </button>
        ) :(
          <button
          style={{
            backgroundColor: '#00008B',
            border: 'none',
            padding: '2%',
            height: '120px',
            borderRadius: '15px',
            color: 'white',
            fontSize: '18px',
            marginTop: '5%',
            cursor: 'pointer'
          }}
          onClick={toggleMode}
        >
          <Moon />
          <p>Switch To Dark Mood</p>
        </button>
        )
      } 
    </div>
  )
}
export default ButtonComponent;

In the above code, we displayed both buttons conditionally with the ternary operator to display when the value of data.darkMode is either true or false. Our initial value as declared in our themeVariable.js is false.

Note: Remember that we can pull out darkMode from the data because we declared it this way in our cache.js field policy.

We added some CSS to the buttons to make them look better and also added the icons we imported from react-feather to each button.

If you noticed we had an onClick property passed into each button which called toggleMode. Let’s declare the function above but still inside the ButtonComponent:

...

const ButtonComponent = () => {

  const toggleMode = () => {
    console.log("Clicked toggle mode!")
  }

return (...)
}

export default ButtonComponent;

Currently, we have a console.log() inside the toggleMode function. In a later part of this article, we will come back to properly write this function to update the value of the reactive variable.

Now let’s go back to the ladingPage.jsx file we created before now and add the button we just created:

import React from 'react';
import ButtonComponent from '../components/button';

const LandingPage = () => {
  return (
    <div
      style={{
        height: '100vh',
        backgroundColor: 'white',
        }}
    >
      <h1>Welcome to Theme Toggle Appliation!</h1>
      <ButtonComponent />
    </div>
  )
}
export default LandingPage

To add the button, we simply imported it into our page and added it below the h1 element we already had on the page.

Here’s how our web app looks like at the moment.

We are almost done building our app. Next, let’s change the background and text color of the page in the landingPage.jsx style to conditionally be black or white based on the boolean value of our reactive variable which would be toggled in the button component later. To do this, we will also use the useQuery hook to get the current value of our reactive variable.

Our landingPage.jsx file will finally look like this:

import React from 'react'
import { useQuery } from '@apollo/client';
import ButtonComponent from '../components/button';
import { darkMode, GET_DARK_MODE } from '../graphql/reactivities/themeVariable';

const LandingPage = () => {
  const {loading, error, data} = useQuery(GET_DARK_MODE);
  return (
    <div style={{ height: '100vh', backgroundColor: data.darkMode ? 'black' : 'white', color: data.darkMode ? 'white' : 'black' }}>
      <h1>Welcome to Theme Toggle Appliation!</h1>
      <ButtonComponent />
    </div>
  )
}
export default LandingPage

Pay attention to the way we change the backgroundColor and color of the div container conditionally based on the boolean value of the reactive variable returned. We make use of a ternary operator to set the backgroundColor to black or white depending on the value of data.darkMode. The same thing should be done for the value of color. This is all we need to for the landingPage.jsx component.

The final thing we will need to do to get our application to be working is to make our toggleMode function in the button component able to modify the reactive variable on click of the button. Let’s look at how to modify a reactive variable again, this time, in a real app example.

Modifying A Reactive Variable

As we’ve previously learned, to modify a reactive variable, all you need to do is to call the function returned by makeVar and pass in the new value inside of it. Here’s how that will work in our case:

Go to the button component and do this:

...
import { GET_DARK_MODE, darkMode } from '../graphql/reactivities/themeVariable';

const ButtonComponent = () => {

  const toggleMode = () => {
    darkMode(!darkMode)
  }

return (...)
}

export default ButtonComponent;

First, we imported the GET_DARK_MODE query and the darkMode reactive variable from the file they were exported from.

Next, we wrote an arrow function for toggleMode and called the darkMode function returned by makeVar and passed an invert of the current value it contained as the new value the reactive variable will carry when it is clicked on.

We have our entire app powered by a reactive variable and once there’s a change to the value held in the reactive variable, every component or page dependent on that variable for an action to trigger is updated and the user interface is updated with the current changes. We escaped all the hurdles of dispatch functions and other ambiguous steps we have to follow when using other state management libraries like Redux or Context API.

Conclusion

Reactive variables in Apollo client give you a sweet, easy to use, easy to update, and a consistent querying pattern with querying a regular remote GraphQL API. Learning to use reactive variables for state management is a plus to you because it gives you the flexibility of choice among many tools. reactive variables would enable you to manage locally shared global state among components without the extra boilerplate that would usually come with the dominant state management libraries that already exist.

  • Check out the finished code on GitHub.

Related Resources