Local State Management with Apollo Client
Tags: javascript, graphql, apollo, react
Published: 2020-12-09 12:05:47

---
title: Local State Management with Apollo Client
published: true
description: I'm building a social media network and collaboration tool based on mind maps, documenting my work in this series of blog posts
tags: javascript,graphql,apollo,react
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/vrb4re0xt7w83qngym92.png
series: Nuffshell Coding Diary
---

*I'm building a social media network and collaboration tool based on mind maps, documenting my work in this series of blog posts. Follow me if you're interested in what I've learned along the way about building web apps with React, Tailwind CSS, Firebase, Apollo/GraphQL, three.js and TypeScript.*

# Today's Goal

In my [previous posts](https://dev.to/pahund/the-nuffshell-coding-diary-6ij), I've built a 3D mind map, using React and three.js. 

For this project, I've used [CodeSandbox](](https://dev.to/pahund/the-nuffshell-coding-diary-6ij)) exclusisvely for coding and I just love it! It works just like VS Code, but runs completely in the browser and lets me share my code easily in my blog posts.

Just one thing that bothered me as I worked on my mind map: Performance issues. The nature of rendering an interactive 3D model involves having a loop that continuously updates, running 60 times per second. CodeSandbox seems to have problems with this when my demo is running in the preview window, writing code slows down and becomes a pain.

To fix this, I'm going to add a “pause” button to my mind map demo that I can use to start/stop the rendering loop.

![Pause button](https://dev-to-uploads.s3.amazonaws.com/i/u25t6hpb7r2ytd60eyo5.gif)

# Picking a State Management Library

I know I'm going to have to add some way of managing application state to my React app at some point. My plan is to ultimately have a web app where users log in to collaborate on mind maps with others. 

My “pause” button is the first use case that actually requires an application state, so it's time to think about state management.

I've been using [Redux](https://redux.js.org) in my day job for five years now. Some people think it's overly complicated. I disagree, especially with the latest version of [Redux Toolkit](https://redux-toolkit.js.org), it's become a lot easier to adopt and use.

For small projects, I also like [Zustand](https://github.com/pmndrs/zustand) a lot – much more light-weight than Redux. 

Then [Recoil](https://recoiljs.org) popped up this year and looks really promising, especially considering it is backed by Facebook, the company behind React.

However, for my collaborative mind mapping project, I've decided to go with [Apollo Client](https://www.apollographql.com/apollo-client). 

This is much more than just a state management library. Apollo is a framework for storing and fetching data with [GraphQL](https://graphql.org), an API query language.

I will need to store my users' mind map data in a database. GraphQL is a great way to access this stored data that I've been wanting to try out for a long time now.

Toggling my 3D animation loop on and off can, of course, be achieved much, much easier with far less overhead. Sooner or later, though, it is going to pay off to have one framework for managing my local application state and remote data storage. 

![Cracking a nut with a sledgehammer](https://dev-to-uploads.s3.amazonaws.com/i/7i1rj3lcu4no3rtjx2ur.gif)

So let's play [Rube Goldberg](https://en.wikipedia.org/wiki/Rube_Goldberg) and crack a nut with a sledgehammer – let's toggle a single boolean value with Apollo Client!

# Recommended Reading

I'm not going to detail out every single step in today's blog post, I don't want to bore you to death. That being said, if you want to use Appollo Client for local state managment in your own app, I highly recommend this article:

* [Local State Management with Reactive Variables](https://www.apollographql.com/blog/local-state-management-with-reactive-variables/)

This was published by the makers of Apollo and is the most up-to-date and comprehensive tutorial I've found. 

When you google “apollo local state management”, you'll come across quite a few more – I found that they were almost all outdated. 

There is a library [apollo-link-state](https://www.apollographql.com/docs/link/links/state/) that is deprecated now, because Apollo Client now supports managing local state out of the box.

# The Code

## ApolloProvider

After adding the npm packages [@apollo/client](https://www.npmjs.com/package/@apollo/client) and [graphql](https://www.npmjs.com/package/graphql) to my project, the first step is to initialize the client and add an ApolloProvider component:

*[index.js](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/index.js)*

```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloClient, ApolloProvider } from '@apollo/client';
import { cache } from './storage';
import App from './App';

const client = new ApolloClient({
  cache
});

const rootElement = document.getElementById('root');
ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>,
  rootElement
);
```

## Cache

Notice how the client in the code example above is initialized with a cache. The cache, in Apollo Client, is the central module for managing data:

*[cache.ts](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/cache.ts)*

```typescript
import { InMemoryCache } from '@apollo/client';
import { appConfigVar } from './appConfig';

const cache: InMemoryCache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        appConfig: {
          read() {
            return appConfigVar();
          }
        }
      }
    }
  }
});

export default cache;
```

Note how the cache module is written in TypeScript. So far, I've been using JavaScript for my project. The tutorial I'm following along is written in TypeScript. I was planning to convert to TypeScript at some point, anyway, so I decide to use TypeScript for the Apollo modules in my project.

## Reactive Variables

The `appConfigVar` in my cache is a [reactive variable](https://www.apollographql.com/docs/react/local-state/reactive-variables/). This is where the magic happens – the idea is to create reactive variables for everything that is stored locally, i.e. not through a GraphQL API.

*[appConfigVar.ts](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/appConfig/appConfigVar.ts)*

```typescript
import { makeVar } from '@apollo/client';
import AppConfig from './AppConfig';
import initialAppConfig from './initialAppConfig';

const appConfigVar = makeVar<AppConfig>(initialAppConfig);

export default appConfigVar;
```

## AppConfig Interface

In TypeScript, we define types or interfaces to help the compiler check if everything is typed correctly.

*[AppConfig.ts](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/appConfig/AppConfig.ts)*

```typescript
interface AppConfig {
  isPaused: boolean;
}

export default AppConfig;
```

As you can see, I'm using `AppConfig` objects to store the state of my application. So far, this contains only one boolean value, `isPaused`. The nut I'm cracking with my sledgehammer.

## Initial App Config

In *appConfigVar.ts*, I'm using *initalAppConfig* to set up my reactive variable.

*[initialAppConfig](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/appConfig/initialAppConfig.ts)*

```typescript
import AppConfig from './AppConfig';

const initialAppConfig: AppConfig = JSON.parse(
  window.localStorage.getItem('nuffshell.appConfig')
) || {
  isPaused: false
};

export default initialAppConfig;
```

I want my app state to be persistent, even when I reload the page in the browser. To achieve that, I'm storing it in the browser's [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).

When the app config reactive var is initialized, I'm checking the local storage for a previously saved app config. If there is one, I'm using this, otherwise, I use a default one, with `isPaused: false`.

## Query to Get the App Config

To get the app config, I define a GraphQL query:

*[GetAppConfig.ts](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/appConfig/GetAppConfig.ts)*

```typescript
import { gql } from '@apollo/client';

const GetAppConfig = gql`
  query GetAppConfig {
    appConfig @client {
      isPaused
    }
  }
`;

export default GetAppConfig;
```

Notice the `@client` part in the query definition – this tells Apollo Client that the app config comes from a local state, i.e. it does not have to be fetched through the GraphQL API.

## Custom Hook

I've decided to write a custom hook to wrap up all that Apollo goodness and to be able to conveniently use it from my React components:

*[useAppConfig.ts](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/appConfig/useAppConfig.ts)*

```typescript
import { useQuery } from '@apollo/client';
import appConfigVar from './appConfigVar';
import GetAppConfig from './GetAppConfig';
import saveAppConfig from './saveAppConfig';

export default function useAppConfig() {
  const {
    data: { appConfig }
  } = useQuery(GetAppConfig);

  return {
    isPaused: appConfig.isPaused,
    togglePause() {
      appConfigVar({ ...appConfig, isPaused: !appConfig.isPaused });
      saveAppConfig();
    }
  };
}
```

As you can see, I'm using the [useQuery](https://www.apollographql.com/docs/react/api/react/hooks/#usequery) hook from the Apollo Client to get the current value of the app config by passing my `GetAppConfig` query.

My hook returns an object that allows React components to get the current state (is the app paused?) and toggle the pause on/off.

## Persisting the Config in Local Storage

In my custom hook, I'm calling this function [saveAppConfig](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/storage/appConfig/saveAppConfig.ts) to store my config in the browser's local storage:

```typescript
import appConfigVar from './appConfigVar';

export default function saveAppConfig() {
  window.localStorage.setItem(
    'nuffshell.appConfig',
    JSON.stringify(appConfigVar())
  );
}
```

## Toggle Button Component

Here's the `useAppConfig` in action, in the [PauseButton](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/features/pauseButton/PauseButton.js) component:

```javascript
import React from 'react';
import { useAppConfig } from '../../storage/appConfig';
import styles from './PauseButton.module.css';

export default function PauseButton() {
  const { isPaused, togglePause } = useAppConfig();

  return (
    <button className={styles.PauseButton} onClick={togglePause}>
      {isPaused ? 'unpause' : 'pause'}
    </button>
  );
}
```

## Adding the Button

I'm adding this button component to my [App](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/App.js) component:

```javascript
import React, { createRef, useEffect } from 'react';
import { PauseButton } from './features/pauseButton';
import renderMindMap from './renderMindMap';

export default function App() {
  const divRef = createRef();
  useEffect(() => renderMindMap(divRef.current), [divRef]);
  return (
    <>
      <PauseButton />
      <div ref={divRef} />
    </>
  );
}
```

## Pausing the 3D Rendering

Finally, inside the loop that runs 60 times per second to re-render my 3D model, I can get the current state, asking: Is the app paused?

*[renderMindMap.js](https://codesandbox.io/s/step-21-local-state-with-apollo-part-6-181dt?file=/src/renderMindMap.js)*

```javascript
(function animate() {
  const { isPaused } = appConfigVar();
  if (!isPaused) {
    graph.tickFrame();
    controls.update();
    renderer.render(scene, camera);
  }
  requestAnimationFrame(animate);
})();
```

In this case, I'm not using the `useAppConfig` hook, because this is not a React component. I can simply get the app config by calling my reactive variable `appConfigVar`.

# The Result

Wow, what a wild ride – so much code for such a little thing to achieve! Even old-school Redux is simpler. I hope it will be worth it in the long run, when I fetch and write user data through a GraphQL API and can then handle everything through Apollo.

{% codesandbox 181dt %}

# To Be Continued…

I'm planning to turn my mind map into a social media network and collaboration tool and will continue to blog about my progress in follow-up articles. Stay tuned!