 Okay. Good morning, everyone. My name is Rahul. And I work at Gojek. And here are the few things which I like to talk about and read about in my free time. And at my professional time as well. So if you want to talk about any of these things, please feel free to ping me after the talk. And I'll be happy to discuss about any of these things. Okay. Enough about me. Let's talk about the topic for today's presentation, which is elegant state management using context API and hooks. So in one of the projects which we started in Gojek, I was fortunate enough to join at the beginning of the project. And the one thing which we decided to try in this project was to try a different way of managing state in our application. So instead of using Redux, we thought of using context API plus hooks for managing state. And this talk is going to be focusing a lot on how to do that and the learnings from that, this kind of approach. So here's the outline. We're going to talk about why state management is required on the first place in react applications. And then an intro to Redux, the benefits and the problems which we face in Redux. And the concept of consumers, providers, and the use reducer hook. And then the benefits of this kind of approach if you compare it with Redux. And the controversial topic which is when to use what. Okay. Let's start with why state management is required at the first place in the application. So I think the three important things are there, why we need the state management. And three important things because I am an ex-machinesi. So the first thing is sharing data. So sharing data, React is all about components, right? So sharing data between multiple components. Now these multiple components can lie between different routes altogether or can be at the same hierarchy but at the very deep level of a hierarchy. So to solve basically the problem-drilling issue and to be able to manage your application in a better way, you need a separate entity for managing the state of your components. Second is caching of data. So if you store your application data into the local state of the component, the caching becomes a pain because whenever the component unmounts, the data is gone forever. And of course you can use local session for that. But again, let's say you want to preserve that data within the session and within that specific application lifecycle. So to make it easier for a user, for a developer, it's important to have a separate entity for state management which can help in caching the data, both at the local level and both at the session level. And third is separation of concerns. You want your layout logic to be different and then the application logic, the business logic. So and try to remember these three things because we're going to keep on talking about these things a lot, in the coming slides. Okay, so a brief overview of Redux. First of all, how many of us have used Redux in production? Please raise your hand. Oh, enough of us. So I can skip to this slide, right? Oh, I'll just explain a bit. So Redux, we can start with this part, the UI layer first. So what it says is that your UI is basically the React component in this case. For changing any data, first of all, let's talk about accessing data. Let's say you want to access a shared data in your React component. It comes from something called a state which is kept in a separate entity which is in the Redux store. So the store contains a state which and the data is passed from the state to the UI. And the UI, if the UI wants to change any data to the store, it triggers something called an action. Action is basically an object which contains two keys, type and the payload. And type is basically a hard string and the payload is what defines what change you want to make in that, using that action. And that object goes to the reducer. Reducer says it's a pure function which contains the logic of what has to be done in any of the action. And it has access to the data of the whole module-level state, I would say. And then you can, it makes it much more maintainable to have module-level reducers. And then each reducer contains the logic of its own module. And then the store consists of multiple reducers, multiple modules, I would say. And it contains the store, the state as well. The state is like a JSON object where your application components can subscribe to. So that was the basic overview of Redux. Now the benefits of Redux and first three things which are the first three points which I have already mentioned. Separation of concerns is very easy because it makes it much more maintainable because your logic is very, very separate from your components. And, in fact, you can use Redux without React also. You can use Redux in VUJS, AngularJS, or vanillaJS as well. So it's not dependent on the components at all. Sharing data, caching data is very much easier because again separate entity. And then time travel debugging. So this feature is very much unique to Redux. It's not there in any of the other state management utilities which I found so far, as much as I know. And this is possible because of the fact that whenever you want to make any change in the store, you'll turn a new object. And because of this fact, you will be able to travel back in time and see what was the state of the application, let's say, before one or two actions took place. You can skip actions, you can go back to three, go back three, four actions. And this is very, very useful for debugging your bugs and all. And structure way because so you have different entities, for example, reducers, actions, and then the middlewares who are supposed to maintain only one single part of the code base of the logic. And that makes it much more maintainable. And the problems with Redux. So this is, I've been working with Redux from last two to three years. And as for my experience, these are some of the things which I don't really like about Redux. First is what to store in Redux store. Share data or persist data. So this question, this kind of question is there always whenever you start writing a module. In your application, you have this question always which should the data of this module go inside the store or should it be inside the local state of the container component. Now, a lot of blogs are also there around it. People say that if your data is taking care of the logic of your application, then it should be in the local state. If your data is being shared across different modules, then it should be in the Redux. But at the same time, sometimes your UI logic is also complex enough that your UI is consisting of multiple different components. And you want to maintain that, you want to solve that problem issue. So what you end up doing is you might end up storing that part in Redux as well because you just wanted to solve the issue of problem. And spending this time before writing any module, I think it's kind of a waste of an effort. And I feel that this should be another way of handling this kind of question. And another thing which happens is that we store something in Redux and then realize later, then it shouldn't have been a part of Redux Store. And then we try to move it, but now it's too late. And now rewriting the logic of logic from Redux to... So moving the logic from Redux to your container component, it's not very easy because how Redux works is totally different than how React functions. And it basically means that you need to rewrite the whole logic again. And too much of a boilerplate code. So you have to, as Shivam mentioned in the previous talk, you have to write too much code to just do very small, small things like you want to integrate an API to fetch a list view. You don't want to write... You need to write your action creators, your thugs, you need to learn about middlewares, then maps it to props, and then stores it itself. And then too many things to read about. For a newcomer in the Redux world, let's say you have your team consists of full stack developers and some of them wants to contribute to Redux and to your front-end application. And it becomes very, very difficult for them to learn in the first place and start contributing because they have to read so many things. They have to learn the whole flow of data in Redux. And then these kind of buzz words that we have, middlewares, side effects, pure functions, and then action creators and maps it to props, dispatch to props. So they end up in this state. Why are you the way you are? Office reference, by the way. Now, enough about Redux. Let's talk about the main topic of this talk, which is context and hooks. So to talk about that, I like to explain a bit about the context providers and consumers. How many of us has used context providers in production applications? Okay. So I'll talk about that. Cool. So this is an example, very simple example of a theme provider. Before looking at the example, I would request you to imagine a provider that a provider is basically a React component which contains the state of your module. So you can think of it as a store, which is for one module. Okay. And then it can contain your methods to change the state as well, which is like what we have in dispatch and reducers. So a provider is basically a module which contains the data and the logic for changing, logic ready to one specific module. In this specific example, if you see the line number three, everyone can see the screen, right? It's visible, right? Yeah. So this is theme provider.js. This is the naming convention that we follow. And we are exporting two things from this provider. One is the context instance, which is created by using react.create context. And this is a named export. So keep a note of that. And by default, we export something called a theme provider, which is a React component, very basic component, which contains the current theme and a method to change the theme from light to dark. Now, if you look at the render method, at line number 14, you will see theme context provider and a value prop. This theme context is basically the same context that we defined in the line number three. And the value prop, in the value prop, we have defined all the objects or all the functions that we want to be visible to the consumers. So this is a provider. The consumers would need to access some APIs. And these are the logic, logical functions and the data as well. So this can contain anything at all. And then props or children. So this we'll talk about later. But this is a very common way of defining a provider. So this was context and provider. Now let's talk about the consumer of this provider that we just created. So for this example, let's look at the left part of the screen shot. This is theme button.js. If you see the line number three again, we are importing the theme context instance, not the provider yet, but only the instance which you just created. So if you want to see again, this is the instance that we just created, line number three. And we are importing that. And in this example, I'm using class-based components, not the functional components. We'll talk about that later. But to use that context in your consumer, or basically any react component, all you need to do is use this specific thing, static context type, and then pass the context instance over there. That's all. Now you will be able to access all the things which you defined in the value prop of your provider very easily here. So here you can see the example of accessing the theme, current theme in your theme button component. And that's one way. Second way is over here, which is kind of a render prop method. In this, you pass your function as a, your react, your children as a function. So this is a very known way of passing down, passing the child elements to the react component. And here also, if you see it's kind of straightforward, you, what you do is import again the theme context, and use theme context.consumer. And in the first, in this argument of the function, you'll be able to access all the things which you defined in the value of your provider. Now, I'll talk about another, the function way in the coming slides again. And one more thing which we need to note, which we need to keep note of it, is that we spoke about importing the theme context, right? But we did not yet use the theme provider which we just exported default. Now let's see how is it being used. So at the app level, what we did was we imported theme provider and wrapped our app inside that provider. This is very important because otherwise all your components would not be able to access whatever you defined in the provider. So it's very important but very easy to miss, so keep a note of it. Okay, now two most important hooks. So again, one more question, how many of us has used hooks in production? Not much. Cool. So hooks are basically, they give you a way to access your features of a class-based component inside a functional component. Features like accessing the state, creating the state inside your function component or reading the context or using a reducer even. So these kind of things are very easier. References are all using hooks. And they provide you a much more composable way and much more cleaner way of doing things and the code becomes much more smaller. But we're not going to talk more about that in detail, but the two important hooks that we're going to talk about is that these are used context and used reducer hook. Now this is the same example that we saw in the previous slide. You see the consumer in the left side and the theme provider on the right side. Exactly the same, nothing changed. Now what will change now? But just focus on the left part of the screenshot and see what happens. So this is the functional way of doing things using consumers and accessing the context. So again, this is the classical way of doing things, like classical way, and this is the functional way. So if you see the line number three, I would say import, we are still importing the theme context instance over here. But we are using something different here. We're using react.useContext and passing the theme context instance over there. And this is just one line. Please note that. And this makes it much more readable and much more cleaner way of accessing the data in your consumers. So here we are accessing the same things which are defined in the value of the provider. So this is a much more cleaner way of using a provider in your functional component. And another benefit is that you can use as much provider as you want. You can have another provider which is called permission provider if you want to access a permission. And if you want to access the user state you can access, do that easily while using user provider as well, user context as well. So this was the context API and consumers, providers, and use context hook. Now let's talk about use reducer hook, which is also very important hook, and which is kind of a main thing of this presentation. To explain use reducer hook, this is one of the views which we created in our application in Gojek. And if you see it's kind of a very simple order flow, you basically add the merchant whom you want to place the order for. And then you search here and then you click on next button. And then you click on next button and you have the product listing view where you can search for products and you can add or change the quantities as well for the products. And in the third part you can see the overview of the products that you selected and the merchant also that you selected. So if you see clearly we are sharing data between all the different steps in the journey. And at the last we are again showing the order details, we are showing the merchant and the product selected and the total amount as well. So we want to basically keep the logic out of this UI part of this component and this was a very good contender of using use reducer because you want product is something which is being accessed by different different steps. So you are accessing products in the second step, add product and then in the cart and in the details as well. So let's look at the code base of this specific view. So this is the cart provider which we created. And if you see the line 3 again, the cart context which is again using the creator context. And cart provider in this case it is a functional component because we want to use reducer hook. So use reducer hook and hooks, one more thing, hooks can only be used in functional components, they cannot be used in class based components unless you write some higher order component to do that. By default they cannot be accessed. So this is the way to access to create a reducer inside your functional component. So basically you destructure an array with state and dispatch. And in the use reducer you part the reducer that you just created. So we are going to talk about this reducer in the coming slide. And the initial state as well which is same as redux. And in the value if you see clearly we are defined state and dispatch. So these two things we have defined state and dispatch. So these two things we have defined state and dispatch. So these two things will be accessible to the consumers. So state and dispatch both of them will be accessible to the consumers. Now let's look at the reducer which we just created for this specific module. So first we have the initial state which is something which is shared between different steps of a view. And then selected products, the products which you add in the second step of the card. And then the current step. Again if you see clearly this is a UI kind of component. We are storing the current step as well inside the reducer. And promo if you add any promo code then store that information as well. And so this reducer is exactly same as redux. Nothing changed here at all. So you have a switch case condition and you are switching to different action types and have a logic of returning what data has to be changed. And since it's a reducer same as redux, we can actually use the approach which Shivam has just mentioned to combining reducers and these kind of things. If you want to separate out a shared logic between different reducers you can do so very easily. Now let's talk about the consumer of this provider. So card provider which we just created before that. One more thing which I just forgot again and which I just mentioned that it is very easy to forget which is where to use the provider which we just exported. Yeah. Where to use the provider which we just exported which is this one card provider. So here this is a tip which we use in our project. So the tip is that whenever you are using a provider make sure that you use it at a router level instead of using in a component hierarchy. So why is this approach important is that is it because all the providers are kept in a single place. That's one. Secondly, all the routes which are using a provider you can easily mention. For example this card provider, we want this to be accessible only for these four routes. So you can easily do that just by wrapping these four routes in a provider and the best part about this approach is that you can enable chunking very easily. So you can lazy load card provider whenever order route is mounted and you don't have to write much logic for it. So you can use react.lazy and suspense API as well which is present out of the box. And for so this is the route level shared provider which we just spoke about which is card provider and you will definitely have some app level shared providers as well. For example the permissions provider or user provider which contains the data which is shared between the different parts of the application. And to do that just move it outside the router and that's all you need. So all the in this case all the application components will be able to access your permissions and your users whatever you define. Now let's talk about the consumer of the provider which you just created which was the card provider. Let's talk about the consumer how you use the state and the dispatch in your consumer. So this is an example of add to card button and I have kind of oversimplified it but it's essentially what it does. So we are importing a state and dispatch from the card context that we just created which is exactly same like theme provider, theme context. And if you see on add to card press we are just dispatching an action. That's it nothing else. And this is an action creator which we have in Redux so you can there's no learning curve involved here at all. And which is very important. And for newcomers as well it's kind of straightforward. You dispatch an action and then it directly goes to the provider and then provider takes care of what has to be done for that specific action. In this case whenever you click on the button add to card it dispatches add to card, add new product action with the payload as product and that will have the logic of pushing that specific product, not pushing but returning a new array with the new product. Now this was pretty clear right so this makes the code much more simpler and much more easy to maintain. You don't have much thing to learn. Now let's take a step back and think why we used Redux in the first place. Three things again, sharing, caching separation of concerns. Fourth is the middleware. This is something which is also very much used in Redux and it's not yet there in the context and context provider and hooks pattern. So what middleware does is that it's a layer, it's a kind of entity between your reducer your action and the reducer. So the moment you dispatch an action before it goes to reducer it goes to the middleware first if you have defined any and this makes your code much more powerful because you can easily do things like managing permissions or managing API level interceptors or web socket connections by using this because middleware has access to all the store to everything which is there in the store and they have access to the reducer they can even stop the action to go to the reducer as well which is amazing I would say. And if you think your app is going to use middlewares a lot then unfortunately this approach is not for you yet. And the last point is ease of debugging. So as I mentioned before time trial debugging is something which is very unique to Redux and it's not yet possible in this kind of approach and I have seen a lot of open source projects around it they are working really hard on it and I think within few months or so we will have something, some output in this as well. Now enough about Redux again and let's talk about advantages of this kind of approach over Redux. So the first most important advantage according to me is having module level stores so you don't have to create one single store where you keep all your business logic again you have reducers to keep different logic but it's again one single store which makes chunking very much difficult in Redux. And over here the chunking doing chunking is very straight forward chunking is basically loading your JavaScript as per its required and it reduces the file size a lot which is very important considering how faster our libraries are going. And second point is very it's another important point I think which is using reducer only if you feel it's necessary. So this API enables us to use reducer only if you feel that it is necessary for your module. For small, small things like theme, managing theme, you're managing your permissions, you don't really need reducer, right? You can just write small, small functions and maintain the local state of the provider and then expose those values and the functions to the consumer. And this is very important. So let's say you started using the local state of the provider and the function and later you realize that the application logic is becoming complex. Then you can easily move that specific module from the local component state method to the use reducer method. And this is very powerful because it's not something which is very easy to do in Redux. It's also your API state the same so the consumers will still have access to the functions and the state. The only thing which changes is how they are implemented in the provider level. And thus is incremental and composite way of doing things. So this kind of way makes writing your logic much more incremental and more agile, I would say. And yeah, so everything is good, right? Now let's talk about disadvantages of using this kind of approach. No middleware support again. Limited server side rendering support. Because one of the good points of Redux is that you can actually render the initial state in the server and then tell your application to use this state instead of creating a new one. And that's something which is not really easy or not really possible using this kind of approach. So if you're using server side rendering then it's not for you. Now, too many render cycles. Now this is something which is again a controversial topic. To give you an example let's take a look at this provider. So since it's a component, whenever this provider re-renders or whenever the reconciliation happens of this provider the value prop will get changed, right? The reference of this will get changed. And all the consumers which are using this provider will get re-rendered. Now this is in our case it's not a big of a problem because we are using providers at the route level not at nested level. So whenever the route changes only then this will happen which is kind of intended. But this is something to look for and whenever when you're writing this provider at an nested level that's why I recommend it to use providers only at the route level. And to solve this issue as well you can use React.memo or pure component. But this is not something to be worried about and if your application is simple enough then I think she won't face this problem at all. I haven't faced it and we are in production. Let's go back. No time travel debugging as I mentioned before. Now the most controversial topic of this talk which is when to use what? Now again this is the current state of this API and it's again I'm sharing my thoughts if you think anything is wrong or anything can be improved then feel free to ping me and ping on Twitter and then whatever you want to discuss we can do it after the talk. So this is what I think. Use context API plus hooks when you don't need buildwares. Your application state management logic is simple enough and you don't need middlewares or interceptors to basically in this case also the data and the functions everything is managed by the React component because probably there is a component right but in Redux it's separate entity which is not a component so even your simple functions can access the Redux which is not really possible in this kind of approach. It's an easy and straightforward way to maintain the state if you want to solve the three basic things of state management which is sharing data, caching data and separation of concern then this is for you and if you're planning to migrate to Redux in future and start with this approach then I would say don't do that please because it's way more complicated than rewriting the whole code base. Example for this would be a simple e-commerce page like slip cart and even Airbnb I think and a simple CMS plus OMS basically an order management system or content management system use Redux when if you're already using it then keep using Redux it's a great library to maintain the state if you need middlewares, you need server-side rendering Redux is for you and if your data is coming from different places web sockets, not React components anything else whatever use Redux in that case and some examples are Zeppelin Slack, one more important thing is if your data is if your modules are talking to each other a lot if your different different reducers are data of different reducers kind of dependent on each other then also it makes sense to use Redux because of middleware again because sharing data between different modules and different reducers it's something which is very complicated to do in provider in this kind of approach which you just explained and Zeppelin Slack, Slides.com which I used for this presentation and Canva.com whatever you do please don't end up like this where you are loading a state management library for a very simple application something like Redux in a very simple application so your application shouldn't be like this just make sure and I've already spoken about these tips so one file exports both the context instance and the provider creating module-level context using use context instead of using the static context type in the class components and avoiding the bad parts of Redux do not try not to plan for future if you want to do that then this is for you and update your DevTools as well because the new DevTools has very good integration with React Hooks and the context you can easily change data and then it will be affected to all the consumers as well which is very nice not as good as the debugger Redux debugger but it's good and use reducer is something which is meant to replace use state when the logic gets complex so I talked about it already these are the references which I used for this specific talk and thank you so much this is my Twitter ID this is my portfolio