This article provides a detailed explanation of how to use the memo feature in React applications, including an exploration of how it works behind the scenes. The idea of returning data that was already stored is not new, but understanding how it works under the hood is very important for all developers. The memo is memory, and that development technic helps to optimize the calculation process for expensive functions. The usage of memorization is a very important practice and is laid as crucial knowledge for development in general. React has good instruments for memorization. But how does it work, why is it important, and how do you use it? Let’s find it out in this article in detail with practical examples. What Is Memorization? Memorization is the function that just has a store. Closure helps store data and take it from there by key to get the result. Very simple idea: key and value. Let’s say that we have a function that has two number arguments, and the result is an addition of both of those arguments. What do we do? We’ll pass this as an argument this function to the memo function. With the below function, we’ll create a property as an empty object and then returned the new function which is going to expect arguments. We’ll use those arguments as a key for storing and returning our results from the memorized function. memory function memorize(fn) { let memory = {}; return function (...args) { let key = JSON.stringify(args) || 'no_args'; if (memory[key]) { return memory[key]; } let result = fn.apply(this, args); memory[key] = result; return result; }; } You can see that after other calls of the memorized function, there is a condition where we check the existing results based on keys from stringified arguments. If the result already exists, we just return the result itself. How to Use It First of all, let’s put some in our function to understand better the state changes. console.log // ... let result = fn.apply(this, args); console.log('result', result); memory[key] = result; // ... Now, let’s create a function for memo: function addition(a, b) { return a + b; } Time to memorize it and reuse the function multiple times, and check how many times the memorized function was called actually. const memorizedAddition = memorize(addition); console.log(memorizedAddition(1, 2)); console.log(memorizedAddition(2, 2)); console.log(memorizedAddition(1, 2)); console.log(memorizedAddition(1, 2)); console.log(memorizedAddition(3, 3)); We created the memorized function and called five times but have passed the same arguments in three invocations. console.log result 3 result 4 result 6 The memorized function will be called only three times because there is no need to call the function again if the result is already known — in which case, the result will be returned from memory. Applying to a React Component What happens if we apply this to react component? Let’s create a very simple component in a React application using without . yarn create vite React.StrictMode Strict mode is not our target. It only makes additional rendering for highlighting potential problems from inherited APIs — for instance, some imported components. We’ll not use any libraries. Instead, we’ll focus on the render process. let key = JSON.stringify(args) || "no_args"; console.log("key", key); Let’s put another to our memorize function, and run the app. console.log const Block = ({ count }) => { return ( <div> <h1>Block: {count}</h1> </div> ); }; Then memorize our component using function: Block memorize const MemorizedBlock = memorize(Block); Let’s try to use the same component several times to make sure that was called only once. MemorizedBlock Block function App() { const [count, setCount] = useState(0); return ( <div> <MemorizedBlock count={count} /> <MemorizedBlock count={count} /> <MemorizedBlock count={count} /> </div> ); } If you check the results from our , you’re going to see the result: console.log key [{"count":0},{}] result {$$typeof: Symbol(react.element), type: 'div', key: null, ref: null, props: {…}, …} key [{"count":0},{}] key [{"count":0},{}] You can see that usage of the was many times, but the results from all the times were the same, which means that we calculate it only once. Other iterations were from memory. The same idea React provides for memorization functionality. MemorizedBlock React.memo Now, let’s create more components in order to play with all of them around, and check what was updated and at which time. export const Content = ({ value }) => { console.log("=== render Content ==="); return ( <div> <h1> Value: {value} </h1> </div> ); }; export const Header = ({ setCount }) => { console.log("=== render Header ==="); return ( <header> <h1>Header</h1> <button onClick={() => { setCount((count) => count + 1); }} > + </button> <button onClick={() => { setCount((count) => count - 1); }} > - </button> </header> ); }; export const Footer = () => { console.log("=== render Footer ==="); return ( <footer> <h1>Footer</h1> </footer> ); }; In each component, let's put in order to see what has been rendered. Finally, summarize everything in our application: console.log function App() { const [count, setCount] = useState(0); return ( <div> <Header setCount={setCount} /> <Content value={0} /> <Footer /> </div> ); } Run the application, and you have to see in the console the following output: === render Header === === render Content === === render Footer === Now, if you hit on for the button, you will see that all components will be rendered. Why does it happen? Because those components (functions) actually have not been memorized. Let’s wrap to in order to store properties, and calculate only stored results from memory as it’s been described before. Footer React.memo export const Footer = React.memo(() => { console.log("=== render Footer ==="); return ( <footer> <h1>Footer</h1> </footer> ); }); And render the app again, and you can see that, after hitting the button, only two components ( and have been updated, except because the has no changes in props. Let’s do the same with the component. Header Content) Footer Footer Content export const Content = React.memo(({ value }) => { console.log("=== render Content ==="); return ( <div> <h1>Value: {value}</h1> </div> ); }); After hitting the button, you will find out that it rerenders only because the is not memorized. But as you can see, we got some value in the , and it’s not rerendered because the value is always the same. Header Header Content <Content value={0} /> If you check the file for function, there will be the function: react.development.js memo memo(type, compare) { //... } And comparison happens by the following condition: compare === undefined ? null : compare It works with two and arguments in the additional argument for function like that: prevProps nextProps memo export const Content = React.memo(({ value }) => { console.log("=== render Content ==="); return ( <div> <h1>Value: {value}</h1> </div> ); }, (prevProps, nextProps) => { return prevProps.value === nextProps.value; }); Thus, you can fine-tune the props comparison for component updates. Now, add to the content the value: <Content value={count} /> and memorize the component: Header export const Header = React.memo(({ setCount }) => { console.log("=== render Header ==="); return ( <header> <h1>Header</h1> <button onClick={() => { setCount((count) => count + 1); }} > + </button> <button onClick={() => { setCount((count) => count - 1); }} > - </button> </header> ); }); After hitting the button, you find that only was rendered. It’s because the same function from the and React care about it. But what if we will create our own function for the handle state, like this: Content setCount useState function App() { const [count, setCount] = useState(0); const setCountHandler = (isIncrement) => { setCount((prevCount) => (isIncrement ? prevCount + 1 : prevCount - 1)); }; return ( <div> <Header setCount={setCountHandler} /> <Content value={count} /> <Footer /> </div> ); } And update the for function in props: Header export const Header = React.memo(({ setCount }) => { console.log("=== render Header ==="); return ( <header> <h1>Header</h1> <button onClick={() => { setCount(true); }} > + </button> <button onClick={() => { setCount(); }} > - </button> </header> ); }); By updating the state, both components and updating too. It’s because the function has a issue. To have the same reference for the function, let’s go to the next part. Header Content setCountHandler referential integrity useCallback For fixing the referential integrity issue, React has a special hook — . Let’s update the handler to: useCallback const setCountHandler = useCallback((isIncrement) => { setCount((prevCount) => (isIncrement ? prevCount + 1 : prevCount - 1)); }, []); Now updating only because it refers to the same for . But if it is provided to dependencies, the empty object is like this: Content setCountHandler const dependency = {} const setCountHandler = useCallback((isIncrement) => { setCount((prevCount) => (isIncrement ? prevCount + 1 : prevCount - 1)); }, [dependency]); The problem will be back because each object has a different reference on each render. To fix that issue, let’s go to the following part. useMemo This hook solves the issue with values with references. Literally, it’s returned memorized value. The hook also has a dependency on updating rules. const dependency = useMemo(() => ({}), []); const setCountHandler = useCallback((isIncrement) => { setCount((prevCount) => (isIncrement ? prevCount + 1 : prevCount - 1)); }, [dependency]); The difference between and is that returns memorized value, but returns memorized function. For example, if you provide value to like this: useMemo useCallback useMemo useCallback Context <Content value={{ count: 0 }} /> The component will keep updating even with a static count value. In this case, also will help to solve that issue. useMemo const value = useMemo( () => ({ count: 0, }), [] ); // ... <Content value={value} /> Now, the value is memorized, and after clicking by buttons nothing happens because the value is always the same. What happens if use state right in hook? count useMemo const value = useMemo( () => ({ count, }), [] ); After the update, nothing’s gonna change, and the state will be not updated at all. It’s because the dependency has not been provided in order to update that value. To fix that, add count from state to dependency: Content useMemo const value = useMemo( () => ({ count, }), [count] ); Now everything works fine. The dependency updates the correct, and value updating updates the component. useMemo Content Conclusion It's important to keep in mind that there is no need to memorize simple, lightweight functions. When dealing with straightforward operations that do not involve complex computations, there's little benefit in memorizing them as the cost of calculating them is minimal. On the other hand, for more intricate functions that involve expensive calculations, it may be worthwhile to store the results to avoid unnecessary and time-consuming recalculations. Therefore, it is recommended to exercise judgment when deciding whether to memorize a function or not, taking into account its complexity and the cost of computing it. Resources : React.memo https://reactjs.org/docs/react-api.html#reactmemo : React.useCallback https://reactjs.org/docs/hooks-reference.html#usecallback React.useMemo : https://reactjs.org/docs/hooks-reference.html#usememo Also published here