Using Apollo Client 3 as a State Management Solution

State management is one the hardest parts of a React / React Native app. From time to time, new solutions are appearing, trying to improve or simplify existing state management tools.

In my first React apps, I used Redux, one of the most popular state management solutions. In many cases I had to add boilerplate, repetitive code to achieve what I need with the App state: Storage, State Update, Reactivity. I had to use other tools like redux-thunk or redux-saga for side effects (e.g. async operations), and in some cases for making the app more performant used reselect. The development process was slow and required some additional efforts to do it the right way.

Then, I started using GraphQL, and the ecosystem impressed me a lot. In my case, I was using Apollo, both server and client, and for the first time, I felt that the Apollo Client was the right tool for managing the application state.

Apollo Client 3 makes it simpler to execute GraphQL Queries and Mutations, track loading states, errors (both GraphQL & Network). Apollo Client also keeps track of the Application State in a normalised cache (Storage), allows to update the State using the Queries & Mutations (State Update), broadcast events notifying about state updates (Reactivity). We can use Apollo Client to communicate with our API and also as a State Management solution.

Let’s try to understand the benefits of Apollo Client in a real-world example.

Imagine a simple eCommerce app:

  • A product list is visible. When the product stock > 0, an ‘add to cart’ is visible, otherwise ‘notify me when available’
  • On a different Screen, we have the Cart: we can view products in the Cart, update the quantity and remove.

So, we need to have a global application state: adding a product to the Cart should update the Cart screen with the new item, and if the stock is now 0 the product view should change the button to ‘notify me’. Same, if the Cart product gets updated from the cart view, the Products screen should reflect the stock and update the button. Doing this with Apollo Client is very easy. Let’s say we have:

type Product {  id: ID!  name: String!  stockAvailable: Boolean!}type CartProduct {  id: ID!  totalProducts: Int!  product: Product!}type Cart {  id  cartProducts: [CartProduct]}Query {  cart: Cart  products: [Product]}Mutation {  addProductToCart: Cart!  updateProductInCart: Cart!  removeProductFromCart: Cart!}

In the App, we will have:

  • ProductScreen -> call products Query, addProductToCart Mutation. The Products Query will make the Apollo Client store a Normalised cache with all Product details, something like a HashMap with this information:

Product:1 { id: 1, name: “product 1”, stockAvailable: true }

Product:2 { id: 1, name: “product 1”, stockAvailable: true }

Then, when calling the addProductToCart mutation, we can query at the same time the last stock information for all the cart products, and that way, in a single call we are able to modify our cart and retrieve the updated information.

addProductToCart(productId: 1) {  id: "100",  cartProducts: [{    id: "101",    totalProducts: 5,    product: {      id: "1"      name: "product 1",      stockAvailable: false    }  }]}

In this example, the stockAvailable is now false with product with id = “1”: Apollo Client will update the internal Product cache, broadcast the change to all the components (ProductScreen, CartScreen) using the product Queries, and now in the ProductScreen, the Product button changes from “add to cart” to “notifying when available”.

With this simple example, we can see how easy and fun is to use Apollo Client 3 as our State Management solution, bringing to our project: State Storage, State Updates, Reactivity in our components.

Senior FullStack Developer with 10+ years experience delivering solutions to clients in Australia, UK, USA and Spain.