Part 2: State Management in Apollo Client
Apollo Client is a state management library for JavaScript that lets you manage both local and remote data with GraphQL. Use it to retrieve, cache, and modify application data while automatically updating your user interface.
It helps you structure code in a predictable, and declarative way that is consistent with modern development practices.
At the end of this post, you'll find a summary of the entire process.
Store management
- 1. Defining the store
- 2. Mechanism for retrieving data
- 3. Mechanism for setting/changing data
Defining store
The first step is to create a field policy for the field/property that we want to store.
Field policy
A field policy is way to customize how a particular field in your Apollo Client cache is read and written. You can define field policies for both local-only fields and remotely fetched fields.
Each field policy can include:
- • A
read()
function - what happens when a field’s cached value is read - • A
merge()
function - what happens when a field’s cached value is written - • An array of
key
arguments - to help the cache avoid storing duplicate values
These three parts of each type policy are used to define a specific (out of the default) behaviour of a particular field.
The integral part of the local state management is the read
function.
The read()
function
If you define a read()
function for a field, the cache calls that function whenever your client queries for the field. In the query response, the field is populated with the read()
function's return value.
It is possible to define a read()
function for a field that is not even defined in your schema. By doing this, Apollo Client will not include it in requests to your GraphQL server.
It is important to note that the read()
function can be used for other powerful use cases as well - transforming cached data, manually executing other cache operations, fetching data from a separate store, manipulating the received remote data - sanitizing, validating, preparing.
In-memory cache
Apollo Client stores the results of its GraphQL queries in a normalized, in-memory cache. This enables your client to respond to future queries for the same data without sending unnecessary network requests.
The in-memory cache comes with a default behaviour, but it is also highly configurable.
Managing client-side local state is one use case that is of interest to us. To achive that, we define our application's field policies in a map that we then provide to the constructor of Apollo Client's InMemoryCache. Each field policy is a child of a particular type policy.
Storing and changing data
For defining the initial values and later for changing our fields we use Reactive Variables.
Reactive Variables are a mechanism for storing local state outside of the Apollo Client cache. Because they are separate from the cache, reactive variables can store data of any type and structure, and you can interact with them anywhere in your application without using GraphQL syntax.
Key points about the reactive variables are:
1. Triggering reactive changes in your application.
Whenever you change the value of a reactive variable, queries that depend on that variable and components that use that reactive variable will refresh, and your application's UI updates accordingly.
A query depends on a reactive variable if any of the query’s requested fields define a read function that reads the variable value.
2. A useful mechanism for storing local state outside of the Apollo Client in-memory cache.
Because they are separate from the cache, reactive variables can store data of any type and structure, and you can interact with them anywhere in your application without using GraphQL syntax.
Retrieving data
First, we need to create a local-only field and a query which we will specifically use to query this field.
Local fields
We can create a local field by including the @client
directive on it.
We retrieve data by using the useQuery()
hook, which is the primary API for executing queries in an application that uses Apollo Client, and the previously created query which defines local-only field/s.
Summary of the flow of the entire process
-
1. Define reactive variable that will be used for setting the values of the local fields
-
2. Define field policies and
read()
functions for each. These are used to return the value provided by the previously created reactive variable for that field -
3. Include the field policies in the in-memory cache
-
4. Use the “setter” reactive variable anywhere in the app to update the value of the specific field and use the getter hook that utilizes
useQuery()
- this way you always get up-to-date data that is automatically cached by the Apollo Client