React Redux Performance Techniques and Optimizations

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
i've got our react redux blog project open here on the left and on the right i've got dev tools open and it's the react dev tools i used the profiler to record our application every time i clicked the thumbs up emoji for the first post and as you can see from the three screens that i have every time i click that emoji i re-rendered all 100 posts even though the emoji is just for the first post today we'll fix that and apply some other optimizations to our application [Music] hello and welcome i'm dave today we're going to apply optimization techniques to our react and redux toolkit blog project to increase the performance of the application i'll provide links to example source code and all resources in the description below please note this tutorial is not for absolute beginners in react it is for those that already know react and are beginners with redux and redux toolkit if you are a react beginner you should complete the react js for beginners full course on my channel or one like it at a minimum before attempting this redux series okay i've got visual studio code open here and we're at the edit post form of the starter code now you can download this starter code from the link in the description below or if you've been working through this series with me maybe you've already got it and that's what i want to address before we get started with anything else one thing from the previous tutorial in this series in the edit post form dot js and that is on the on author changed i don't believe in the previous tutorial i wrapped this value in number and it needs to be a number otherwise it saves a string and then the comparison doesn't work and you don't get the updated author now again this was already changed in the repository for the starting code so if you've just downloaded that you've already got this here now before i apply optimizations today i want to add a couple of quick features that will help me demonstrate these optimizations the first one is going to be in the users folder and we're going to create a users list so i'll start a new file and name this users with a capital u and then list with a capital l dot js nothing really new in the file so i'll just paste in the ingredients and we'll quickly cover this now i'll get rid of that extra line at the top but we're importing use selector as we have before and our select all users selector and then link from react router dom and then inside the component we are getting the users by calling the use selector here and passing in the select all users selector that we created in the user slice and then we render the users so inside of render users is an array full of these list items because we're calling map over all of the users and then each user gets their own link and then of course it links to an individual page for each user based on that user id and then you can see we just returned the rendered users here at the end of the component and so that's essentially the entire users list component now we need to create the user page for each user but before we do that we need to go to the user slice and create a select user by id selector so let's scroll down and once we get down to the bottom here just under select all users we'll add some space i'll paste this in just to go a little faster but it's really easy to create we call it select user by id it's the state and the user id and then we just call the user state here state dot users and then find on that and we return what matches the user.id to the user id that we pass in and of course we have export here so it's ready to go and we'll be able to import it into the user page that we create so now let's create this new component and we'll call it user not users but just user singular page with a capital p dot js this component doesn't have much new either so i'll paste this in now of course it's going to be able to display any given user that has their data passed in so at the top we have use selector and then we're importing that select user by id that we just created in the user slice we're also importing select all posts and then from react router we're importing link and the use params hook so we're going to get that user id out of the url and that's what we're doing right here as we call use params we're grabbing that user id now note that will be a string as it comes from the url so we need to wrap it in number once again when we pass it into a selector and that's what we're doing here to get the user we're calling use selector and of course inside that it receives the state and then we use that selector we created select user by id which receives the state and the user id that must be wrapped as a number and then we have posts for user because what we want to do is not only get the user we want to display a list of all of their posts so once again we use use selector we pass in the state and then we use select all posts inside of the use selector to define all posts and then we call filter on all posts and return the result where the post dot user id matches the user id so we get all of the posts that belong to any given user and then we list those out and now we link the titles of those posts to a specific post id page which we've already created in a previous tutorial so we can go to a specific post for any given post they have their own page already and then of course down here we're just displaying the name in an h2 and all of those post titles with their links and that is the end of the user page component all right with those components created and the selector created in the user slice we're ready to go to app js now just under the import of the edit post form we're going to import user list and we're going to import user page after that we need to scroll down and this will look a lot like the path to the post information here where we have route path equals post but this will be for user and i'll paste this in i'll save to get some better formatting and we can see we've got our path user then there's an index route and it's going to display the user list component and then if the route has an id a user id at the end of it then it's going to display the user page component and while we're in the app.js let's scroll up i need to import navigate from react router dom and we're going to add a catch-all page now you could create a 404 and display it but what i'm going to do is just put essentially a redirect here with navigate and we'll navigate back to the home page if any page doesn't exist but as i note here in a comment you could replace with a 404 if you want to and this is a catch-all it's got the asterisk here which is going to select any route that makes it past these other routes so if it doesn't match any of these then it will be grabbed here and then it's just going to redirect to the home page with navigate notice i've got the replace flag here also and that's going to replace the bad request whatever that address or page was that didn't exist it's going to replace that in history with the good address that we're sending the user to now we're almost finished with this and ready to test it out but first let's go to the components directory go to the header and here after the post link i'm going to press shift alt and the down arrow to copy that line down and then we'll just change this to user but then here instead of post i'm going to say users because it's actually going to display a user's list so i'll save that change as well and now we're ready to test it out so i'm going to press control in the backtick type npm start to start up our app and then drag visual studio code to the left click on chrome so we can see what comes up and we'll see if we have any errors or if everything is working as expected and here's our blog it appears everything is working as expected we see users up here in the top right so i will click on that and now we have our users page and we have our list of users so i'll just click on the first user and now we have leanne graham from the data that we're getting from jsonplaceholder which is the data source for this blog that we're making an example of and then we have tin post and of course this is lorem epsom text here but these different 10 titles of posts and we can click one and go to any specific post as well so it all worked out like we expected it to let's click and go back to users and everything looks pretty good here now we need to add one more feature before we can apply these optimizations that i would like to so i'm going to grab chrome drag it to the right grab visual studio code let it fill out the page close the terminal and head over to the posts slice and inside the post slice we're going to add a simple counter now it really has no purpose in the blog but it's going to be a nice review for implementing some new state with redux and it's also going to help me demonstrate some optimizations so to do that we're going to start out here in our initial state and i'll put a comma and then i'll just put a count value of 0 to go along with the rest of the initial state next we're going to scroll down past all of our async thunks and get down to the create slice where we have our reducers now we're not using post added anymore ever since we applied our async thunks so i can really eliminate that and if we come down here although i'll just select it all and hit backspace so we still have our reaction added now underneath reaction added i'm going to go ahead and put in the other reducer that we have here and that is going to be increased count it's very basic it accepts state and action just like reaction added does but then we're just taking state count and we're adding one to it now again you can write code that looks like it mutates the data inside of create slice we can only do this inside of create slice and so that's what we're doing here just kind of like you see here with the reactions as well so you might see code that looks like it would mutate the data but immer js is running underneath the hood only inside of the create slice and that's why we can do it here and now we need to create another selector for our account so we'll scroll all the way down here to our selectors i'll click on the last one in that grouping where get post error is and we'll just change this then to get count and then it's going to be state dot posts dot count now action creator functions are automatically created when we put a reducer in the create slice portion of the slice so we remove that post added and so now we can add what we created which is called increase count so we need to export that action creator function now we can save our changes here in the slice and what we're going to do is go to the header component where we were just a little bit ago to add this users link and we're going to apply the counter to this component so underneath our import of link i'm just pasting in the other imports but we have use dispatch and use selector from react redux and then we have increased count and git count from the post slice so then we need to define our dispatch here so we'll say const dispatch equals use dispatch and after that we also need to get our count so there we're going to use the selector we imported and we'll use git count that we created and finally in the nav after the unordered list i'm just going to paste in a button when i save it formats better but this button has an on click and that's where we dispatch the increase count function and then we have the count state on the button so that's all there is to it really let's drag visual studio code back to the left and let's see what error we have it says git count is not found inside of the post slice so let's see what's going on with that i'll need the full screen to figure that out let's look at the post slice and let's see if we have git count oh i spelled it wrong i had an r at the end once i remove that r and save we should be good to go okay let's drag this back to the left it looks like it's working now and now everything seems to be working here's our button in the very top right and if i click it we should see it increment yep went right up to five after five clicks and now we're ready to make our first optimization and what you need here at least to record and see that you need to make optimizations is react dev tools now i'm going to open up dev tools by right clicking and choosing inspect you could also press control shift and the letter i but once you're in dev tools then you want to select the couple of greater than signs here that allow you to see more tabs essentially and once you have react dev tools which i'll link to in the description so if you don't have those you can get it for free at the chrome web store and it's an extension you install for chrome and i believe it's available for other browsers as well we're going to choose the profiler here and once we have the profiler it wants us to start a recording well i'm going to go to the page for leanne graham over here on the left so now we're at an individual user page and then i'm going to start recording our app and i'm going to go ahead and click the button five more times after i've done that i'm going to stop recording and now we can see the components here and we have five pages to arrow through and we're seeing what rendered so we can see this orange indicates the header rendered and that's correct because the state changed but notice what else happened the user page also rendered and that's kind of strange because the user page isn't related to the count at all the count's in the header and that state really has nothing to do with the user page other than they're both kept in the post slice so if we arrow through our pages here we'll see that the user page is rendering every time that the header was updated and we still see that and so that's kind of strange and really not what we want so why is that happening let's go back and take a look at our code once again i'll drag this over so it takes up the full page but we need to look at the code of the user page component so here inside of the user page component i'm going to scroll up until we see the posts for user definition and here we're using the use selector we pass in the state then we select all posts with the select all post selector and then we're returning all posts but it's filtered and this is where we run into a problem there's really nothing wrong with filter it just returns a new array every time and use selector will run every time an action is dispatched and so when we dispatch that increase count in the header then the use selector runs again and it forces a component to re-render if a new reference value is returned we're returning a new value every time with filter so that's why we're rendering the user page and we can fix all of this by creating a memoized selector so let's go back to the post slice and learn how to do this so back here in the post slice i'll find it in the list over here on the left i want to scroll to the top first and let's look at our imports here we have create slice nano id that we're no longer using so we can remove that we have create async thunk and after that i'm going to import create selector and we'll use that to create this memoize selector so let's scroll down now to our selectors all the way towards the bottom and we'll find here we have select post by id this is going to be just a little bit different this is going to be select post by user so when i save i think i'll get the formatting i want yes so here we have select posts by user and we're using our create selector create selector accepts one or more input functions and notice they're inside of brackets like an array that should be your first clue that these are dependencies actually the values returned from these functions are the dependencies and they provide the input parameters for the output function of our memoize selector so if the select all post value changes or this anonymous function which is taking state and user id and it's simply returning the user id we just need the user id so we have to write a simple function to do that because these are input functions but then if posts or the user id changes essentially that's the only time that we'll get something new from this selector that's the only time that it will rerun so it's memoized so with this added now to our slice let's go back to our user page and use this memoize selector so let's find the user page there it is now at the top we've already got select user by id this is going to be coming from the post slice as well and this is select post by user so i'll just put it after our select all posts and now that we've imported let's use it inside of the file so we'll come down here where we were previously calling filter and creating that new value and we'll just replace everything that we had for the post for user and now we'll use our new selector that we've created i'm going to press ctrl b so we can see everything but here we're using use selector once again passing in the state but now we're using this new memoized selector we created select posts by user id it gets the state once again we're making sure it's a number because we're getting that from use params where it's a string from the url and then we're passing that user id in so now that we've made these changes let's go ahead and drag visual studio code to the left let's go back let's clear out the changes here and let's see if we need to reload or make any other changes if we're getting everything we expect we are not getting our posts currently let's go back and quickly check our code there may be something wrong with that selector we created so i'll double check that that's back in the post slice as i'm looking through this yes yes there is a problem we've got post dot user i should have put post dot user id and that's what we need to check so once i make that change we go back and now we'll of course make the chrome browser fill up the page and yes now we've got all the posts here on the individual user page so we're once again ready to check so what i'm going to do is start recording again with react dev tools clicking the number button five times and stopping notice what happened now as we look on our first page even we can see that we've got it right we're rendering the header we're not rendering the user page anymore so on each of these attempts we did not re-render the user page and that's why i wanted to add a counter just to give something that was completely not a reference to what we had in the other component and really out of the way as well to just show how one can impact the other if you do not apply the optimization correctly so now that we have this memoized selector we're not re-rendering the user page every time we increase the count okay with this problem solved i'm going to clear out the profiler again let's go back to the home page and while we're here i'll start recording a profile again i'm just going to click thumbs up about three times again and stop the profile and let's see what we're getting so every time i clicked the thumbs up we got not only the post list but every individual post excerpt was also rendered so we've got 100 of those so one thumbs up meant re-rendering all of these and as i arrow through you'll see they re-rendered every time we added a reaction so we do need to fix that as well and there's a couple of ways to do it i'm going to drag chrome back over to the right and dev tools will fill up the screen i'm not dev tools vs code will fill up the screen again and now there is one quick way to do this that is definitely legitimate and there's nothing wrong with doing it this way however we're going to look at a full featured solution after this also so what we're going to do first is just say import react from react we don't usually have to do that but in this case we will we're going to change this const for post excerpt to a let and you'll quickly see why here because after that we're going to set post is a post or posts it's post with a plural s at the end excerpt and we'll set that equal to react dot memo and then we'll pass in posts excerpt and what that does really is it allows this component to not re-render if the prop that it receives has not changed so if the post does not change that's passed to post excerpt it will not re-render so this easily solves the immediate problem that we have so i'll just save that one change right there and now i'll drag this back over we'll go ahead and look at chrome in the full screen again i'll clear out our previous recording and we'll go back home and we'll start a recording and i'll click three more thumbs up and now we'll stop the recording and now look at our profile recording we've got the post list which is fine and that's expected but we're only rendering this first post excerpt and of course anything that's nested in it here's the time ago component which that's so small it doesn't really hurt a thing anyway but overall for this example we just wanted that one post excerpt to render and that's what happened let's look at the next page same result and the next page same result so now we eliminated that big list okay there's nothing wrong with our react.memo solution but i'm going to remove all of this and i'll show you why because we're going to talk about state normalization and just using normalized data overall which is recommended and then after this i've got this last line to remove here after this we'll quickly discuss all of the benefits okay normalized state structure is a recommended approach for storing items and normalization means no duplication of data and it also means keeping the items stored in a lookup table by item id so our normalized state shape is comprised of an object with an ids array and then a nested entities object that contains all of the items and the best part of using normalized data with redux toolkit is that redux toolkit offers a create entity adapter api and that will make your slices less complicated and easier to manage let's get started with normalize state back in the post slice file so we'll have to go back up to where the imports are and you can see i'm getting kind of a long line here i'll press ctrl b again for all the imports from redux js toolkit and so instead of that i'll just paste in the difference where i put them on each line you can see we've got the three we were already using but then i'm also adding the create entity adapter okay next under our post url i'm going to paste this in but it's a posts adapter that we're going to create and here we have the create entity adapter and we can put a sort compare function inside of the create entity adapter now this should look familiar because it's kind of like what we're using inside of the post list so if i open the file tree quickly look at the post list we were previously applying a sort that looked much like that right here inside the component so one thing i like about this already is we're abstracting this back to the slice instead of doing that logic in the component so now let's go back to the slice again and you can see that here where it does the sorting let's scroll just a little bit and now instead of just our initial state and i'm going to select down here through post as well we're going to have the post adapter dot get initial state for the initial state and notice i got rid of the empty array that was posts because our initial state will already even if we didn't put anything else in it it will already return that normalized object that i was talking about where you have an array of the item ids and then you have that entities object that will actually have all the items so that's there automatically and this is the extra state we're adding on top of that and now we may need to change some of our reducers and other functions the async thunks are just fine so let's scroll down and let's look at how we get the existing posts for our reaction added here we're using state.post.find and we'll be able to change that so now it's going to be dot entities and we're passing in that post id because we're using this as an object lookup so the entities is an object and then we're passing in the id to look up that specific post and now as we scroll down here into the builder cases there are several to look at and we're going to specifically be looking at the fulfilled cases so when we get down here instead of the state.post equaling state.post.concat and the loaded posts we're going to change this now because this post adapter has its own crud methods so let's look at the first one which is upsert mini and we just pass in the state and then the loaded post now let's scroll down to the add new post where we have the fulfilled here and once again instead of state.post.push i'll select that and this has its own method as well so it's post adapter dot add one and we pass in the state and the action payload which is the new post now we're looking at updatepost.fulfilled and as we get down here instead of setting the state.posts equal to this new array where we spread in the posts in the action payload we can use postadapter.upsert1 pass in the state and the action payload once again and finally for the delete post fulfilled instead of state.post equals posts we'll just highlight that part and the post adapter dot remove one pass in the state and the id so now you've noticed of course there's a line or two that we're not using we're not using this post line right here and actually if you didn't want to destructure the id you could say action.payload.id here and just have it right there instead of that line but i kind of like just having it down to the id myself also here in the update we no longer need to define posts as well because we are just getting it from the action payload there as well and here we're not using this id we destructured at all either so we can remove that i believe the rest is fine let me double check as i scroll up everything else yep looks good so with that we now need to scroll down to the selectors area i'm going to save the changes we've made so far and we'll be replacing some of these selectors because git selectors is another method we're going to call that automatically creates some selectors that we would typically need one of those would be the select all posts so we can remove this and the other one would be select post by id so we can remove that as well and then it's going to give us one new selector i'll paste this in and we'll take a look so here again git selectors creates these selectors then we're using es6 destructuring to give them aliases so they match up with our pre-existing code so get selectors create select all select by id and then select ids we're going to of course name them select all posts that we already had select post by id that we already had and then just to stick with that naming convention here we'll say select post ids notice we do need to pass in a simple state selector here and we can do that with an anonymous function so you do have to pass in a selector that returns the post slice of state right there so post adapter dot get selectors pass in a selector for the state but then it creates these three that we can export with these aliases that we are assigning using destructuring i'll save and i want to say we're finished but vs code is telling me there's an error up there so i really want to check and see what i did that might have created a problem and now i see the issue exports underlined here because we have our get initial state and then i just had a curly brace at the end so if we put a parenthesis around that should be good now that's quite a few changes to the post slice to put this create entity adapter into action however once you get the hang of applying this post adapter it sure made things a lot easier especially down here where we applied the crud methods it also made it very easy to get the existing post here for reaction added as we got it from the entities object lookup but then all of these different cred methods from upsert many and then we had add one and then upsert one and eventually remove one all of those make that pretty easy to work with now there are more methods and i'll make sure to link to those in the description or at least the documentation for those in the description and now we need to make a couple of changes in the post list component to take advantage of the changes we just made in the post slice so right up here at the top with the imports now instead of select all posts we're going to select the post ids so now we have select post ids and now we're going to change this post that we have with use selector all posts which we're no longer importing and we're going to define our ordered post ids right here using the use selector and pass in the select post ids and then in the succeeded block here of our conditional with the if and loading status and then succeeded and then finally failed we'll change what we have here because remember we're doing that sort compare function already inside the create entity adapter that's inside the post slice we no longer need to sort here and we'll set the content just a little bit differently because we're using the ordered post ids we'll map over that and we'll pass in post id so let's save these changes and now go to our post excerpt that we did have the react memo on but we'll just make a few changes here as well so now it's not just getting the post it's getting the post id and we also need to go ahead and import a couple of things so let's put the imports here we'll have the use selector and the select post by id selector that we've created and then inside the component we'll put those to work by calling well we'll have use selector here and then we'll call the select post by id when we pass in the state and the post id that we received as a prop after that we'll have our post defined and everything in the jsx should work as it was before okay that's a lot of changes but let's drag vs code back to the left and let's look at our page i'll clear out the profiler all together for now and let's just make sure everything's working first so let's view a post we've got a post here we can edit the post all of that looks good and of course here on the homepage we see all of the post excerpts as we expect so now let's see what happens when we record the profile and we click on thumbs up three times and now we'll stop the recording once again it only rendered that very first post excerpt so everything is working as expected with the normalized data we simplified the post slice and we were able to use those adapter crud methods and then it also created some selectors for us that we destructured and of course using aliases renamed what we already had in our code but we simplified our code we pulled more logic out of the components put it in the post slice and now this optimization stood the test of time here as far as rendering all of our post excerpts and only re-rendering the one that got the reaction so those are some good optimizations to add into your redux toolkit arsenal remember to keep striving for progress over perfection and a little progress every day will go a very long way please give this video a like if it's helped you and thank you for watching and subscribing you're helping my channel grow have a great day and let's write more code together very soon
Info
Channel: Dave Gray
Views: 33,356
Rating: undefined out of 5
Keywords: react redux performance, react redux perfomance, redux performance, react redux concepts, redux perfomance, react redux, redux toolkit, react redux toolkit, react, redux, redux toolkit performance, performance techniques, performance optimizations, redux optimizations, redux toolkit optimizations, react redux optimizations, redux performance techniques, react.memo, memoize, memoized, entity adapter, createEntityAdapter, react performance, react.js, reactjs, createSelector, js
Id: GdOgYQzGexY
Channel Id: undefined
Length: 34min 40sec (2080 seconds)
Published: Fri Apr 15 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.