5 Reasons why Apollo Client 3 is my top pick for Global State Management in React Apps
Regardless of what solution we choose, managing state in a client-side application involves handling storage, update the state/store and notify any components that subscribed to that data of the changes (reactivity).
After having used Redux before for managing my global client state I recently started favouring Apollo Client 3. Here are quick 5 reasons as to why :
- Quick and Simple Setup
All we need to do is initiate and configure the cache and the client. The most basic configuration is to just create a new InMemoryCache with the default initial values, an ApolloClient and point it to the GraphQL endpoint as well as passing in the cache. If we only use local data than we can pass just the cache. This is in contrast with Redux where you need to setup reducers, selectors, actions, action creators. Just feels like a lot more setup to get going, especially for small and medium sized projects.
As for data access and operations in Redux we use the useSelector hook for retrieving data from the store and dispatch for modifying it. In Apollo Client we have the useQuery and useMutation for reading and writing data.
2. Reactivity out of the box using reactive variables and cache policies.
These variables can be accessed and modified from anywhere in the application and they re-run every query that depends on them as well as triggering a component re-render each time the data has changed.
These reactive variables are also good containers for storing global state that we need access to throughout the app.
3. Data normalization and automatic updates of local cache (client store).
Also whenever we modify data on the server Apollo automatically updates the local cache with the updated data as long as we return it in the mutation result :
This is where normalizing the data is important because this gives Apollo a way to keep track and identify the data that needs to be updated.
Note here that the default behaviour is to replace the old data with the new data but we can also define custom merge functions.
There are few cases where Apollo does not do this automatically but in those cases we can pass an update function to the mutation where we can specify how we would like to update that data in the cache:
4. Easier handling of fetch logic and async states.
Apollo Client comes with all of the logic for handling async state, retry logic and normalizing data build into the client and exposed through the API and all we need to do is declaratively ask for the data we require as well as perform the operations on that data (write, update, delete):
Apollo Client also gives us the possibility to configure the cache policies and by default it will look in the local cache first and then fetch from the server just the data that’s not available in the local cache, optimising the network calls for future requests of the same data.
This is also where normalization of data plays a huge part because it is able to easily identify all the entities in the cache and what data needs to be fetched.
Also we can set default values for the local data when we configure the cache. There we can also modify what the cache returns before the reads and the writes — we can perform client logic on the data before being exposed.
5. One solution for both client and server. — Like the line from the Lord of The Rings , “One ring to rule them all”.
Since GraphQL is my go-to solution for exchanging information between the client and server because of the strongly typed schema and intuitive query language, I find it more convenient to use Apollo Client for managing the client data as well instead of trying to store that data in a Redux store for example and have to go through the process of integrating the 2 solutions.
We can even have Apollo include both client and server data fetching in the same query. This is possible using the new field policies and reactive variables in Apollo Client 3. When declaring field policies for the local data — separate from the server schema- we can specify what data to populate them with, either from storage or from a reactive variable. Using the @client tag we can query for data from these local fields only.
Bonus — Quick and easy types generation on the client. We can easily generate types on the client based on the server schema using one of the few tools available. I wrote a more detailed article with a few tips for getting GraphQL Code Generator to work well with React. We can even take this a step further and generate React hooks that are ready to use for fetching and modifying the data — fully typed.
So there you have it, quick 5 reasons why Apollo Client is currently my go-to tool for managing global state in any React application.