If you already have a subscription, you can sign in.
Enjoy free content straight from your inbox 💌
00:00
We've looked at the use memo hook and how it can help improve the performance of your application and prevent unnecessary re renders. Close. The related hook and one that we will cover in this tutorial is the use callback hook, which you can think of as the use memo for functions. So let's go to demonstrate the objective of the use called back hook. Consider a very simple custom component called primary button. It takes the standard button props, which will be the children, and on click, we have some custom button styles to set this button apart from the default button.
00:32
And then finally, we have the function for our button component. And because we plan to use this button again and again in lots of places in our application, we want this to be as performance optimized as possible. And for that purpose, we use the builtin react memo function to turn this into a pure component. This means that this button should not read render if the past in props haven't changed. And to validate that I added a very simple console log statement to make sure that we get notified if this component ever re renders despite us thinking that it really shouldn't.
01:05
So let's try to use this primary button in a very simple application. We bring in the primary button from the primary button module, and then within our app component we create a simple state variable for a counter. And then we have this nice little utility function called increment, which increments the count by one. Within the ui we render out to diviv, which displays the current count value, and then we use our primary button with on click wide to that increment function. And this simple application definitely works. As you would expect. We can press the plus button to increment the count.
01:36
However, what you might not notice is the fact that every single time the component reruns because of a change in the state, the primary button still reruns even though it really shouldn't. We have the same increment function and we have the same children every single time. The answer to this mystery is just like all of the mysteries in the universe lies within JavaScript. Every single time we write a function body, we are essentially creating a different function. So if we have two variables pointing to what seemingly looks like the same function, they will be different.
02:07
Only the variables pointing to the same instance will be the same. So if you want a React memo on the primary button to think that the on click increment function is the same, we need to provide the same function every single time. And for that, there is the use call by hook that is provided by React. It takes a function as the first argument and the dependency array as a second argument. And if the dependencies do not change, then it returns the same function that it received in the previous render. And this preserves that referential equality so we don't get unexpected renders.
02:42
So let's jump back to our application and then bring in the use callback hook, which is exported from the main react module, just like you state use effect and other hooks that we've looked at. And then instead of assigning a function to increment directly, we first invoke use callback pass data function and then also pass an independency array to identify anything that the function body depends upon. In our particular case, it only depends upon the set con function. And now use callback will memorize the function that it returns. So increment should be the same between different re-render. We can verify this fact quite easily
03:15
by running our application. And now as we change the count value, the increment function is going to be the same that gets passed into the primary button and the primary button and does not read enter. Similar to the use memo hook, the use callback also takes a dependency raise a second argument, and it's surprising how often you might need to use this if your callback depends upon variables outside of the function body. But be careful as you might be causing a de optimization. Let's make a simple modification to our application to allow the user to set their own custom step size. For this, we create a new state variable
03:49
and we initialize it to one. And then within the increment function, instead of doing count plus one, we will use count plus step size. Within the ui, we will render out a simple input of type number wired to this step size state variable. So now the UI will start off with a default increment size of one, but of course we can jump into the input, try to modify the step size value, for example, to three, and now the increments should be in steps of three. However, as we click the plus button, you can see that it is still incrementing with the value of one.
04:22
And of course, the reason is pretty simple because use callback is caching a function. It only captures the value of step size on the first render. So that original function with the step size of one is what we get back every single time for our increment function. Since we want to use callback to capture a new function, if the step size changes, we add that to our dependency array. And with this simple change in place, our application should now work perfectly fine. We have the default step size of one. We can change the step size to, for example, three. And now the increment function is incrementing in steps of three.
04:55
A perhaps obvious side effect of this that is not ideal is that while we change the count, the button does not re-render. However, because we recalculate increment on a change in step size, changing the step size causes a re rendering in our button. And ideally we wouldn't want our primary button to read enter. So here are our constraints. We understand that anything that this function depends upon needs to be provided as a dependency array. And any change in a dependency array does cause a re rendering of the button component. The solution to this problem is to rewrite the code
05:30
to reduce external dependencies. We consolidate the count and the step size into a single counter state variable. This means that our increment function only really needs to depend upon the set counter function, and it does not need to depend upon the count or the step size. With the set counter function, we take the previous state of the counter and then copy all of the props except for the count, which we modify to be counted, count plus counter step Size. And then with the step size function, we do the same except this time we replace the step size with the past in value within the ui.
06:04
Instead of using just count and step size, of course we need to use counter or count and counter step size. And now that we've removed the external dependency on count or counter for the increment function, it'll be the same function that gets returned every single time. If we jump to the ui, you can see that as you modify the count, there are no additional re renderings of the primary button. And similarly, if we change the step size, the primary button does not re-render because the increment function that is returned is going to be the same, and we still get the correct increments of the step size because they are being fetched within
06:37
that set counter callback. In summary, if you want to preserve referential equality for functions, which helps with performance optimization, you can do that with the use callback hook. As always, thank you for joining me and I will see you in the next one.