Managing Cache with Apollo in React JS
Introduction
Apollo Client uses an in-memory cache to store the results of your GraphQL queries. This allows Apollo to automatically update your UI with cached data and send fewer requests to your server. In this tutorial, we will explore how to manage cache with Apollo in a React app.
Step 1: Setting Up Apollo Client
Before managing cache, ensure that you have set up Apollo Client in your React app. If you haven’t done so already, follow the steps below:
Install Apollo Client and GraphQL:
npm install @apollo/client graphql
Create an Apollo Client instance:
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000/', // Replace with your GraphQL API endpoint
cache: new InMemoryCache(), // This is where caching happens
});
export default client;
Wrap your app with ApolloProvider in your main entry file:
import { ApolloProvider } from '@apollo/client';
import client from './apolloClient';
import App from './App';
ReactDOM.render(
,
document.getElementById('root')
);
Step 2: Using Cache in Apollo Client
Apollo Client uses an in-memory cache to store query results. By default, it caches the results of your queries so that subsequent requests for the same data are served from the cache rather than making a network request.
Example: Query and Caching Data
Let's fetch a list of users and take advantage of caching:
import React from 'react';
import { useQuery, gql } from '@apollo/client';
// Define the query to fetch users
const GET_USERS = gql`
query GetUsers {
users {
id
name
}
}
`;
const UsersList = () => {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return Loading...
;
if (error) return Error: {error.message}
;
return (
{data.users.map((user) => (
- {user.name}
))}
);
};
export default UsersList;
When you first make this query, Apollo Client fetches data from the server and stores it in its cache. The next time you make the same query, Apollo will serve the data from the cache without making a network request.
Step 3: Customizing Cache Behavior
Apollo Client allows you to customize how the cache works using options such as fetchPolicy
and nextFetchPolicy
to control when the cache should be read or updated.
Using fetchPolicy
The fetchPolicy
option determines how Apollo Client fetches data for a query. By default, it uses 'cache-first'
, meaning it will try to fetch data from the cache first and only make a network request if the data isn’t in the cache.
const { loading, error, data } = useQuery(GET_USERS, {
fetchPolicy: 'network-only', // Force a network request every time
});
Cache-first Policy
The default fetch policy is cache-first
. This means Apollo Client will first check the cache for existing data and only request data from the server if it's not available in the cache.
Network-only Policy
The network-only
fetch policy forces Apollo Client to always fetch data from the network and never use the cache.
Cache-and-network Policy
The cache-and-network
policy returns data from the cache if available but always triggers a network request to get updated data:
const { loading, error, data } = useQuery(GET_USERS, {
fetchPolicy: 'cache-and-network',
});
Step 4: Manually Updating the Cache
In some cases, you may need to manually update the cache after performing a mutation (e.g., adding a new user). You can do this by using Apollo Client's cache.writeQuery
and cache.writeFragment
methods.
Example: Updating Cache after a Mutation
Let’s create a mutation that adds a new user and updates the cache to reflect the changes immediately:
import React, { useState } from 'react';
import { useMutation, gql } from '@apollo/client';
import { GET_USERS } from './UsersList';
const ADD_USER = gql`
mutation AddUser($name: String!) {
addUser(name: $name) {
id
name
}
}
`;
const AddUserForm = () => {
const [name, setName] = useState('');
const [addUser] = useMutation(ADD_USER, {
update(cache, { data: { addUser } }) {
// Read the data from the cache
const { users } = cache.readQuery({ query: GET_USERS });
// Update the cache by adding the new user
cache.writeQuery({
query: GET_USERS,
data: { users: users.concat([addUser]) },
});
}
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
await addUser({ variables: { name } });
setName('');
} catch (error) {
console.error("Error adding user:", error);
}
};
return (
);
};
export default AddUserForm;
In the code above:
update
: The update function allows us to modify the cache after the mutation is completed. We manually update the cache by reading the current data withcache.readQuery
and then writing the updated data withcache.writeQuery
.
Step 5: Cache Eviction and Resetting
Sometimes, you may want to clear or reset the cache. Apollo Client provides methods to reset the cache when needed.
Evicting a Specific Cache
You can evict a specific cache entry using cache.evict
:
client.cache.evict({ id: 'User:1' }); // Evict the user with ID 1
client.cache.gc(); // Garbage collect to remove the evicted cache entry
Resetting the Entire Cache
If you need to reset the entire cache, you can use the client.resetStore()
method:
client.resetStore(); // Resets the entire cache and refetches active queries
Step 6: Running Your Application
After setting up cache management, run your React app to test cache behavior:
npm start
Your application should now be using Apollo Client’s cache to store and update data efficiently.
Conclusion
In this tutorial, we learned how to:
- Use Apollo Client's cache to store query results.
- Control fetch policies to manage cache behavior.
- Manually update the cache after a mutation.
- Evict and reset the cache when needed.
By understanding and managing Apollo Client’s cache, you can significantly improve the performance of your React applications by reducing network requests and ensuring that your UI is always in sync with the latest data.