In this talk, I will cover why your switch to GQL and Apollo Client 3 should make you walk away from Redux. I will also talk about my journey from Redux -> Apollo Client and use a few project examples for why it makes sense holistically and the challenges I experienced during that transition. Toward the end of this talk, you will be confident about the pros and cons of each approach.
GraphQL. State management
AI Generated Video Summary
Today's talk is about GraphQL State Management and the benefits it offers. Redux and Apollo GraphQL are compared for their state management capabilities, with Apollo Client 3 highlighted as a powerful tool that simplifies front-end development. Data transformations and manipulation in Apollo Client, including type policies and reactive variables, are discussed as key features. The talk concludes by emphasizing the advantages of using Apollo Client for state management and providing access to the presentation materials and newsletter for further updates.
1. Introduction to GraphQL State Management
Today's talk is about GraphQL State Management. A State Manager is a way to manage application state. It allows us to update and access state easily. A good State Manager normalizes data, transforms it consistently, and allows visualization of global state for easier debugging.
So, yeah, let's get started. So as I said, I'm Ankita. I've been working in the tech industry for over 10 years now. I am currently working as an educator and creating digital products online, such as ebook and courses and whatnot. I have a Next.js course and an ebook if you want to check that out. And I also just create stuff to help you grow as a developer.
So one quick note, all snippets and slides that I use in this specific presentation are available at this link and here is a barcode for you as well, which is pretty handy.
So let's get started and talk about GraphQL State Management. So first let's take a step back and really understand what standards are we living up to, right? Like what is a State Manager? A State Manager is essentially a way that we can manage our overall application state. So whenever we are making a request to the server, the server is giving us back data and that data may or may not come in the right form as we like and this is why it's important that we use a State Manager to transform that data and access that data the way we like. So we can dispatch specific actions and then the state can change overall, right? And that's what we will take a look at now that we understand a little bit more about State Manager.
So what is a good State Manager? A good State Manager allows us to update the state easily. We can access state easily, right? And there are a bunch of different characteristics of a good State Manager that we can take a look at now. For example, your data is normalized. And what I mean by that is whenever you update data, whenever you try to update one thing, it's only going to update that one thing. Your data is not duplicated everywhere, so you are not going to have a bunch of different card queries, for example, or card data or product data or whatever data that you are using in multiple different places. You only have that one state piece of information and a good State Manager really allows you to do that. Now data transforms easily. So whenever, as I said, if whatever data you're getting back from the server, if we are able to transform that data consistently, then, for example, if you want to show a user's name, right? As simple as that, a full name, we can display that on the homepage, on the login page, and wherever else we want, but we are able to transform it to however we like, right? So we also want our data to transform consistently. You can visualize your global state. So as we all know, debugging is extremely important. So in order to help with debugging, we also need to be able to visualize global state. If we don't have tools to do that, then we won't be able to visualize it. We won't know how our data looks like. So we are kind of shooting in the dark, which is not what we want. And as I mentioned, debugging is a lot easier. So you can debug your applications very easily by adding any sort of console logs or trying to see what is the before and after state. If something were to go wrong, you know exactly what to do and what specific code probably caused it.
2. Introduction to Redux State Management
Redux fixed majority of our problems by making state management and debugging easier. It made front-end development easier and data manipulation more straightforward. Although Redux may have a learning curve, it allows for efficient data transformation and manipulation.
So you know the flow of data really well. And as we all know, Redux, which is a very popular state management library, really fixed majority of our problems, right. The Redux came along wherein we were able to manage state debug easily with Redux dev tools really helped us along the way with the specific problem that we were having on the front end. In fact, I would also say that Redux made front-end development a lot easier because suddenly data manipulation was so much easier and straightforward. I would caveat that Redux might not be, if we want to get started with Redux, it's not as easy as we would think, but once you do get the hang of it, you would be able to transform data and basically manipulate your data in general, right, properly.
3. Redux Requests and Apollo GraphQL Hooks
In Redux, requests involve defining different states like cart request, cart success, and cart error. Redux Saga helps manage async side effects, allowing proper handling of asynchronous requests. With Apollo GraphQL, we only request and manipulate the specific data we need, making front-end development more efficient. Apollo GraphQL hooks, such as useQuery and useMutation, enable fetching and manipulating data with ease.
So let's take a look at what a request in Redux looks like. So in this case, we have a very simple example, right. We have a cart, we are sending a cart request, a cart success, and a cart error. So we have three different states here. So whenever we make any sort of request, right, in general, on our front end, we have three different types of actions that we need to dispatch, and that's how Redux essentially works.
So we have a cart request that we need to define, and then we have a cart success, once we do get data from the server, and then there's a cart error as well. So whenever we request for data for cart, it can either resolve in one of these states, it can resolve in a cart success, or it can resolve in a cart error. And we essentially mix these up and we have these three phases of an API request, right. And an async request in Redux will probably look something like this. Now this specific example does use Redux Saga, if you have not used that before, it's essentially a library that allows you to manage your async side effects.
So a side effect is anything that, you know, whenever your application changes, it's able to handle all the asynchronous requests properly. So for example, in this case, we are fetching cart data. And as you can see, you can think of yield as just fetch, right? And you are yield is like kind of like an iterator, which stops whenever the fetch request, which is call gets resolved, and get cart query is essentially a query on the back end, right? Now in this case, what we are noticing here is, one, what happens is we look at listening for, we review watch fetch cart data query, and take every, that means every time a cart request is made, we invoke the function fetch cart data. So you can see number one, listen for cart request, and number two fires fetch cart data function. So this is a request in Redux with Redux Saga. It's an async library that allows us to manage our asynchronous request. And whenever we are able to resolve it, if we have data, then we basically put, will allow you to dispatch an action, right? So then we can either say cart success, or in case of an error, cart error. So this was pretty straightforward. We knew exactly how to transform data. We knew exactly what to do in our front end application. So what changed? Right. Why switch? Well, Apollo, GraphQL happened, right? GraphQL took us for a ride, wherein we don't have to request all the possible data from the server and manipulate it. We only get what we want, right? We can ask for a specific object, and we get that object back. So let's take a look at what happened after Apollo GraphQL hooks came along.
So this is just an example of Apollo GraphQL hooks on your front end. So Apollo Client, which is essentially a client library for making GraphQL requests, gives us with different types of hooks such as useQuery, useMutation, where we are able to make requests. Either we can fetch data using useQuery or also useMutation if you want to manipulate data or update data, whatever. So for example, in this case, we have useQuery. And useQuery again calls getCartQuery and based on that we get different sorts of inputs. So like we get different outputs such as loading, error, data, and refetch states.
4. Apollo Client 3: State Management and Benefits
Apollo Client 3 does the heavy lifting for you. It allows you to focus on the front end of your application and do exactly what you want. With Apollo Client 3, data is normalized by default, eliminating duplicates. It keeps track of requests and allows for easy data manipulation. Understanding how Apollo Client works gives us a good idea of its benefits.
And isn't that what we want? This is exactly our frontend application. So we don't need actions anymore. We don't need loginRequests, loginSuccess, and loginFailure because we are able to just do that with hooks. And we can just say give me three different types of data. Data, success, and error, right? Which is exactly what we saw and also give us loading so we can show to the user some UI while our application is loading.
But there are still some challenges, right? Like it's great that we are able to do this but how do we transform data? How do we manage state? Let's say if we get a user object from the back end with name in it, how can we show names in two different ways on the homepage versus the login page, or however else we want to manipulate data in general? So how do we transform data, normalize data, and create a million actions and reducers. Sorry, I'm taking a dig on Redux, but how do we do all of that in GraphQL, right? And as we know that with Redux, this was a common pain point, right? So, every time you have to do even a small, you know, really small thing, you might have to create an action reducer, and yeah, as we all know that same code repeated over and over.
So GraphQL, Apollo Client 3 came along and said, challenge accepted. And we have Apollo Client 3 that essentially solved a lot of our problems, and we will take a look at that, how GraphQL allows us, with Apollo Client 3, to be a really good state manager. And Apollo Client 3 is here. It's magical. Let's take a look. Apollo Client 3 does the heavy lifting for you. So if you, for example, in Redux, we had to do everything, right? We had to request for data, we had to resolve data, we had to dispatch an action for error, we had to manage asynchronous requests and whatnot too, right? Apollo does the heavy lifting for you so you don't have to do a lot of it in the front end. So what is the difference between Redux and Apollo Client? Redux says, yeah, I'm okay, I can do it, I got this. And Apollo Client is like, No, you don't have to. I will take care of majority of the stuff for you. So you can do exactly what you... You can focus on the front end of your application and you can do exactly what you want. So again, let's revisit some of the state management principles that we took a look at initially. We said our data is normalized, right? We don't want any duplicates, for example. And with Apollo Client 3, it definitely allows us to do that, because our data is normalized by default. We don't... Our data is normalized and you can see over here in the root query. This is an example of how Apollo Client 3 would keep a... Keep track of your requests. So this is an example of a cart, and a cart specifically, let's say, has different products in it, right? And it will create an array. And each array is a reference to a specific object that we have invoked for. Again, we don't need to get into the nitty-gritty details of exactly how Apollo Client 3 works but you need to have a good understanding of what the internal workings are, so that you can manipulate data easily. And with Redux, there's also a concept of immutability that came along, which is that whenever you update state, there's always a new state that gets formed, right? With specifically Apollo Client, having that understanding of how it works would really give us a good idea.
5. Data Transformations in Apollo Client
Let's explore data transformations in Apollo Client. We can manipulate data by invoking queries and displaying specific information based on conditions. Apollo Client's cache allows us to add additional data to the frontend, similar to Redux's state manager. Local only fields enable resolving specific fields on the client side, providing flexibility in displaying data. In-memory cache in Apollo Client stores data and allows us to define how specific fields will resolve.
So data transformations was the second thing a good state manager has. So let's take a look at how we can do data transformations in Apollo Client. So this is again an example, right? On the right hand side, you will see a sample data.json, wherein we can again see products. We have Glossier, and like item name is lipstick, prize, and so on. It's a very typical product, eCommerce product, right? On the left hand side, we have getCartQuery, and on the bottom, we can invoke that query, right? So again, this is exactly how we would invoke the data. We would declare exactly what we want, and then invoke that query inside our react component using useQuery.
So how do we manipulate data then, right? Like how do we do more things with it, for example? So in this case, again, if you notice that we are manipulating data. So here is how we can manipulate data. So on the right hand side, again, it's the same product, right? But on the left hand side, we are invoking the query, and we already took a look at the query in the previous slide, but we're invoking the query, and here we're just displaying whether to display getOutOfStockText or low stock text. If the stock minus quantity is less than or equal to 2, then it's low stock. Otherwise, if it says it's out of stock, it's out of stock, right? Again, very simple example, but this is exactly how you can manipulate data because exactly you get data back from GraphQL. But you might imagine that data will get repeated, right? Let's say if you want to show that same product in different places, you want to make sure that we are storing out of stock message in all the different places as well.
So let's take a look at what localOnlyFields are introducing localOnlyFields. In this case, we can see that we have Apollo Client and Apollo Server, and in the cache itself, we can add more data. So whenever a GraphQL makes a query, or an Apollo client makes a query and we get data on the frontend, we can additionally add more data to that cache. All the data is stored in Apollo Client 3 in the form of cache, right? Similarly to Redux, all the data was stored inside a stateManager. So for example, if we were to refresh the page, the data is gone, we have to refresh all over again. Same thing over here too, it's cache, and we need to make sure that we are storing new data in that cache as well. So how can Apollo Client differentiate between server data and frontend data, right? This is exactly what we're going to take a look at. So you can see over here, stock text has this specific value next to it called atClient, and that allows us to specify that so that we can say that, resolve this specific field on the client side, right? And then whenever we want to resolve it, whenever there's an atClient next to it, we can just call the cache for it.
So let's take a look at an example, same example, again, same cart query, right? So what happens is at the bottom, we are invoking the query inside the React component. And we all know about that, we have reviewed that code already. But on the top, for stock text, Apollo client will resolve stock text on the client side, right? So again, getCartQuery is exactly the same, but in addition to stock text that we can just resolve on the client side, which is amazing. So local only fields allow us to do that. We can just declare as many local only fields as we like. Now if we can imagine, if that same card object is being displayed in different ways, we can access stock text as well in all the different places in our front end. Isn't that amazing? So again, if you want to add, define more client side fields in the GraphQL type itself, that doesn't need to come from the server, we can define local only fields so you can resolve state locally, right? On your front end.
Now this is an example, again, a core sample of what is in-memory cache and how we are declaring it. So again, that cache that I talked about on the front end, that is called in-memory cache. And we can declare a field called as stock text, if you want to now go ahead and resolve that stock text, right? Stock text, we are able to declare it with the at client derivative, but then in the code itself in-memory cache, we have to then say what that stock text will resolve to.
6. Manipulating Data with Apollo Client
We can manipulate data using type policies and reactive variables in Apollo Client. Reactive variables allow us to react to specific values and update components accordingly. By using Apollo Client's features, we can achieve a good state management system. It's important to keep an open mind and consider whether we really need to rely on Redux for state management when Apollo Client can handle the heavy lifting. All the snippets and slides from this presentation are available at a specific link, and you can also sign up for a newsletter to stay updated on front-end news.
And this is the function for that. Again, if you remember, we did see this code exactly like a few slides ago, wherein we were doing that on the front end. But now instead, we can directly do on the product. So there are type policies inside in-memory cache. And on the product type, we are adding a new field called as stock text. And that allows us to manipulate data, right? So now stock text is either going to be out of stock or low stock depending on a product.
Then came reactive variables. Again, these are my favorite variables. And I know we are seeing a lot of code in this specific presentation, but all the snippets are available at the link. I mentioned initially, I will also share that link later in this slide deck as well. All right. So in this case, again, reactive variables allow us to, again, react to specific action. So similarly, how we had in Redux, we had a request and a success and error, right? We had an action. So similarly, reactive variables react to specific values. In this case, again, we are saying is logged in, we get the token from the cookie, right? Then we declare a reactive var, and how we declare reactive var, is essentially using a make var function from Apollo Client. And then in that, we can store whatever function we want. And then at the bottom, again, on type policies, we can extend it and add isUserLoggedIn and declare that isLoggedInUserVarReactive variable. So again, this is another way of reacting it. And the best part is that on the front end then, we can say, whenever the user is logged in, right, we can use another hook called useReactiveHook inside our application and then say, either resolve it to app or login. And how we can update it is, again, we can directly access the var and update the component. And this reactive vars honestly are my favorite. And this really helps us piece all the things together that we talked about of a good state manager. We can say that data is transformed. We use local only fields, we use reactive variables, right? And I think one more important thing here is, we need to keep an open mind, right? So, I understand that we love Redux and Immutability is something that we live by because React works that way, right? We always need to update state all the time. But do we really need to, like if Apollo client is doing the heavy lifting for you, then we can just use that and then access, add reactive variables whenever we need that reactivity on the front end, and add local only fields whenever we need to, whenever we need to add that additional client values that transformation layer is that local only fields with type policies, right?
So, that's all for this specific presentation. Before you go again, I really want to say that all the snippets and slides are available at this specific link, and you can also use the barcode as well. Now, keep an open mind as a summary of this specific presentation, and if you want to check out any of the slides and snippets that I shared here, it would be really fun to check out this specific link. And lastly, to help you grow as a developer, I have been creating these weekly visual snippets as well as part of my newsletter, BlueDuty 2 GraphQL, React, Next.js. So again, if you want latest front-end news or food for thought or snack for thought, you can check out the front-end snacks newsletter that I shared. A lot of the snippets that I shared are talked about in that newsletter. You can sign up here, and again, here is the barcode as well. So this is pretty much the end. If you have any questions, comments, feedback, or catch me after on Twitter or anywhere else that you have questions for. And thank you so much for watching this video and listening to this talk. I really hope you have a wonderful day or night. All right, thank you.