Learn React State In a Single Video (Context, Reducer, Dispatch, Memo)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey welcome back to the channel so who is this video for this video is for you if you've already been at least a little bit introduced to react so you know that react is a front-end library you know that it uses something called jsx which looks a lot like html you know that it lets us break down our code into separate components and you know that components have something called state and the updating state is what lets the app you know update in real time so you've at least heard of all of those big picture concepts before but the actual details and syntax and specifics of working with state maybe you could use some help there and not just how state works in really really simple examples but how state works when you need to share it across multiple components that are nested different layers deep so on and so forth now before we get started i want to share a little bit of my philosophy and that is that i think the hardest part of learning react is avoiding the pitfall of trying to learn 10 things at once so react starts out simple but as your app gets more complex and you start reaching for some of the power tools that react offers it's easy to get wrapped up in trying to do things the right way in the powerful way all at once what i mean is that when you reach for one power tool that react offers it probably has synergy with another power tool and then the combination of those two together probably synergizes really nicely with another power tool and before you know it you've got three or four chainsaws going full speed and it's loud confusing it's over engineered you're super confused and you forgot what you were even trying to build in the first place at that point you're just gonna give up and we don't want that to happen right so what are we gonna do in this video we're gonna start simple react is easy when you start with the basics and then we're only going to introduce one new problem and one solution to that problem at a time by the end of the video we'll be using all the cool power tools that react offers like context and reducer only you'll actually understand what's going on so i think the hour and a half time investment is worth actually understanding the tools in your toolbox let's get started okay so to get started if you like to actually follow along coding instead of just watching a video you can find a link to this github repo in the description for this video so what's in this repo well remember when i said that we're only going to introduce one new topic or one new problem or one new solution at a time well that's why i've created these different subfolders we're going to work through each one and learn one concept at a time before moving on so without further ado right now let's get started with this first folder called one starter so you can either just clone this repo or click on code click download zip either way if you get these files i have this folder on my desktop here's that first folder one starter just go ahead and open this up in vs code cool so here are the contents for that folder with a command line pointing towards that directory i want you to run npm install if you don't know what npm is or if you don't have node installed on your computer then this video is not for you that's okay you might want to just go watch a different video on you know getting node installed on your computer you would just visit the node website we're not going to cover that in this video cool let's keep moving forward so now that we've ran npm install we can just say npm start that's it and now it's going to watch us it's going to watch for when we make changes and automatically re-bundle up our react code so for example if you go into your src folder and jump into index.js around line number eight we see a headline where it says welcome to our app you could add a few exclamations here just as a test click save and now let's go preview that in the browser so in your o1 starter folder if you go into the dist folder just drag this index.html on top of your web browser window cool so here is our react application there we see the three exclamations if you see that that means your copy is working and now we can actually get to the whole point of this video which is learning about state we don't need to go out of our way to make things academic or hypothetical or theoretical we just need to bring this app to life because currently if you click on these buttons nothing happens so to be clear what is the goal of this app well in this input field right here you could say you know you want the text to be 30 pixels and then this text should change and it should appear as 30 pixels in font size and then in this field you could say green and then this text should be green right or you could click this button and clicking that button will automatically set this value to be 20 and this value to be pink so on and so forth now we're displaying those values here on this text itself but then we also want to display in the header you know it should say the current size is 20 or 30 and then the current color is blue or pink and then for the final detail the header also says this page has been liked you know x number of times in the footer you can click this button to increment that value and then also in the footer there's a button you can click to set the font size to 30 but it does not change the color value just changes the font size value cool so we've established our goal what we're trying to build now the question is where do we start let's begin by giving our app initial or default values for these dynamic pieces of data so for example when you first visit the app let's say that the default font size is 15 pixels and let's say the default color is green now obviously you could just set those values in html or css without any javascript whatsoever but we're building my cat just attacked me we're building a real-time react application so we want to set those values in a way so that if we update them in the future our app just automatically re-renders based on those new values so how do we accomplish that well that's exactly what state is for so let's just go create a piece of state we can start with the size of the font so back in our code around line number four here is our overall app component so within the body of this function we would just say const we're making up a variable square brackets because we're actually going to destructure multiple values from an array don't worry this will make sense in about 20 seconds from now but in these square brackets we can make up a variable name i'll make up a name of size there's nothing special about this this could be unicorn or pizza but i'll say size comma set size now we're going to set that to equal and then we're going to call react.use state in these parentheses we can provide a default value so let's just say you know 15 is the default font size now when we call reacts use state what is it going to return what is it going to give us it gives us an array with two different values in it so that's why we're destructuring it the first thing it gives us is the actual value right so in this case it would start out as 15 and then the second thing it gives us is a function we can call to update or change the value if we want to in the future now just to reduce a little bit of typing you don't have to spell out react dot use state instead you can create sort of a shortcut for yourselves up here when we're importing react we could say comma curly brackets use state and now we don't need to type out react dot we can just access use state directly now cool let's do the same thing now for the font color so on a new line i just say cons square brackets maybe color comma set color equals use state let's give it a default value of green okay now let's actually use those dynamic values in our header so when we say you know the current size is blank instead of blank it would be curly brackets for something dynamic and then size right that's what this first property is do the same thing for color so this would be curly brackets color we can give that a save if we refresh in the browser perfect so the current size is 15 green let's also go use those values on this area so that it's actually green and 15 pixel font size so back in our code we're looking for this div around line number 24 that has a class name of main area so after the quotes but still on this opening div tag i would just say style equals curly brackets in these curly brackets we can include another pair of curly brackets and now we can just say color colon right so the name of the css property and then we just happen to name it color so color color in modern javascript if the name of your property is the same name as your value variable you can just say color once comma let's set the font size so it would be font size right this is the jsx equivalent of the css property font dash size colon and for example you would set this to a number and then px right you could use rem but in this example we're using pixels well instead of quotes if we use back ticks then we can just hollow out this hard coded number with a dollar sign curly brackets to do something dynamic and then you can just say size so if we give that a save and refresh cool this text is now green and it's the 15 pixel default to test it out up at the top you could adjust these default values you could bump this up to 25 and say that the default color is sky blue give that a save refresh you get the idea so we've learned how to use the current state value but now the question is how do we change or update the state values to answer that let's first set up these input fields so that they show the current value and then we'll work on setting things up so that anytime you change the value of these input fields we actually update our pieces of state so back in our code around line number 20 we see those two input fields so on the first input field i would just say value equals and now if you're wondering when you should use quotes versus when you should use curly brackets it's simple if you're just giving it a simple string value you would use quotes but if you want to use a variable or like an actual dynamic javascript value that's when you use curly brackets so we would want this to be our piece of state so a variable i'm going to use curly brackets so that would just be size and then on this input value equals curly brackets color save that refresh perfect now let's set up on change event listeners or event handlers so that when you click in this field you can actually change the value because currently if you try to click into this field it won't even let you change the value this is because we've told it that its value should always be in sync with state so in order to change the input field now we would need to change the state value so let me show you how we can do that let's start with the size so on this input field we would give it a react property of on change equals curly brackets and now you give it a function that you would want to run anytime the value changes now to be clear in these curly brackets we're not calling or executing a function we are providing a function and then react will actually call or execute it at the appropriate moment in other words in order to change the state we know that react gave us this this function called set size so we know that in some way or another we want to call that to change the size so we might be tempted in these curly brackets to just say set size parentheses you know and give it a new value but that would execute immediately as soon as the browser interpreted this code instead we don't want to call the function we want to provide a function and then react we'll call it at the appropriate moment so you could type out an anonymous or arrow function here or you could create a function and then just point towards it so let me show you what i mean within our component function maybe right about here we could say function and you could give it any name maybe handle size change parentheses curly brackets right and then you would just point towards that function so on change in the curly brackets handle size change so react is going to call or execute our function at the perfect moment now in these parentheses let's have an incoming parameter you can name it anything i'll name it e now in the body of our function this is where we would want to call set size so we'd say set size we wouldn't want to just hard code it to 40 or 50 pixels we'd want whatever value the user has actually typed into this input field so that's why we included this incoming event parameter so you can just look inside that so e dot target so that's the html element that triggered this event and then you can look inside that for its value so e.target.value so altogether if we save that and refresh and now if i change this to be 30 pixels or 40 pixels you get the idea so in real time now this is pretty cool it's updating not only the text itself but our header is also getting updated so that's the whole idea of state right you update a little piece of data and your entire application reacts to that change in real time let's do the same thing for the color so let's find the second input now you absolutely could do the same thing and go create a separate function but this time around let me show you how you could just write an anonymous or arrow function so on this second input you could say on change equals curly brackets and just have an arrow function so the one parameter would be the event arrow symbol and then in the body of the function we don't need curly brackets if we're just on one line with one statement so just set color parentheses give it e dot target dot value give that a save if we refresh i can go into this field and if i change it to green or yellow or orange or purple perfect it's updating both here and here practice makes perfect so now let's work on this button that you click that changes both properties at the same time right it sets the size to 20 and the color to pink in one click so to do that i would find that button here it is i would on the opening tag say on click equals curly brackets and let's point towards a function that we can create in about five seconds from now so i'll call it maybe our button handler and then let's just go create a function with this name so right below our handle size change i'd say function hour button handler in the body of our function i would just say set size 20 set color pink now you don't need to worry this is not going to cause multiple re-renders react is smart enough that inside of an event handler if you call two different set state functions it will batch them it will perform all of them in one re-render cycle so this is perfectly efficient nothing to worry about we can go ahead and save that if i click the button cool we changed the size and the color at once let's move on to the footer so you click this button and it changes just the size to 30 pixels but it leaves the color alone so down in the footer for me that's around line number 36 here we see that button i was just on the opening tag say on click equals curly brackets let's have an arrow function so parentheses arrow symbol and then just say set size to 30. give that a save we can reload if i click that button perfect finally for our last detail in this initial project or folder let's work on this like count incrementer so up in the header it says this page has been liked blank number of times down in the footer you can click this button and that number should go up by one each time you click the button so to set that up i would just create another piece of state so up at the top underneath size and color i'd say const square brackets maybe call it like count comma set like count equals use state you could give it a default value of zero okay then down in our footer here's that button like the page on the opening tag say on click equals curly brackets let's give it an arrow function so parentheses arrow symbol this would be set like count now if you knew the exact number that you wanted to set here you could just feed this a number but because we want to base it on the previous value right whether that was 0 or 100 we just want to take the previous value and add 1 to it so when you're using one of these set state functions that react gives to us you can give it a value or you can give it a function to access the previous value so let's give it an arrow function with exactly one parameter and that one parameter is the previous value so that that way we can actually work with it so arrow symbol right now we're in the body of our function but we can access this so then it would just be that plus one we can give that a save if we refresh let me zoom out a little bit if i start clicking this button whoops i forgot to update our header so that this is actually pulling from state so back in our code up at the top in our header around line 26 this page has been liked instead of hard coding that to zero we would just say curly brackets like count if i give that a save try it again now when i click this button perfect you can see this number gets incremented each click okay so that does conclude our first example in this video before we move on to the next example if i move too fast and you're lost that's okay that's the entire point of this o2 folder so it doesn't contain anything new it's just the finished product of everything that we just typed in the last 10 minutes so the o2 folder is a working example that you can reference so if things are cloudy and confusing right now i'd encourage you to pause the video experiment with things in the first project until they work and make sense you can use this folder as a reference if you need to you can always watch this first portion of the video again a second time until the idea of setting state and updating state makes sense okay but once that concept does make sense we're ready to move on to the next example so what topic are we going to cover in this o3 folder well there's one huge problem with the app that we just built what is that problem well if we look at our code the problem is that our entire application is just one big single giant component now yes our app was so simple that it's still only 50 lines of code but the point is you could use your imagination in the real world your app might be complex and it might be hundreds or thousands of lines of code and the idea is that you would want to split it up into bite-size chunks or you'd want to split it up into multiple components instead of just the one overall app component containing everything itself so for example you know this area could be its own totally separate component called sidebar and this area could be its own totally separate component called main area this could be its own component called footer so on and so forth now for the next 20 or so seconds you do not need to follow along with the code i'm about to type but for example i'll show you how you could set up the main area as its own component so down in our code again you don't need to make these changes in the next 20 seconds but i could delete this div that has a class of main area and instead point towards an imaginary component that i haven't actually created yet called main area sort of a self-closing html looking tag like this and then in react all that a component is is just a function that starts with an uppercase letter so down below i could just say function main area parentheses create brackets you just have it return a bit of jsx so i could just return a div that says hello this is the main area but the idea is that all of the actual code maybe if it was hundreds or thousands of lines of code that could live in this own totally separate component right so if i save this and refresh it still works right our overall app component is taking care of rendering that totally separate component called main area so this approach is great it lets you break up your app into reusable bite-sized areas helps us stay organized different team members can be responsible for different components but there's just one big problem well it's not a problem it's just an obstacle in our learning our understanding and that is that now since we're using separate components working with state just got way more complicated because now within this main area within that separate component by default before we take matters into our own hands we have no way of accessing the state data of the font size and font color this is because in react a component cannot access state that was born in a different component in another component a component until we take matters into our own hands can only access its own state that originated inside of it this means that in order for our main area to actually access those values of you know color equals green the size equals 15. we are going to need to manually and explicitly pass those values from our overall app component down into our main area separate component now doing that is not rocket science and it's exactly what we're going to learn how to do in our next example so back to the question of what in the world is going on in this o3 folder well i've already taken the time to split our different areas in our app into separate component files much like you'd find in the real world so right now i want you to change gears with me in your command line you can stop the task that's currently running press ctrl c you can close this instance of vs code and i want you to open up this 03 folder in vs code instead cool so with the 03 folder open in the command line pointing towards that new folder you can just say npm install and then go ahead and run npm start cool so now you'd need to open up that file in your browser so basically go into o3 folder go into the disk folder and just drag the index.html on top of your browser cool now we're back to square one none of the buttons work only now we've set things up in a way where there's actually individual separate components so let me show you what's going on if we open up the src folder you'll see there's a new folder named components and it has footer main area and sidebar so for example if you click on sidebar there is the html or jsx for that component right it's just a javascript file that's importing react down at the bottom we're just exporting our function right that's all a component is it's just a function that returns jsx in our overall file so in index.js in the source folder we still have our app function right the overall component that's getting rendered and then if you look in it's jsx yes here's the code for the header but then you'll notice down below everything else is split up into its own little bite size component if we scroll up just a little bit you'll see that i left these lines intact right so the state for size color and light count still lives within the app component and because i didn't split out the header to live in its own separate component that means it's very easy to access the state from this location right so we could hollow this out the current size is curly brackets size and the current color is hollow that out color and the light count instead of zero would be you know like count so if you save that and refresh that's easy enough right now our header is actually using those values that was super easy because we're in the same component where that state was born where it was originated from now the question becomes you know inside our separate main area component right this area how do we access those state values well there are multiple ways of achieving that but let's start with the most basic way so in our index.js file in our overall template right when we're including the main area we can just give it a prop so for example we could say you know color equals blue and size equals let's say 20. only we wouldn't hard code these values like this we could just pass it we could just give it the actual state values so it would be you know color equals curly brackets or piece of state named color size equals curly brackets our piece of state named size so let's save this and then we just need to go into our main area component and use these props these properties so in the components folder jump into mainarea.js before we change this code at all you want to be sure that on the opening function line in these parentheses let's have a parameter you can name it anything but the industry standard is to name it props now in the body of our function we can access that so on the opening div tag we would just say style equals curly brackets this outer pair of curly brackets is to do something dynamic we want another pair of curly brackets inside that for an object with multiple properties so color the css property would just be props dot color right those incoming properties comma we can have another property called font size colon you know and then string of text 13 pixels but we would want it to be dynamic so instead of quotes i'd make these back ticks hollow out the 13 with dollar sign curly brackets and this would just be props dot size let's give that a save if we refresh awesome let's work on making the sidebar component come to life it's going to be a little bit more work because it doesn't just need to read the incoming state it needs to be able to change and update the state so we're going to have to pass more things into it let's start with populating these input fields with the initial values from state so back in index.js here's our sidebar component i would just give it a prop of you know color equals curly brackets color size equals curly bracket size give that a save then jump into the sidebar component file when we're defining the function in these parentheses be sure to give it a parameter props right react is going to pass that into our function as an argument now we can use it so on this first input i would just say value equals curly brackets props dot size and then on this one value equals for the brackets props dot color give that a save refresh perfect however now we need to set things up so that you can actually click into this input field and change it so back in index in addition to giving it the actual state values we want to give it the functions that let you change those pieces of state so set color equals curly brackets set color as you might have guessed set size equals set size give that a save jump back into sidebar set up this first one so we'd say on change equals curly brackets and remember we're not calling or executing a function here we're either providing a function or pointing towards a function that react will call for us at the right moment so i'm not going to call set size directly i'm going to first create an anonymous or arrow function and then call set well actually we look in props right props dot set size now in order to access the value that the user has typed into this input field we would need that event parameter so e here if you just have exactly one parameter you don't need the parentheses around it and then what would we set it to it would be e dot target dot value let's do the same thing for the second input to save some typing you can just select this on change equals curly brackets just copy and paste that here really the only thing you need to change would be instead of set size it would be set color let's give that a save and test it out so now if i set this to 10 or 20 30 perfect i can change this to orange green beautiful really quick let's make this button come to life once again so it should change both the size and color all at once so on the opening button tag i would say on click equals curly brackets let's give it an arrow function and i want to do two things inside the body of this function so two statements so i do actually need the curly brackets and if you're like me and you're using prettier in vs code i think it's going to drop down in these curly brackets for you so we might as well drop down and i would just say props dot set size set it to 20 and then we would also say props dot set color and set it to pink let's give that a save you can see prettier sort of changed the indentation and line breaks to look like this but if we refresh if i click this button perfect practice makes perfect so let's go ahead and set up the buttons in the footer the footer doesn't need to be able to read any of the state values it just needs the set state functions for set the font size and increment the like count so back in our index.js file here is the footer component call so we would just give it a prop of set size equals set size and then another prop of set like count equals set like count give that a save now let's jump into our footer component be sure to include a parameter of props cool now we can access those on this first button that says you know make the text 30 pixels but leave the color alone we would just say on click equals curly brackets give it an arrow function and then we would just look inside of props for set size set it to 30. okay and then on this increment like count button we would just say on click equals curly brackets give it an arrow function props dot set like count instead of giving it just a numerical value let's give it a function that way react will give us the previous value because we don't know what the previous value is but we just want to add one to it so we can give it an arrow function where there's exactly one parameter we can call it prev for previous value arrow symbol we would just return and if it's all on one line you don't need the word return right so just the previous value plus one if i give that a save test it out refresh click this button cool that bumps up to 30 click this button well actually let me zoom out so we can see yep you can see the light count is going up and that means we are done with this example and we can now move on to the next example if i moved too fast that's okay that's what this 04 separate files finished reference folder is for so there's nothing new in this o4 folder it's just the exact code that we just typed out over the last few minutes cool so what's next though where do we go from here well we're going to learn about something in react called context but before we even get into that let's first talk about a problem with the app that we just built right so we don't even know what context is yet but let's first talk about what problem it solves so the problem with what we just built if we jump into our index.js file well while we were just building this you might have thought to yourself hey manually passing down props like this doesn't feel very fun or efficient now sure it was manageable because our app was so simple but you can use your imagination and think that if our app was big and complex passing props around like this could get very tiresome for example our app only required us to pass down these props one level but imagine if for example our sidebar what if it was comprised of multiple sub components itself so you don't need to type this out but check out this example if i go into our sidebar.js file you know what if inside of our sidebar it was made up of separate components like sidebar area one you know and sidebar area two so what if for organizational purposes we split it up into these multiple sub components and then what if they needed to access the state of color and set color well you would have to manually pass that down another layer deep so you'd have to say you know set color equals curly brackets props dot set color and then what if this component was made up of multiple sub components itself and they needed the state you'd have to pass it down again so it's sort of like our overall app component is the grandparent and then our sidebar is the parent and then this component these two would be the children but then they could have grandchildren great-grandchildren great-great-grandchildren there's no limit to how many layers deep you want to go to keep your code organized you know with different components and if one of those deeply nested components needs a piece of state that originated way higher up in the family tree well it can get very annoying and very tiresome to pass that data down each step so let me get rid of these we don't need these the question becomes how can we solve this problem of state being difficult to share across multiple components that are all nested various layers deep well the solution to this problem is something called context now this o5 folder this is actually the finished working reference for context so we don't need to switch into it right now we can keep working in the vs code we already have open and we can just convert this to use context instead so right now let's just jump into our index.js file and big picture ask ourselves what in the world is context well if we look at our overall jsx template imagine if we had some sort of special wrapper component that we could wrap our jsx inside of and then magically any component that is nested inside of it it doesn't matter if it's directly just one layer nested inside of it or you know a great great great grandchild 20 layers nested deep inside of it any component that lives inside this magical wrapper component can access values that it's offering that is what context lets us set up and right now let me show you how we would do this so the first step would be to create a new context so maybe up at the top even above our app function right about here let's make up a name let's have it start with an uppercase letter so i'll make up a name of our context and just set it to equal react dot create context now if you wanted to you could create a shortcut for create context so that you don't have to type react dot so up on the very first line when we're importing react in these curly brackets you could just say you know comma create context and then you can just call that directly here cool so we have something called our context now how do we use it well check this out when we're returning our jsx let's wrap all of our jsx the entire template of jsx inside a wrapper component so right about here i would say our context dot uppercase provider now vs code is going to try to add the closing matching tag for that right here we don't want that we would want that to be at the very bottom right we want this to be the wrapper element or you know wrapper component so i would just take this closing tag just cut it and then include that down here at the very bottom cool so we have wrapped everything inside of our context.provider now check this out on this opening provider tag we can say value oops that should not be uppercase value equals quotes hello this could be anything it doesn't have to be a string of text it could be a number it could mean object we're just giving it a prop now i didn't make up this name of value this prop needs to be named value but the idea is that now any component that lives inside of this right and it doesn't matter if it's a direct child or a great great great great grandchild 20 layers deep it can super easily access this value so now instead of just a string of text that says hello let's have this value be something useful so for example down on our sidebar let's stop manually passing color equals color so let's get rid of that prop and as soon as i save this that's going to break our application right because now the sidebar if i refresh well the entire app isn't broken but you can see that input field no longer can receive that value so now that our sidebar is no longer accessing the color value let's adjust our context provider so that that's the value it's offering right the color piece of state so we could say value equals instead of quotes just value equals curly brackets color so now the question becomes within our sidebar.js file how do we access this well if our sidebar component if the function definition for this lived in this same file as index.js this would be super easy essentially in this component of sidebar we need to be able to access this object this variable right up at the top when we created the context we named it our context we need to be able to access this in order to use the context provider's value so again if the sidebar component function lived in this same file accessing this global variable would be a piece of cake but in the real world you usually split things up so each component is its own file so with that in mind i say that we just create a separate file where we create the context and then we can import that file in both our index and in sidebar so let me show you what i mean inside our source folder i would just create a new file and name it our oops our context dot js in this brand new empty file let's just import curly brackets create context from react then let's make up a name so const we'll call it our context equals create context and then that's what we want this file to export so we can import it from other files so we would just say export default our context give that file a save and now we can jump back into index.js and this line where we had const our context get rid of that and instead we can just say import our context from quotes dot slash our context so we're just pulling that in from a separate file and the real reason we did that is so now we can go into our sidebar file and also import that file so that way we're working with the same context right so we can say up at the top import our context from this time it would be quotes dot dot slash to go up a folder right up out of the components folder so back into the src folder and then we can pull in our context cool so now how do we leverage this in this file well check this out inside of our function but before the return line right about here we can just make up a name say const and you could make up any name you want pizza unicorn i'll name it color and say that it equals and then we use something called react dot use context in these parentheses you say which context you want to use and that would be our context right that's why we imported it here and this is going to give us whatever we spelled out in the value prop back in our jsx so that's why i named it color right because that's what we provided so now down on this second input field for the value instead of props.color you could literally just say you know whatever you named this variable here when we're consuming the context if i give that a save and if i refresh perfect you see that value actually appears here now the reason this is cool is because our sidebar could have been 20 layers nested deep inside of our overall app components it doesn't matter no matter how many layers deep you are leveraging and consuming context is always this simple now that we've seen context in action like this let's take things a step further so let's go back into index and in our jsx when we're using sidebar let's get rid of all of these props so just get rid of every single prop that we were passing into sidebar and let's instead adjust things so that sidebar can access all of those things through the power of context so in other words on our context provider here we wouldn't just want the value to be a singular property of color we instead want to provide multiple values now on the provider you can only have this one prop called value but what we can do is just in these curly brackets just provide another pair of curly brackets and just spell out an object with multiple properties now before we go on i do want to say that there is a caveat in react regarding spelling out an object literal like this when you're working with a context provider in certain situations this can cause unnecessary re-renders don't worry we'll talk about that a bit later in the video we will address it but for now don't worry about it so let's just spell out the object here so we know we would want color so you could say color they know the name of the property and then the value and that's our variable name but remember in modern javascript if the name of the property and the name of the variable value are the same you don't need to spell that out twice you can just say color so then we'd also want set color comma size comma set size and while we're at it why don't we also just include like count and comma set like count cool so we're passing in this object with everything that a component could need and our goal right now we can go ahead and save that is we just need to adjust our sidebar now to pull all of these values instead of trying to pull from props right because we're not manually passing any props so at the moment if you refresh in the browser i'm sure the sidebar component yep is totally broken so to fix this just jump into sidebar and now on this line where we're saying use context it doesn't make sense to name this variable color why don't we name it instead just state or you could name it anything you could name it tools or data but the idea is that it's going to be that object that we just spelled out with all the different properties so let's just go through and use that now so on this first input instead of this props dot size this would be state dot size and then instead of calling props.set size it would be state dot set size on this line instead of color that would be state dot color instead of props dot set color it would be state dot set color cool and then let's adjust the 20 pixel pink button so instead of props it would be state instead of props state let's give that a save if we refresh it's looking good let me zoom in i can click this button perfect i can change this orange yellow green change this 10 40. perfect now practice makes perfect so let's go into index.js and let's do the same thing for main area right the idea is that we don't need to manually pass props like this so we can get rid of that okay and then in our separate main area component file up at the top we can just import our context so import our contacts you could just copy and paste this from sidebar if you wanted to from quotes dot dot slash to go up a folder our context in our function let's actually just copy that line to save some typing so back in sidebar you would just reuse this line of code so copy and paste that and now we can access state so in our code instead of props.color this would be state.color and then this would be state.size and give it a save refresh cool still works perfectly let's do the same thing for footer so back in index.js let's get rid of these props on footer we can save that i would jump into main area copy this line so i don't have to retype it up at the top of footer just paste that in i would also recycle this line so i don't have to retype that just include that in our function here okay now we can use state so on this button instead of props it would be state dot set size and this would be state dot set light count save it refresh it click this button bumps up to 30 let me zoom out click the increment button perfect now that would conclude our chapter in this video on context but remember i did promise that i'd talk about the caveat with context in terms of the value you provide being an object instead of just a primitive value like a string of text or a number so let's talk about that caveat right now we can actually visit the official react documentation on the topic of context and if you perform a control f or command f on the page for caveat here we see the subheading caveats now what is reacts trying to warn us about right here essentially in this example they're just trying to let you know that on your context provider if the value that you're providing is an object right instead of just a simple primitive value well that can trigger unintentional re-renders in any components that are consuming that context now don't worry i don't expect that to make sense because in order for this explanation to make any sense you would already need to understand the way that react handles re-renders by default versus the way that it handles re-renders on memoized components now what i just said i don't expect that to make sense yet so hear me out what i'm trying to say is that in order to have this explanation make sense we need to go on maybe a 5 or 10 minute tangent but i think that tangent is going to be worth it because i want you to actually understand the tools that you're using so to have this topic of unintentional re-renders make sense we need to talk about how react re-renders things by default so let's get practical let's jump back into our code and i want you to go back into index.js and let's do something that has absolutely nothing to do with context so in our overall jsx template down below footer but still in that overall div let's just make up a new component called extra footer okay and then we don't need to create a separate file we can just create the function definition for extra footer right here right so just its own top level function would say function extra footer parentheses currently brackets let's just have it return a bit of jsx with a div and let's have a paragraph that says cat name colon let's just have a blank x placeholder do the same thing for you know dog name blank okay let's go ahead and save that and if we refresh cool down at the bottom cat named dog name now let's get on to the topic of re-renders in react so in this functional component before we return anything just at the start of our function i want you to say console.log and let's just have a string of text and say imagine this function is slow or expensive to run let's go ahead and save that refresh and open up your browser's console okay so we see that message in the console once because the page ran however if i do anything on the page if i click this button to make the text pink we see that message ran again and if i click the like increment button five times the message ran five additional times so that means our functional component is getting recalled or re-executed every single time the overall app component changes and this has nothing to do with context this is just how react handles things by default anytime a component re-renders so in this case i'm talking about our overall top level app component when its state changes it itself is going to re-render but in react that means any of its children components are also going to get re-rendered so because our extra footer component lives inside the overall app component anytime the app component changes our extra footer function is going to run again and again and again now before you start stressing out that this might sound inefficient let me calm your mind it's not actually re-rendering anything to the physical dom of the browser working with the browser's dom is super slow compared to just performing computations in memory so rest assured even though react is you know re-executing our component again and again and again it's not terrible for performance because it doesn't actually touch the browser's real dom unless it detects a difference between the virtual dom and the result of all of our components and the actual physical dom of the browser in other words every time react is re-running this function our function is returning the exact same result so that wouldn't cause a difference in the dom so nothing is actually getting physically re-rendered so we're not causing a slowdown so i guess the point i'm trying to make here is that when you hear the word render in react it can actually mean two different things so there's the traditional obvious meaning behind the word right when we think of render we think of the dom actually getting updated but then there's the react internal version of the word render which just means that a functional component is getting executed again and that's the type of accidental re-render that the context caveat is warning us about okay but if any time a parent component changes any and all children components are re-rendered like this anyways what is the caveat trying to warn us about well that brings us to the real heart of this tangent which is something called memoization what in the world is memoization well let's get into that so you might be wondering why does react internally re-render every component every time well one of the reasons why is that because for a lot of components we create the function that powers them so in this case you know this footer right here that this is going to be computationally cheaper or faster to just execute again and again than it would be to first check to see if anything new is being given to it so that it would have an actually different result however not all components are going to be super cheap and quick to run like this sometimes you will actually have a component that is doing something computationally heavy or that's going to return a huge huge amount of jsx and in that case you actually would want to avoid running that function if at all possible right you would only ever want to rerun that function if it was actually necessary if it was being given new incoming arguments so that it would actually return something new and different and giving react to behave that way for a specific component is actually pretty simple so maybe right above our function definition we can just say const and let's make up a name i'm going to call it memo memoized extra footer equals react dot memo in these parentheses you give it a regular component so extra footer so we essentially just created a different version of this component that now react knows that it should only actually re-execute it if it's being given new input so that it would return something new so then this is what we would use up in the jsx so instead of extra footer up here it would be memowized extra footer if we give that a save if i refresh yes we see that message once when the page first loads but now if i click any of these buttons again and again and again you see it's never getting called again unnecessarily now it's not like our component is frozen in time if you actually gave it a different prop right if it actually had different input or different values to work with so that it would actually return something different then it actually will get called again so if you gave it a prop that was going to change over time or better yet if it consumed a context provider so for example in our extra footer function if i say const and just make up a name example equals react.use context and then use our context right so now this component is consuming or it's subscribed to the context provider now if i save and refresh and if i click the increment button five times you can see our component actually did get called and re-ran five different times this makes sense it needs to be ran again to make sure that it has the newest and latest values that the context is providing okay so this situation does make sense of why it would need to update and at this point we are now finally in a position to understand the context caveat that the react documentation was trying to warn us about so let's go back into the code i'm going to stop consuming or you know i'll unsubscribe to the that context provider so now this component is back to not needing to re-render every single time cool however and i promise this is the final conclusion of this explanation remember in this new component we said cat name and then we have these placeholder values well what if this component wanted to consume a different context provider so imagine if in our overall jsx template what if there was an outer overall different context provider that offered the cat name and dog name so really quick i'll just create it in this file because we don't need to import it anywhere else but i'll just say const you know animal names context equals react dot create context or we actually already have a shortcut for that so just create context okay and then in the overall jsx if i add an outer wrapper called animal names context dot provider move the closing tag to actually be at the bottom okay and then for the value if i say value equals and now here we're finally getting to the caveat if i give it an object inside the curly brackets of you know let's say cat name is meows meowzalot comma dog name is barks a lot okay and then let's go subscribe or consume to this context provider within our new component so down at the bottom an extra footer if i say const names equals react dot use context animal names context right and then i could actually use those values here so instead of the x placeholder it would be names dot cat name and this would be names dot dog name if i save that and refresh cool we see those names down here meow's a lot in barks a lot however those values are never going to change right nothing is set up in a way so that these values of meows a lot and barks lot are pieces of state they're not subject to change so it really wouldn't make sense for this function to need to re-render again and again however unfortunately if i click the like button five times we can see that our extra footer function got recalled and re-executed five times so why why is that right you might be confused because we're giving it the exact same incoming values this isn't changing it's still the exact same properties and values but the reason it's re-running and this is the caveat that react was trying to warn us about is because think about where this line of code lives it lives in the app component and anytime the state of the app component changes so anytime we change the size the color the light count that's going to call this function again the overall app function so then this line of code is going to run again and it's going to see that we spelled out this object this object literal and react is going to consider this a new value right because we're creating this object each time this line of code runs now if what you were including here as your value is just a primitive value like a simple string of text or a number react can perform that comparison and realize that the value hasn't changed but this syntax of actually spelling out an object due to the way that react performs a shallow comparison it's just going to see this and assume hey this is a totally new object each and every time so that's what the react documentation is warning you about if you have a memoized component be careful right if you're using an object on the context value you might be needlessly re-executing that function again and again and again now an easy way to solve this would just be to move this object into state right because if something lives in state react is really good at remembering it so for example if i just took this object cut it into my clipboard and then said you know const names set names right if i just created a piece of state so use state the initial value is that object now react can hold on to this value in between re-renders and it will know that it hasn't actually changed so now if we just provide for the value names give it a save refresh we still see those values but now i can click this button as many times as i want our function knows that it doesn't need to rerun again right because it's not getting any new input that would lead to different output okay and that concludes our context example if you want a working reference that's what this o5 context folder is so there's nothing new in this folder it's just for your copy and paste or reference purposes anyways at this point we are ready to move on to our final example and topic which is something in react called reducer now before we worry about what a reducer is let's first discuss a potential problem with the way that our app is currently structured right in other words what problem does reducer solve so let's go look at our app i'm going to jump into index.js and notice that all of our state lives in our app component right our overall top level component this is where we have size color light count so they all live here and this is great it's easy to share them with children components however we might start to feel like it's a bit weird how our state is centrally located here in terms of where it's being created but in terms of where we're updating our state or in terms of where we're spelling out the logic and details of how and when it should or shouldn't be updated all of that logic is spread out across who knows how many different files right it's not centrally located so we've got you know our footer.js file and yes it doesn't contain any super complex business logic right it's just providing a text size value and then it's incrementing a number by one but still it has unique code for changing the state in a unique way and then you know our sidebar js file totally separate file it also has its own logic and details of how state should change now our app is really simple there's not a lot of complex logic and decision making going into how the state should change but you can use your imagination that in a big large complex project you're going to have tons of different component files and more advanced logic of how the state should be changing and maybe you don't want all of that to be spread out across so many different chaotic files maybe it would be nice if you could just have one centrally located place right so we have a centrally located place where we're creating the state maybe it would be nice if we could spell out all of the different ways that our state can change in one place right our logic our conditions our different actions we could wrap that up in a nice single package and then all of our different child components can just talk to that package and say hey i'm just a child component in a random file i don't know the details of how state should change i'm just giving you a request i'm dispatching a request to you i'm saying hey here's the new color value or here's the new font size value it's up to you to do something with it right i'm just brainlessly giving you this value you're the centralized logic tool that should actually take that value and update state in an organized way well that's exactly what a reducer is in react so right now let me show you how we can adjust our application to use a reducer so a reducer is all about working with state so in other words something in react called use reducer is an alternative to using use state so up at the very top of this file when we're importing react and you know we had previously used something called use state well let's import something else the ordering doesn't matter but maybe right before it import something called use reducer okay now let's go leverage use reducer so down in our app function i would say const square brackets and just like when you use use state use reducer is going to return an array with two items in it so we're destructuring that array let's make up variable names of state comma dispatch don't worry i'll explain why we're choosing these names in just a moment but let's say that that equals and then use reducer now in these parentheses we're going to give it two things so a comma b as placeholders this first argument that we give it so instead of this a placeholder is a function now the idea is that when react calls this function that we provide it it's going to give it everything that it should need to make its decisions so the function does not need to live inside of our component function because it doesn't need to directly access any of our data right so the reducer function is going to be given input and then it's just a pure function it's going to return output so let's create a function right above our app component so right about here the name doesn't matter but i'll name it maybe our reducer function parentheses curly brackets okay and then instead of the a placeholder that's what this would be just a reference towards that function so our reducer function the second argument instead of the b placeholder is your initial state so what should the values be by default so right below that new function we just created i would just make up a variable say const you can name it anything i'll name it initial state equals an object we didn't touch on this before so earlier when we were setting up state we set it up so that each piece of state was just one singular value right like size is just one single number color is just one single string of text you don't have to set things up that way use state can absolutely be used where the value is an object with multiple sub properties that absolutely can be done however even the official react documentation says that use reducer is usually preferable to use state when you have complex state logic that involves multiple sub values so in other words you don't have to use use reducer but if you want your state to sort of be lumped together in one unified object then use reducer sort of the right tool for the right job but it's totally up to you how you want to structure your data in your app anyways in our initial state so in this object i would just spell out the various properties so i would say you know the default value so size default value of 25 you know color default value of quotes sky blue comma likes default value of zero cool and then that's what this second placeholder value of b should be so it should be initial state so now the question is well what does user reducer give back to us when we call it well it gives us two things that's why we have an array here that we're destructuring the first thing is just state that's simple enough right so you could say something like state.size to get this value or state.color the second thing it gives us is dispatch what is dispatch well it's going to be a function that you can call and you can describe which type of action you're trying to dispatch and the idea is that we can pass this down into our various components for example our footer can say hey i'm calling dispatch and the type of action that i'm trying to dispatch is increment the like count so it just has to pass a label that says increment likes and then it will be up to our reducer function to see that label and go oh you're trying to increment the likes and then it will perform the actual code to make that change in the state if that doesn't make sense that's okay i think it truly won't make sense until we see an example up and running the real idea here is that we want to pass both of these things state and dispatch into our application like all of the sub components right these are super useful things it's not like we're just going to need to access them inside of app we need our children and grandchildren great grandchildren to be able to access these so this is where everything comes full circle let's just provide them with a context i'm actually going to set things up so that there's one context provider just for state and then a different context provider just for dispatch and at the end we'll see why i would do something like that anyways let's leave this our context provider in place and that can be responsible for offering up our state so for the value it would just be value equals we can get rid of all of this right we don't want two sets of curly brackets just one and just give it state okay but now let's set up a totally different context provider and pass down dispatch so to do that i would just create a new file well to save typing i would just duplicate this our context file so i would just take these three lines copy them create a new file in our source folder that lives right by our context and just name it dispatch context.js paste in your clipboard just change this from our context to be dispatch context save that if you really wanted to you could rename our context to be state context but it doesn't matter back in index let me go ahead and import that new file so import dispatch context from quotes slash dispatch context okay now let's go add it in our jsx so essentially i would just create one more wrapper provider so right about here i would say dispatch context dot provider move the closing tag to truly be down here okay back up on our new opening tag we give it a prop of value curly brackets dispatch cool so now our children and grandchildren and great grandchildren components it's up to them they can choose to consume or subscribe to either one of these the reason i wanted to split this up is because if we think of our footer for example right this area it doesn't need any of the state values it only needs to be able to dispatch actions when you click on these buttons but it's not actually reading or loading you know the size value or the color value we'll get more into that in just a moment and speaking of this footer let's go set it up first to use our new approach let's set up this increment like count button so let's jump into our footer.js file and up at the top we don't need to consume or subscribe the our context any longer let's instead pull in dispatch context from the file named dispatch context and then in our footer let's say const instead of state let's change this to be i'll name it dispatch i think that's a fitting name and then this would be dispatch context now let's go actually use dispatch so down on this bottom button when it gets clicked let's leave the arrow function but instead of calling you know state dot set light count and actually spelling out how the state should change instead of all of that so i'd get rid of from state to this closing parenthesis all we would just say is dispatch call it let's give it an object and let's say the type is increment likes and then let's also adjust this top button so this button when you click that it sets the font size to 30. so again let's leave the arrow function but get rid of state dot set size call parentheses instead we would just say dispatch give it an object type would be let's make up a name of change size comma let's give it a property of value should be 30. so the idea is we're not actually changing the state in these files we're just calling dispatch and then it's going to be up to our reducer to see these labels and these values and change the state appropriately let's go ahead and save this file and then jump back into index and now we just need to set things up so that our reducer function actually has logic in it so remember when we created that new empty function up here in the parentheses for it let's have two parameters state comma action now there's a million different ways you could set this up you could write a bunch of if statements but sort of the industry standard is to use a switch so we'd say switch parentheses curly rockets and then just spell out the different cases but first of all what are we switching based upon action.type so remember in that last file when we said you know type is increase like so type is change size well in this function we're just going to perform different actions we're going to change state in different ways depending on what type it is so for example let's spell out the case for when it's increment likes colon and we're just going to return what the new state should be so in other words how should the state change to represent this new action that just occurred so i would just spell out a new object right this is what our state initially looked like now it's important to point out that react isn't going to perform any sort of object merging for us so we need to spell out a new object in the same shape as our state because we can't just directly mutate the existing state we need to provide a new value and just give that to react so in other words in this new object that i'm spelling out in order to save a bunch of typing i'm going to use the spread syntax so dot dot dot state so that will sort of copy over or give us what the incoming previous state was and then i can just customize things from there so i'll say that likes should now be state dot likes plus one okay now let's spell out the case for when you want to change the font size so on a new line it would just be case quotes change size colon we would return an object we would return new state let's start with dot dot dot state and then the only property that needs to change is size and it should now be action dot value so remember when we called dispatch we gave it the type which was the label of the action you wanted to perform and then we gave it a value we said 30. so that's what we're setting it to while we're here we might as well just set up cases for change color so new line case for change color colon return an object dot dot dot state the color should now be action dot value and finally let's set up a case for when in one action you want to change both the color and the size so case for change color and size colon return curly brackets dot dot state color would now be action dot value dot color size would be action dot value psi i made some assumptions for this one so when we actually dispatch and use this and call this we'll just give it an object for value that has properties of color and size anyways the idea is that this reducer function it now has in one centrally located place the logic and you know the code that's actually changing the state in very specific ways now in a more complex app you could do whatever you needed to do in this function you could have all of that complexity tucked away in this one centrally located place let's give this a save and now we just need to adjust our application to use state and dispatch so for example if we scroll down in index to our overall jsx template let's adjust our header right so instead of just pulling size this would be state dot size and this would be state dot color this would be state dot like count while we're at it let's go ahead and delete these three lines we don't need these individual used state lines anymore right our reducer now holds that state so we can just absolutely get rid of these size color and light count lines i'm going to leave this names use state line in place because it was a totally separate example cool so we've adjusted our header let's save this and go adjust our sidebar so in our sidebar.js file it's going to need both our context which has the state and dispatch which has the dispatch so let's import dispatch context from dispatch context let's be sure to set that up right here so const dispatch equals react.use context in the parentheses just dispatch context cool so let's start with the values they don't even need to change we were already naming it state cool but we do need to change the function calls to now use our dispatch so let's start with this size so leave the arrow function in the e parameter but get rid of state dot set size in the parentheses instead it would be dispatch we'd call that we give it an object type would be change size comma and then value would just be e.target.value do the same thing for color so leave the arrow function get rid of state.setcolor dispatch give it an object type is change color comma value is e.target.value finally we have the button that changes both the size and the color at once keep the arrow function but get rid of these state dot calls instead we would just want to dispatch the type would be change color and size the value would be an object and it would have properties right so color would be a string of text of pink and size would be 20. let's save that and go adjust our main area so mainaria.js it doesn't need to import the dispatch right it's sort of a read-only component and we got lucky because we were already naming this state we actually don't need to change anything in this file if we go back to the browser and refresh i can click the 20 pixel pink button i can increment the likes oops that's actually not working and you can see the value isn't even being pulled up in our header so let's go back into our index.js file and this is actually a great example of why reducer is useful so i had said likes and in our initial state likes but remember throughout this entire video we were calling it like count instead of likes so in our initial state change this from likes to like count and then in our reducer for the case of increment likes this would be like count and state dot like count the reason this is good that we're using a reducer is because imagine if i made that same mistake spread out across you know 30 different files that we're all spelling out the actual code that changes the state but because we're using a reducer i can just fix my mistake in this one centrally located place and it's perfectly fixed the idea is you're sort of moving all of the complexity and detail into one repeatable centrally located piece of code so if we save that can refresh now i can increase the likes perfect i can also click this 30 pixel footer button awesome now for the one final example in this video before we really close things out i want to show you a real world example of why i set up different context providers for state and dispatch so let's revisit the memoized component idea so let's assume that this footer is also for some reason really expensive and slow to run so check this out if we go into footer you know and in this function we said console.log imagine our footer is slow and then down at the very bottom of this file if instead of just exporting footer we instead exported react.memo footer right a memoized version of this component well because we only consumed and subscribed to the dispatch context that means this function is not going to have to rerun every time state changes only when dispatch changes which dispatch doesn't really ever change so in other words if you have components that you really don't want re-rendering all the time it's a great idea to split your context providers into separate chunks so then you know the children and grandchildren can choose which ones to consume and subscribe to so now if we save and refresh we see this imagine our footer is slow we see that once but we are free to use the application and that function does not get called again and again and that does bring this final example to a close now i know we moved pretty quickly so if you want a copy and paste finished working product reference that's what this 06 reducer folder is for all right and before we bring this video to a close i do want to say that while we learned new ways of sharing state across an app please don't think that this means you have to lift all of your state up to your topmost components so what i mean is if a component has state that only it would need and it doesn't need to be shared anywhere else in your app then just simple local state for just that component is probably the cleanest solution so the idea is that not everything needs to be globally available right we don't want to be like someone who's in their first week of learning the program and so they make all of their variables global so that they're easy to access everywhere certain things need to be global yes sure but not everything an example that the official react website gives is you know whether the visitor wants the website to use a dark or light theme another thing to consider when using context is that it hurts the reusability and portability of a component because if a sub component relies on context that it's consuming now it can only be used inside of that context provider wrapper now in some situations that doesn't matter in other situations you are going to want your components to be super reusable and portable so i guess the moral of the story is context can be really useful but it's not the right tool for every situation so big picture all of the concepts that we learned in this video you don't need to use them in a 100 global fashion right so as you break your app up into different components a sub component can use one of these techniques so for example if your sidebar was super complex it could have its own reducer or context provider right so you can use these ways of sharing state on a smaller closer to home level not everything needs to be global right it's only when a piece of data needs to be shared across multiple components that are you know nested different layers deep that's when we quote lift state up to a more top level component if you enjoyed this video about react state you might also enjoy my 16 hour premium udemy course all about react so in this course we build a real world simple social network application we use react router there's a live chat feature so on and so forth so it's a bit more real world instead of just really focused on state and nothing but state in the description for this video you can find a link to this page that has heavily discounted coupons for all of my premium udemy courses thank you so much for watching i hope you feel like you learned something and stay tuned for more web development tutorials [Music]
Info
Channel: LearnWebCode
Views: 14,191
Rating: undefined out of 5
Keywords:
Id: LZxAnUjZxZU
Channel Id: undefined
Length: 87min 5sec (5225 seconds)
Published: Mon May 16 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.