React 16.5.0 now supports the new Profiler feature in the React Developer Tools. In this video, we will update an existing app to React 16.5.0 and show off various features of the new Profiler. The tool can automatically record a lot of useful information from your React App, but you can also add your own custom interactions to help track user generated events.
Instructor: [00:00] Here, we have the Redux example, TodoMVC, running in the browser. We're going to test out the new profiler feature of the React Developer Tools. Make sure you have it installed with the latest version.
[00:12] Once you have the React Dev Tools extension and switch to the tab, you may not see anything immediately different. There is no profiler option. If that's the case, it's probably because you don't have React version 16.5 or above installed, which added support for the profiler. Let's upgrade our version of React.
[00:30] We're going to use a tool called npm-check using npx, with the -u flag so we can interactively update our dependencies. Here, we will update React and ReactDOM to 16.5.
[00:42] After our app refreshes, we'll see the new profiler tab in the React Developer Tools. No data has been recorded yet. Let's do that now. I'll hit the record button and then start using the app. I'll make a to-do item, check it off, mark them all as done, and then clear all the completed, and stop.
[01:03] Now in the profiler, we'll see a flame graph of the renders that we committed. In this case, there were 12 renders recorded. For each render, we could see what components were involved and which ones took the most time to complete. The create items didn't need to re-render at all. It could be they are pure components, or they implemented shouldComponentUpdate manually.
[01:23] You'll notice there's not much going on in the first sub-renders, but if we drill into the orange item, we'll see the to-do text input component on the right. We can see the props and state for that component.
[01:34] This is the render from which I started to type the first to-do in the app. If I click the right arrow, we can see the state update and flash yellow for the items that have changed. Pretty neat.
[01:44] Next, in the render scrubber, I can visually which renders took longer than others and jump to one for further inspection. We could double-click on an item like main section, for example, and see a comparison graph of how long it took across all renders. Then I could drill into one of the instances by double-clicking on it to see which render that was related to.
[02:04] We could also look at a flat ranked view of the components from top to bottom, where items at the top took the longest, and items at the bottom took the least amount of time to render, which could be helpful when trying to make sense of all this data.
[02:18] There's another view called interactions, but it'll probably be empty by default. We have to add some code to our app before anything will show up here. The point of interactions is to give meaningful names to user events, like when a to-do action was being added or when an item is being deleted. It's a way to tag a render with a name.
[02:37] To do this, we'll need the track function from the Schedule npm package. Let's install Schedule from the terminal, and then go to the to-do text input JS file. At the top, we'll import unstable_track as track from schedule/tracking. As a side note, the schedule API is unstable at the moment and could change slightly going forward.
[03:04] Then inside our handle submit, we'll add a track call around the save and set state code. We'll give it a name to identify it by, add to-do, provide the time that this occurred with performance.now, and then a callback with our existing code.
[03:23] If we start to profile our code again and start to exercise our app, we'll add an item, mark it complete, mark them all complete, and then delete the items, and done. This time, if we go to the interactions tab, we'll see a marker indicating that add to-do that we coded.
[03:44] We could see on the right exactly how long that took and when it happened during our recording. If we click the link, it'll bring us to the associated render, so we can examine other components at that point in time. The metadata on the right of the render shows that this is the add to-do interaction.
[04:02] This is pretty great, but what about a faster way to add interactions? Since this app uses Redux, we could dive into the main section and make a helper function to wrap our actions. First, let's grab the import from to-do text input and copy that into our main section file.
[04:20] Now we'll create a track actions function that will take our actions object, loop over the keys, and reduce over them to create a new object. Here, we'll save off the action for later use. We'll add to our memo accumulator the name of the action, and start to build function wrappers around them.
[04:44] Each wrapper will use the schedules track function and leverage the name of the action as what will be displayed in the UI, grab the current time from performance.now, and then pass the action as the callback, making sure the appropriate arguments are getting passed along in the process. Then we'll use our new track actions function and pass our bind action creators to it.
[05:13] Let's rerun our app and try this again and record, create a to-do, mark as complete, mark all complete, remove all completed, and done. Now if we look at the interactions, we'll see three items. One that we manually added, add to-do, and the others were automatically generated based on Redux actions. At this point, we could poke through the interactions and investigate as needed.
[05:39] The ability to profile React like this is very powerful. I'm super excited about having it in React developer tools. Thank you, React team.
Awesome Elijah, thanks for explaining this so clearly!