React - useCallback() and its dependencies

It is very important to understand the useCallback() hook. useCallback() allows you to save a function so that you can reuse it. Now you had to specify this dependency array, and I don't know about you, but initially you might think, what do I need this for? My function has always the same logic across rerender cycles. Well, keep in mind that in JavaScript functions are closures, which means they close over the values that are available in their environment.

Let's look at this code:

App.js:

import React, { useState, useCallback } from 'react'; import Button from './components/UI/Button/Button'; import DemoOutput from './components/Demo/DemoOutput'; import './App.css'; function App() { const [showParagraph, setShowParagraph] = useState(false); const [allowToggle, setAllowToggle] = useState(false); console.log('APP RUNNING'); const toggleParagraphHandler = useCallback(() => { if (allowToggle) { setShowParagraph(prevShowParagraph => !prevShowParagraph); } }, [allowToggle]); const allowToggleHandler = () => { setAllowToggle(true); }; return ( <div className="app"> <h1>Hi there!</h1> <DemoOutput show={showParagraph} /> <Button onClick={allowToggleHandler}>Allow Toggling</Button> <Button onClick={toggleParagraphHandler}>Toggle Pharagraph!</Button> </div> ); } export default App;

Button.js:

import React from 'react'; import classes from './Button.module.css'; const Button = (props) => { console.log('BUTTON running'); return ( <button type={props.type || 'button'} className={`${classes.button} ${props.className}`} onClick={props.onClick} disabled={props.disabled} > {props.children} </button> ); }; export default React.memo(Button);

DemoOutput.js:

import React from 'react'; import MyParagraph from './MyParagraph'; const DemoOutput = (props) => { console.log('DemoOutput RUNNING'); return <MyParagraph>{props.show ? 'This is new!' : ''}</MyParagraph>; }; export default React.memo(DemoOutput);

So functions in JavaScript are closures. That means when a function is defined, which happen when the app function runs in this functions case, when that function here is defined, JavaScript basically locks in all the variables that we're using in there. All the variables that are defined outside of the function what we are using, I should say. In this case, the allowToggle variable, that is a variable or a constant coming from the app function and I'm using it inside of the toggleParagraphHandler function. Therefore JavaScript closes over that constant and basically stores that constant for the toggleParagraphHandler definition. That means the next time when this function is executed, the toggleParagraphHandler function, the stored value for allowToggle variable will be used. This generally is perfect because this allows us to use variables from outside the function, in the function, and call that function at any point of time we want, which is exactly what we want for a function that we bind to a button. The problem with that, and with useCallback however is, that with useCallback we're telling React to store that function and exactly that function somewhere in memory. Now, when the app function is re-evaluated and reexecuted because the toggle state changed, then React will not recreate this function because we're using useCallback where we told React that we don't want to recreate it under all circumstances.

So therefore the allowToggle value that React stored for our function, is still the old allowToggle value from the first time the app component was executed, not the most recent one because JavaScript, as I just explained, stored allowToggle this constant when it created that function here. There are cases where we actually want to recreate a function because values being used in that function that are coming from outside the function might have changed. And here we have such a case. So here we would want to add allowToggle as a dependency in our dependency array. And that tells React that we generally want to store that function. But, whenever allowToggle changes and it has a new value, we want to recreate that function and store that new recreated function. And this ensures that we always use the latest allowToggle value inside of that stored function. If allowed toggle does not change however, then we don't recreate the function.

Category: React Tags: #react, #performance