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