Preventing function re-creation in React with useCallback

We can make React Memo work for prop values that are objects as well. We just need to tweak the way we create and store those objects a little bit. There is an extra hook provided by React that helps us with that. And that is the useCallback hook.

useCallback is a hook that allows us to basically store a function across component executions. So it allows us to tell React that we wanna save a function and that this function should not be recreated with every execution. So for example if we have let obj1 = {} and let obj2 = {} and we make compare them: obj1 === obj2 the result will be false. If we have obj2 = obj1, obj2 will point to the same point in memory so obj2 === obj1 is true.

And that's in the end what useCallback does for us, it will save a function of our choice basically somewhere in React's internal storage and we'll always reuse that same function object then when this component function executes. And using it is simple. We just need to wrapp the function we wanna save with it. useCallback then returns that stored function and when our component reruns useCallback will look for that stored function which React stored for us and reuse that same function object. So if we know that this function should never change, we can use useCallback to store it.

useCallback like useEffect actually wants a second argument and it wants it even more so than useEffect and just like for useEffect the second argument should be an array of dependencies off this useCallback call and the dependencies here are the same as they are for useEffect. Anything you use in your function which is coming from the surrounding component, so any state or props or context should be specified here. Now here we only have to state updating function and we could add this as a dependency but we don't have to because as mentioned early in the course React guarantees us actually with help of useCallback probably that this will never change. Let's look at an example:

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); console.log('APP RUNNING'); const toggleParagraphHandler = useCallback(() => { setShowParagraph(prevShowParagraph => !prevShowParagraph); }, []); return ( <div className="app"> <h1>Hi there!</h1> <DemoOutput show={false} /> <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);

In this example we see the console.log with the "BUTTON running" only once because we use useCallback hook and the toggleParagraphHandler function is created only once, even if the App component reruns when we change the state for showParagraph.

Category: React Tags: #react, #performance