Nerd For Tech
Published in

Nerd For Tech

Boost Your React Applications with React.memo, useCallback, and useMemo

One of the many reasons we love 💙 React is because it is really fast. React does tons of work under the hood to guarantee everything renders efficiently. Hence, in most cases, performance is not something we need to worry about. However, we can run into some situations where we find out that our components are rendering more than they need to, and consequently, slowing our apps down. 🐢🐢

Hi! I’m Ernesto, and in this post, I’ll talk about how to use , , and to make our React applications run like the wind. ⚡

React.memo 🧠

It is one of the coolest features that came with the release of React 16.6.0. ⚛️ As its name refers, allows us to make a performance boost only for function components by a memoizing process. It's similar to used for class components.

is a High Order Component (HOC), which is a function that takes a component and returns a new component.

takes a function component and returns a memorized (optimized) component.

Confused? 😐 Let’s see an example. 😁 — You can check the example in my CodePen. ✒

//Child Component
const ChildComponent = (props) => {
//This message is printed when the component renders
console.log("ChildComponent rendered")

return (
<div className="box">
<h1>Child Component</h1>
<h2>{props.name}</h2>
<p>This component does not change, so it should not render</p>
</div>
)
}
//Main Component
const MainComponent = () => {
const [value, setValue] = React.useState(0)
handleClick = () => {
setValue(value => value + 1)
};

return (
<div>
<h1 className="title">React.memo example</h1>
<div className="box">
<div>{value}</div>
<button onClick={handleClick}>+</button>
</div>
{/*Child component, its prop does not change*/}
<ChildComponent name="Ernesto"/>
</div>
)
}

We have a that renders a with the prop. The has a state, and if it changes, the component will re-render.

It makes sense, right? 🤔 If a component changes, it should re-render, but if it does not, it should not.

However, when we click the + button and change the state, the re-renders too. If you check the console, you'll see the "ChildComponent rendered" message.

That is an unnecessary re-render because the did not change at all; the only one that changed is the .

How can we solve this problem? 🤨 Well, now is when comes to the rescue. The only thing we have to do is to wrap our inside the function.

//Child Component
//We use React.memo
const ChildComponent = React.memo((props) => {
//This message is printed when the component renders
console.log("ChildComponent rendered")

return (
<div className="box">
<h1>Child Component</h1>
<h2>{props.name}</h2>
<p>This component does not change, so it should not render</p>
</div>
)
})

Now, if you see the console, the "ChildComponent rendered" message should appear only once, and when you click the + button and change the state, this message won't show because the did not re-render. 😎

compares the props passed to a component. If they are the same as last time, will use the previous rendered result and skip the re-rendering process.

Remember that only looks for props changes. That means that if your function component has a state (with hook), it will still re-render when the state changes.

useCallback 🏹 and useMemo 🔥

These two hooks can be kind of confusing. 😵 Both of them receive a function and an array of dependencies as parameters, and both will compute a new value only when one of the dependencies changes.

//useCallback returns a memoized function
const memoizedCallback = useCallback(() => {
doSomething();
}, [dependencies]);
//useMemo returns a memoized value
const memoizedValue = useMemo(() => {
return computeExpensiveValue()
}, [dependencies]);

The difference is that will return a memoized value, the result of the function passed, and will return the memoized function itself.

Let’s see how and when to use them. 😉

useCallback

It is especially useful when passing callbacks to optimized child components. Therefore, always works with .

We will take the previous example and change it a little bit. — You can check the example in my CodePen. ✒

//Child Component
const ChildComponent = React.memo((props) => {
console.log("ChildComponent rendered")
return (
<div className="box">
<h1>Child Component</h1>
<h2>{props.name}</h2>
<p>This component does not change, so it should not re-render</p>
{/*Button that will change the state of its parent component*/}
<button onClick={props.handle}>+</button>
</div>
)
})
//Main Component
const App = () => {
const [value, setValue] = React.useState(0)
handleClick = () => {
setValue(value => value + 1)
};
return (
<div className="main-comp">
<h1 className="title">Main Component</h1>
<div className="box">
<div>{value}</div>
</div>
{/*Pass the handleClick function through props*/}
<ChildComponent name="Ernesto" handle={handleClick}/>
</div>
)
}

Now, the button that changes the state of the is inside the ; therefore, we have to pass the prop that contains the function from the .

If you go to the console, you’ll notice the “ChildComponent rendered” message shows again every time we click the button. We are using , but the component is still re-rendering. What is happening? 🥴

That happens because every time the state changes, a NEW function is being created. That means that the prop of the is different every time we click the + button. That is the reason is not working; the prop is changing.

We can fix the problem by using the hook to memoize the function.

//Main Component
const App = () => {
const [value, setValue] = React.useState(0)
//useCallback hook
const handleClick = useCallback(() => {
setValue(value => value + 1)
}, []);

return (
<div className="main-comp">
<h1 className="title">Main Component</h1>
<div className="box">
<div>{value}</div>
</div>
{/*Pass the handleClick memoized function through props*/}
<ChildComponent name="Ernesto" handle={handleClick}/>
</div>
)
}

Notice that the array of dependencies is empty; this means that the function will be created only once. Therefore, the prop of the will always be the same. 😎

useMemo

We use when we must compute an excessive value, and we do not want to re-compute it again every time our component re-renders.

Let’s go with an example. — You can check the example in my CodePen. ✒

//Function that computes an exessive value
const heavyProcess = (times) => {
for (let i = 0; i < times; i++) {
console.log('Go ...', i + 1);
}
return `Heavy process done (result: ${times})`;
};

//Main Component
const MainComponent = () => {
const [value, setValue] = React.useState(0)
handleClick = () => {
setValue(value => value + 1)
};
//The returned value is saved
const heavyProcessValue = heavyProcess(1000)

return (
<div className="main-comp">
<h1 className="title">Main Component</h1>
<div className="box">
<div>{value}</div>
{/*The returned value is printed*/}
<p>{heavyProcessValue}</p>
<button onClick={handleClick}>+</button>
</div>
</div>
)
}

We are simulating a heavy process with the function that will print to the console the numbers from 1 to 1000 and return the last number printed (1000). Our has a simple counter, and it prints the returned value of the function.

If you go to the console, you will notice that all numbers from 1 to 1000 are being printed every single time we click the + button. So, every time the components re-renders (due to a change in its state) the executes again.

This time, will save us. We will use it to memoize the computed value.

//Main Component
const MainComponent = () => {
const [value, setValue] = React.useState(0)
handleClick = () => {
setValue(value => value + 1)
};
//The returned memoized value is saved with useMemo
const memoizedHeavyProcessValue = React.useMemo(() => {
return heavyProcess(1000)
}, [])
return (
<div className="main-comp">
<h1 className="title">Main Component</h1>
<div className="box">
<div>{value}</div>
{/*The memoized value is printed*/}
<p>{memoizedHeavyProcessValue}</p>
<button onClick={handleClick}>+</button>
</div>
</div>
)
}

Now, instead of passing the value that the function returns, we pass the memoized value that the hook gives us; this value will be computed again only if one of the dependencies changes. In our case, the array is empty, which means the value will be computed only once. 😎

Do not forget the statement in the first parameter of the hook.

Conclusions

  • Use to skip ⏭ unnecessary renders in your function components. If you want something similar to that for class components, use .
  • Use the hook when you want to pass callbacks 🏹 to child components through props.
  • Use the hook when you want to memorize an excessive value 🔥 returned by a function, like an HTTP request that gets tons of data.

Remember that before you start optimizing your apps, you should measure the cost first. ⚖ Otherwise, you will be optimizing things that may don’t need to be optimized. 😉

Thanks for reading! 📖 If this post helped you, please give it a clap. 🖐 And let me know if you have any contribution, comment, doubt, or recommendation. It helps me a lot to improve my content. 😃

See you in the next post. 👋

Originally published at https://ernestoangulo.hashnode.dev.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store