Life Without Redux: Using Apollo for Local State

We show you how to handle local application state using Apollo.

Shain Lafazan
The Notice Board
4 min readSep 6, 2018

--

Hail Guildmates!

My name is Shain, and I’m an engineer on the BitGuild platform. I’ll be sharing how we handle application state, and some of the knowledge I’ve collected while integrating Apollo into a React + Redux environment.

To start, if you’ve ever encountered a software bug, you’re not alone! There are many possible causes for bugs in applications, but I’m willing to bet that the last bug you ran into was caused by problems with application state.

State management has been around for a long time. For those of you who browsed the web in the 90’s, this might knock some memories off the shelf:

Google’s home page, ~1997

Web apps have come a long way in 20 years, and they’re increasingly more complex. Modern web apps are made of hundreds of components that we can read, click on, toggle, type into, and drag around the screen — just imagine the cognitive load of keeping track of them all! The complexity that makes our apps so useful also makes them prone to error, so the web community has created powerful patterns and tools to manage the state for all those components (and help keep web developers mostly sane!).

At BitGuild, we use React, Apollo, and GraphQL to manage application state.

An Apollo Client workflow pattern

I’ll break it down:

  • React renders, updates, and destroys components of the user interface
  • Apollo defines the ways we fetch and send data and provides interfaces to store it
  • GraphQL is the query language for fetching and updating our data

So, you may be asking… Where is the Redux? How do you handle local state? While implementing the Apollo client, we realized that apps with few local state keys can use apollo-link-state to take the place of Redux for managing local state!

I’ll take you through a simple example using “To Do’s” and a color scheme. Install apollo-boost and graphql-tag and invoke the constructor in a new file:

// apollo.jsimport ApolloClient from 'apollo-boost';import gql from 'graphql-tag';const client = new ApolloClient({  uri: 'https://my.api/v1',  /* the magic */  clientState: {    defaults: {},    resolvers: {},    typeDefs: ``,  },});

We’ve created our Apollo client, which can interface with our GraphQL server and fetch / send all the data we need. Note that the magic happening is the clientState object, where we will now configure our Apollo client to query its local cache as well as our remote server.

Let’s update our default state and typeDefs so we have an initial state and definitions:

defaults: {  uiColorPalette: 'light',},typeDefs: `  type Query {    uiColorPalette: String  }`,

Say our user wants a dark theme for the app and clicks an imaginary “Dark theme” button on the page. We can update our state by writing to the cache. Let’s do it:

client.writeData({ data: { uiColorPalette: ‘dark’ } });

Seems too simple, right? We simply pass a state key and a state value, and the value will change. If we want to confirm this, we can use a local query to access our local state:

const { data } = await client.query({  query: gql`{    uiColorPalette @client  }`,});

Behold! The data object now holds our brand new local state, { uiColorPalette: ‘dark’ }. We now have a method of caching local state and modifying it. But what if we wanted to do something more complex, perhaps perform a complex mutation?

Let’s add to our Apollo clientState object:

defaults: {  todos: [],  uiColorPalette: 'light',},typeDefs: `  type Todo {    id: Int    value: String  }  type Mutation {    updateTodos(todos: [Todo]!)  }  type Query {    uiColorPalette: String    todos: [Todo]  }`,resolvers: {  Mutation: {    updateTodos: async (_, { todos }, { cache, getCacheKey }) => {      await cache.writeData({ data: { todos } });      return null;    },  },},

We’ve done a few things here:

  1. Added a default value for the new todos state key
  2. Added a type definition for Todo
  3. Added a Query key for todos
  4. Added a resolver for our updateTodos mutation

Now we can mutate our local state and query for the result:

client.mutate({  mutation: gql`    mutation updateTodos($todos: [Todo]!) {      updateTodos(todos: $todos) @client    }  `,  variables: {    todos: [{      id: 1,      value: 'Play more crypto games',      __typename: 'Todo',    }],  },});

That’s it! If we inspect our Apollo cache or query for our local Todo’s, we’ll see that the state has been mutated and the new todo has been added to the array. This is powerful because it allows us to write apps without all the boilerplate from Redux!

At BitGuild, we like to use the right tools for each project and get as much utility out of them as we can. While this article shows a way to use Apollo and GraphQL without Redux on the server, we still believe that Redux is a valuable state management pattern… but if there’s a way to simplify and streamline application state, you can bet we’ll be on it!

BitGuild’s mission is to revolutionize the global gaming industry by creating a platform for a brand new class of games that live on the blockchain. Blockchain games completely redefine the relationship between players and developers by facilitating full and true ownership of in-game assets, cheap & safe item trading, cross-game compatibility of items & currency, and more.

Join the community on Twitter, Discord, and Facebook.

--

--