And to demonstrate to you how bad those can be, I even implemented a little bit of an app. So this is an app that renders a list of components, and has a little bit of interactivity. So take a look. On the right, Performance tab, I click everywhere, and everything is instantaneous. On the left, exactly the same app, but I made a couple mistakes there, and look how unbearably slow all of this is. Just a few tiny mistakes in the right places, and I just destroyed this app. And it's just a list of components.
So common mistakes that lead to performance like that, and also useful performance tips and tricks to avoid re-renders of the entire app, is what I want to share with you today. Let's start with mistakes. The very first one is one of my favorite, is what I call the myth of useMemo and useCallback. So as probably most of you know, React uses referential equality when it compares props or dependencies in all the various hooks. And referential equality is basically this. We have two arrays or two objects. If we want to compare them, if we do it like this, the result will be false, because we're comparing them by the reference, not by the actual value.
And from React's perspective, it sounds like this. We have a component, it renders a child component. I pass a value to this child component that is an array. If I do it like this, during a rerender, this value will become a completely different value. So if React compares those props, React will think that value of the prop has changed. And if I write a memo and use callback hooks, hooks that allow you to memoize this value, and basically to preserve reference to this value between rerenders. So if I extract this array into useMemo hook, then when a rerender happens, React will think that value in a child component will be exactly the same. And the fact that one of the most important reasons why a component rerenders is a state or prop change, in combination with how those hooks work, lead to the widespread belief that if we memoize all the props on a component, that will prevent this component from re-rendering. And this results in something that I call a useMemo or useCallback hook hell, because memoizing absolutely everything leads to your app becoming, having useMemo, you wrap in useCallback, and then another useCallback, it's just useMemo and useCallbacks everywhere, and the app becomes incomprehensible and completely not readable and not debuggable. So I think those become really horrible.
But the worst part of all of this is that it's actually sometimes, it's actually useless, because we're forgetting one key component in all of this construction. So if we take a look, for example, at this code, we see a component, it has a child component and then onClick, Prop, and we want to prevent child component from re-rendering by wrapping onClick in a useCallback hook. But what exactly can trigger child components to re-render? We prevented Prop from changes. The only thing that is left when a parent component re-renders. So we will trigger a state, for example, a child component will re-render, and React will not actually check whether Prop has changed or not at this stage, because React's natural way of dealing with components is components re-render, and then re-render every single child. Wrapping onClick here in useCallback is just completely useless, we are doing nothing here.
Comments