Caching in React – is it that simple?

We all know about useCallback(), useMemo()And memowhich are used to optimize performance in React applications. In this article, I will dive into how they work and create a quick cheat sheet to use them meaningfully and effectively.

Let's start from afar…

Why use caching at all?

When we write a small application in the form of a training TODO list or several pages with a small number of components, it may seem that caching is not necessary. The interface is responsive, and React is so cool and optimized that it does not touch the DOM tree like pure JavaScript, doing point redraws. But everything changes when projects become large and complex (lots of markup, logging, dependencies and conditional rendering), and your application starts to open on devices with less powerful hardware than your working one.

Caching in React is used to optimize application performance. It reduces the load on the CPU and GPU by using more memory resources. Modern devices often have more available memory than CPU or GPU resources, so caching becomes an effective optimization method. Redrawing components requires significant computing resources to perform various operations, such as creating a virtual DOM, comparing it with the previous state, and updating the real DOM. This can significantly slow down the application, especially if we are talking about complex calculations or a large number of elements. Caching allows you to avoid these costs by storing the results of previous calculations in memory and using them when needed. Thus, thanks to caching, you can significantly improve the performance of your application by reducing the time spent on redrawing and improving the overall responsiveness of the user interface.

First, it is important to understand that if you wrap absolutely all functions in useCallback and useMemo, as well as all components in memo, this will only increase the load on the device's memory. These “wrappers” hide the implementation of React, which “under the hood” checks whether it is necessary to use cached data or whether it is time to reset and re-execute the operation. This is done by comparing old dependencies (from the array of dependencies in the case of hooks and props for memo), when they change, the cache reset algorithm is performed.

Hook useCallback()

I use it when I need to pass a function as a callback prop to another component. It is also useful if the function performs really complex calculations. For example, it could be iterating over a large array, filtering data, performing complex math calculations, or processing data from an API. useCallback prevents a new function from being created on every render, which is especially important for performance when the function is passed to child components used as event handlers or callbacks in components that are updated frequently.

Hook useMemo()

Used to memoize the results of complex calculations such as iterating over a large array of data, sorting, filtering, processing data from an API, or complex mathematical calculations. useMemo returns a memoized value that is recalculated only when the dependencies specified in the dependencies array change.

While useMemo should not be confused with React.memo, you may occasionally see useMemo used to memoize the render results of a functional component. However, this is not a common practice, as the React community typically uses React.memo to memoize components. Following common practices makes your code more readable and understandable for other developers. For example, a function that returns JSX is better wrapped in React.memo, as it is more expected and understandable for those reading your code.

Just as it is common to prefix state-mutating functions created with useState with a set (e.g. setCount), using React.memo for components is a standard in the React community. This helps keep your code consistent and makes it easier to maintain and understand.

React.memo method

I use it to wrap components whose props don't change often. It's especially useful for reusable components from shared/ui (your UI kit). If the component is as props.children accepts a complex data structure (another component) rather than a primitive (a string or a number), such a component should not be memoized. Memoization takes memory, and comparing primitives is inexpensive. However, in the case of complex objects, it is expensive in terms of resources, since complex data structures (objects, arrays) are compared by reference. Even if such an object in props.children similar to the previous one, a reference to another memory cell is passed, and memo will not work – the component will be redrawn, and resources will be spent on checking the old and new value.

Use memoization wisely. Good luck, workers!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *