useContext() + useReducer() = Magic?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi and welcome to this video you might have seen my first part where we built this tiny app or where we actually had this tiny app here and moved it from Redux to the react context API but we use classes and isn't using classes so 2018 well let's do it the 2019 way and let's convert this to use react hooks and functional components everywhere so for this we'll need to know what react hooks are obviously and I created a video and an article which you should definitely check out and to which you find links in the video description where you can learn the most important things about react hooks from scratch no prior knowledge about it needed so it you assume that you know what react hooks are for this video so how can we use hooks here then we got classes everywhere like here in the global State in the app component here and also in our card and products pages everywhere we got components time to change this now let's start in the App J's file and let's convert this component here this class-based component into a functional component and for this I'll create a constant and name it app which will be Errol function that receives props and that does not have a render method here but just a return statement that returns some JSX and converting this component here was fairly trivial because there's nothing in here which would make that complex we can now get rid of that component import up there now that was simple let's move on to the products J's file and here I'll do the same I'll name this products page and this will now be a functional component that simply gets some props as all functional components and that has no render method of course so let's get rid of this and with that changed we can get rid of that component import and yeah Dada's is already that is it nothing too fancy here I'm using shop context consumer in a way that worked in functional components all the time so even before react hooks were added and therefore no adjustments are required here now that differs in the cart JS file here we got component did mount though I'm only doing some logging here but more importantly we're getting access to the context with the static context type here and that is not supported and functional components so let me name this card page here and again it's a function that receives and that will have no render method and therefore gets rid let's get rid of render but the problem is component it mount and this static context type this will not work so here we need hooks and we actually need two hooks here the more important hook for the context which we are really using here is use context this gives us access to context in a functional component the less important hook which we need here because we're not really doing anything useful with it is use effect which allows us to replace component it mount now let's start with the context here we can create a new constant and name it context the name is up to you and then you call us context and as an argument you pass a reference to your context so shop context in this application here which we import from context shop context and in case you're wondering what the shop context is about and what does entire app is about of course watch part 1 of this video where we do that read acts to context API transformation so now we're getting access to context here we can therefore a console.log context here but not in component that mount but instead we can use use of fact here and use effect as you know takes a function as an argument which will execute right after every render cycle essentially now I don't want to lock the context as often I only want to log it when this component renders for the first time when it was mounted and for that you can pass that second argument to use effect which is an array of all the dependencies of this function and only if one of the dependencies changes this will rerun no obviously we got no dependencies in here right so this is an empty array and this is interpreted by react such that it will basically never rerun this function here it will only run it once when this component mounts with that we got access to context we hopefully lock that to the console now all the places where I access this context down there of course now have to be converted to just context without this because context is now just a Const in this function but with that we should have everything we need and if we now go to our app here and we go to the cart we see our context here we see what's in there and if I add something to your cart this still works even though I haven't converted everything to functional and Hookes based components but that's the great thing about react hooks you can just dump them into an existing project as long as you're using react 16.8 and it does matter if some of your components use classes and our use hooks and are functional they work seamlessly together but with that let's tackle our biggest functional a class-based component that is the global state where I do that entire state management with my context API in there I now want to have a functional component which receives props obviously and that means that we'll have problems with the state here because state management with this state property and with this said state is not supported in functional components it never was and it isn't with hooks now we can simply use the use state hook for that so we import use state instead of component and that starts here instead of having state as a property like this we can add a constant here and we'll use array D structuring in a second and we'll pass this object here to use state now the array destructuring here allows us to get access to the current state which is then updated whenever we change it and a function that allows us to update the state and we always get back these two elements here by use state we always get back an array with these two elements now the cool thing is we don't have to stick to one state we can actually have two or three or as many as you need States and here it would make sense to manage the card and the product separately because unlike this set state with class-based components when you update your state here with the help of use State react is not merging your update with the old state it will overwrite it and therefore if you have something which doesn't always update together like products and cart it makes sense to use different states so here I'll use another state which will just be an array for my cart and actually this products state here can therefore also just be the array doesn't have to be an object here I'll name these products and set products and here I'll name this cart and set cart because the second element always is that function that allows us to set a new state and both states here are simply arrays now now I actually don't even have to manage products here as a state because in this tiny application there is no logic that would change the products so therefore this doesn't even have to be state we can just have a products array here like this and this will do we don't have to use use State here for the card however we do because the card will change so let's also get rid of that render method down there and just return JSX and now how do we use our new card state then well first of all things like add product to cart can stay there we can defined functions and functions but we have to store them as functions either with the function keyword or here with cons or let so we create a variable or a constant that then holds this function so now this allows us to add functions in that functional component which is great but inside of these functions inside of add product cart and off remove product from cart we're still using the state incorrectly instead of this state cart we just have well cart right this year this constant which we use here or which we get here with array D structuring this is our cart and here this can all stay the way it is when we call set state and update the card however we want to call set card and pass our updated cart in there and that is all this should be all we need to do on this first add product to cart functions easy as that now for remove product from cart it's essentially the same instead of this state card we just copied a cart because there is that current hard array as set and updated by said card and by use state where we initialize it and then down there we change all kinds of stuff and in the end when we set state we set card 2d updated card again so really straightforward and simple now the products are just products now they're not even managed with use state but here it doesn't matter if it is a normal constant which is not generated by you state or if it was generated by use state we just used a constant here therefore it's the same for the card this is also a constant now it's this constant this here is the products constant and for the methods or the functions while we remove to this keyword and we just call add product to cart or remove product from cart here so these constant functions I defined up here and that is almost all instead of this props children it should just be props children and therefore now if we save this we should be able to still add stuff to the cart and do it there and also remove it from the cart this works just fine and it works as it did before but now with functional components and our hooks now we're not done yet though this is of course nice transformation just a quick check this here was a functional component so we're only using functional components now which is great but instead of using use state here which is perfectly fine in the global state we can also have a look at another hook provided by react and that would be the use reducer hook now the user reducer hook is also a hook related to state management but it allows us to create a more complex reducer function that receives the old state and an action and spits out a new state and in the end that allows us to manage this at product cart and remove product from card logic in a bit of a more elegant way and to outsource it away from our component here even if you want you for this I'll create a new file here which will name reducers j/s which is totally optional you don't have to create that new file here and in reducers j/s I want to basically have my my functions in there so I'll copy them in there though we'll have to adjust them of course because we're using things here which are only available in our global state component and not in every random file where we add them to now I want to use these functions here in a reducer function which I create so I'll treat yet another new function here and I'll name this or I'll export this as a default here export default shop reducer and that is a function in the end that will get a state and an action and then should return something it's actually export this as a named export to make this really clear so here I get a state and an action and this will be a function that we can later use with use reducer now I will first of all check which type of action we have so I expect the action to be a JavaScript object that has a type property that defines which action we're executing here and this again is basically logic you might know from redux where we also have actions with the type so here I switch on the action type and I check my different cases and let's say here I define my cases up there so our export constants and the first one could be add product which in the end just holds that string here as an identifier and then we have remove product here which holds this string as an identifier so we have these two constants here which we can use as action types and I'm just storing them in separate constants so that we can simply import and use them everywhere and we can't miss type on the string identifier and here I never check for add product and if that is the case I return a certain state if we have a number case namely remove product I will return yet another state and in a default case so that we have some our actions I simply return the state as we get it so untouched basically now an ad product I of course want to return the state as it is spit out by ad product here so I want to call that function here and that function requires a product as an input but this function will also require cart as an input the old cart so what I will do then there is I will return at product to cart and for the product that I need to pass and I expect that to be part of my action that action just needs to have a product because how could I add a product that I don't get so this is a reasonable requirement the cart is part of my existing state because this reducer will be used for managing the cart and therefore state will have a cart property let's say or the state itself is our cart that could also be a reasonable assumption so state is our car terrain we could go with that as well but to make this a bit clearer I'll say start has a cart property so state is an object which has a card property which in turn is an array so I've rested in to add product to cart and in add product to cart we're then duplicating our cart array here to not mutate the old array in the old state object we're then finding the item we want to update we're then doing all the updating logic and in the end here that's the important thing I will not call set cart or set timeout even here I will just return my updated cart here or to be precise since I returned at product to cart here and I need to return a new state here and we just the point that state should be an object that has a cart property what I actually return up here is not the updated cart but an object that has the cart property where I do actually get my updated card words to assign my updated card now if your state object has more data than just the cart then you maybe want to pass the state in to add product to cart so that here we get state instead of card so that where I want to use the cart I have to access state card like this and then down there I can copy all our state properties first and then only override the card property so that would be the right strategy if your state is an object with multiple properties and this reducer function only changes one of them so that you don't lose the others now we're returning this here in the case of add product to cart now when we're removing a product from cart I want to call remove product from card here and I need to pass in my product ID which I expect to get on my action let's say so action product ID could be what I pass on and I also pass on my state here as an extra argument because and remove product from cart I also expect to get that product ID and that state so that in there I can get my current cart with the same strategy as when we add an item to the cart then we do all the updating logic as before and as explained in part one of this a miniseries or off this to video series but then here at the bottom I'm also not setting anything instead I'm just returning a copy of the old state and I override the cart with the updated cart just like this and now with that we have our reducer function here which we export we're also exporting our actions up there and we can now use this here in global State and other parts of the application if we want to now let's use it here in global State we got our context here where I bind add product cart and remove product from cart and now I want to use my reducer function for this instead of using use state like this so let me comment this out I use userid user and I need to pass in my reducer function here and I will get something back for that now let's first of all import the reducer function so let's import something from and that would be the reducers file so from there I want to import my shop reducer and that shop reducer is the function I want to pass to use reducer here and what I get back is the state so the state managed by dad reducer we could also name this cart state if you want to remember I simply change the logic so that state here is actually an object with a card property and therefore we should use it as such here in global state and the second element we get here in this array returned by us reducer is a dispatch method which allows us to dispatch a new action object that reaches that reducer function and then hopefully triggers something there so that is that action that is received here by the reducer function we control what this is because we dispatch it now therefore in here in global state we can get rid of all their logic here in front of set timeout I will leave set timeout here though and the same for removing items because that is now handled in the reducer and now instead of setting the card here or setting some state directly I'll comment this out and instead now I will dispatch and now I need an action from a reducers file so we'll import both actions because eventually I will need both add product and remove product so these two action identifier and now we don't just dispatch add product instead our action has to be a JavaScript object because in our reducer function we are expecting that action has a type property and a product or in the case of removing a product ID property and hence in the global state whoops in the global state here I dispatch type add product that is our identifier we're looking for and then product which is the product I'm getting here and the same logic applies down there here I dispatch a JavaScript object with a type of remove product and here we need to forward our product ID so let's do that and now we just have to make sure we use the card state object here so in the place where you just used cart we use card state dot card now that is all and this should now use use reducer in conjunction with the context API so if we now go back hmm we get an error do you know why well we get an error because whilst everything I said is basically how it works we got one flaw in this application and that is well the moment where our app loads we have no initial State anymore right we have our product but that is not related to the card for the card here we have our reducer here and that essentially returns our default state initially and we have no default state here so this is just a undefined what we can do to fix this is in the place where we call use reducer you can pass in an initial state as a second argument and here this could be an object with the card key which is an empty array initially and now with that we ensure that this is not undefined but actually an object that fits our card state structure and therefore now this reloads and it works and if I go to products and they start adding items you see this updates correctly here we see our items here in the cart and we can also remove them from there with that slight delay added by set timeout and this is how you can use the context API with hooks with either use state as I did it in the first part of this video or with the help of use reducer to get even closer to that redux experience where you also add reducer functions of course and ultimately it's of course up to you which style you prefer the advantage of the reducer approach of course is that you can have more complex state changing logic that you outsource so that you don't bloat your component too much here but ultimately that is up to you of course the same restrictions and thoughts as I shared them at the end of the first video still apply so should you use context API over redux have a look at my first video and there at my summary at the end to hear my thoughts on that that does not change by the fact that you are now using hooks because the reasons I named there still are all there they're not eradicated by hooks I hope this was helpful and I hope I see you in our videos too by
Info
Channel: Academind
Views: 157,328
Rating: 4.9364529 out of 5
Keywords: useContext, useReducer, react hooks, react context, context api, react context api, context usereducer, context usecontext, context hooks, tutorial
Id: R_7XRX7nLsw
Channel Id: undefined
Length: 22min 49sec (1369 seconds)
Published: Thu Feb 21 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.