Skip to content

State Management with Apollo Client and Vue using Reactive Variable

State Management is an integral part of Software development with tools like VueX, Pinia which is like VueX 5, Redux, Context API, and others.

In our example, we will be using Vue3 composition API, a bit of TypeScript.

This article will assume that you already have a project setup with Apollo.

State Managemnet

Before we dive into what state management is and what it offers, lets understand few thing which are integral to it.

What is State?

State is a part of an application, such as user details, usernames, login information, and website themes(dark or light).

In simple terms, state is like a warehouse that's contents you can access when you need to from wherever you are.

What is State Management?

State Management is just simply a design pattern to help synchronize the state of the application throughout all of the components in the application. This also prevents us from passing too many props accross the application.

When to implement State Management:

  • When the application contains large number of components.
  • To prevent redundant data, knowing well some other components might need that data.
  • Prevent passing props across the application, making it messy.

For more explainations, you can check the links below:

How to implement State management?

There are so many ways to do it. But in this discussion, we will be implementing it with Vue Apollo.

Apollo Client

It is the client for Vue Apollo. It helps to easily integrate GraphQL queries and mutation in your application which, in our case, is Vue.

Reactive Variables

This is a new feature from Apollo Client 3.

Reactive variables are a useful mechanism for representing local state outside of the Apollo Client cache.

How to create a Reactive variable in Apollo?

This is quite simple since Apollo client provides us with makeVar.


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

const counts = makeVar(0);
console.log(counts());
// Output: 0

/* Update the value of counts */
counts(1);
console.log(counts());
// Output: 1

const cartItemsVar = makeVar([]);
console.log(cartItemsVar());
// Output: []

/* Update the value of cartItemsVar */
cartItemsVar(['Vue', 'Apollo', 'State Management']);
console.log(cartItemsVar());
// Output: ['Vue', 'Apollo', 'State Management']

const isLoggedIn = makeVar(false);
console.log(isLoggedIn());
//  Output: false

/* Update the value of isLoggedIn */
isLoggedIn(true);
console.log(isLoggedIn());
// Output: true

What the above code means is we make use of makeVar to create a variable of any type be it boolean, number, array, or object. Also, by calling the variable that makeVar is assigned to without passing a parameter will only output its current value, but passing a parameter will update its value.

You can learn more about makeVar here.

Setup of the Project

We will be creating a state for a counter that does increment, decrement, or reset. But first, let's set up our enviroment.

Setting up the Reactive variable

Lets create our reactive variable in ./variables/counts.js:


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

export const counts = makeVar(0);

Configuring the cache

Lets also update our cache config to something like this:


// Need to import the variable we reated
import { counts } from '../variables/counts';

// Cache implementation
const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        count: { //The name we will be querying
          read() {
            return counts(); //Returns the updated value of counts
          },
        },
      },
    },
  },
});

Creating a component

Let's create ./components/Counter.vue component:


<template>
  <div>
    <div class="col-3">
      <h6>
        Count: <span v-if="!loading">{{ count }}</span>
      </h6>
    </div>
    <div class="col-3">
      <button @click="increment">Increment</button>
    </div>
    <div class="col-3">
        <button @click="decrement">Decrement</button>
    </div> 
    <div class="col-3">
      <button @click="reset">Reset</button>
    </div>

  </div>
</template>

<script lang="ts">
import { computed, defineComponent } from 'vue';

export default defineComponent({
  name: 'NumberCounter',
});
</script>

<script lang="ts" setup>
import { counts } from 'src/variables/counts';
import { useQuery } from '@vue/apollo-composable';
import gql from 'graphql-tag';

// This is to fetch the count value using the query
const COUNTERS_QUERY = gql`
  query Counter {
    count @client
  }
`;

const { result, loading } = useQuery(COUNTERS_QUERY);

const count = computed(() => (result.value ? result.value.count : 0));

const increment = () => {
  let cnts = counts();
  cnts++;
  counts(cnts);
};

const decrement = () => {
  let cnts = counts();
  cnts--;
  counts(cnts);
};

const reset = () => {
  cnts= 0;
  counts(cnts);
};

</script>

Please note that you can always get the counts value even without the GraphQL query. All you need to do is call count() like the example about Reactive variables, and force a re-render using loading as a ref.

Like this:


 <template>
  <div>
    <div class="col-3">
      <h6>
        Count: <span v-if="!loading">{{ counts() }}</span>
      </h6>
    </div>
    <div class="col-3">
      <button @click="increment">Increment</button>
    </div>
    <div class="col-3">
        <button @click="decrement">Decrement</button>
    </div> 
    <div class="col-3">
      <button @click="reset">Reset</button>
    </div>

  </div>
</template>

<script lang="ts">
import { ref, defineComponent } from 'vue';

export default defineComponent({
  name: 'NumberCounter',
});
</script>

<script lang="ts" setup>
import { counts } from 'src/variables/counts';
// loading ref to help re-render when there is a change so as to get the latest value of counts.
const loading = ref(false);

const increment = () => {
  let cnts = counts();
  loading.value = true;
  cnts++;
  counts(cnts);
  loading.value = false;
};

const decrement = () => {
  let cnts = counts();
  loading.value = true;
  cnts--;
  counts(cnts);
  loading.value = false;
};

const reset = () => {
  cnts = 0;
  loading.value = true;
  counts(cnts);
  loading.value = false;
};

</script>

loading ref to help re-render when there is a change so as to get the latest value of counts.

The manner you will prefer depends on what you want to achieve. But I will go with the example with the query as it still preserves the GraphQL feel.

Conclusion

State Management with Apollo Client saves you the stress of installing VueX, or Redux (for the React users), as long as the application makes use of Apollo. In this quick training, we learned about state and why state management is important to building and maintaining applications.

Further, we learned how to manage state by using a reactive variable in Apollo. If you need any help understanding how to use this training, or additional clarification, please feel free to reach out to me.