React: Advanced Hook memo, useCallback, useMemo === ![](https://i.imgur.com/ZLirEbH.png) ###### tags: `React` --- ## memo **memo** lets you skip re-rendering a component when its props are unchanged. ```typescript! // App.tsx import { useState } from "react"; import ChildComponent from "./components/childComponent"; export default function App() { const [value, setValue] = useState<string>(""); console.log("App redering!"); return ( <div className="App"> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <p>App : {value}</p> <ChildComponent num="1234" /> </div> ); } ``` ```typescript! // ChildComponent.tsx import React, { memo } from "react"; type TNum = { num: string; }; const ChildComponent = ({ num }: TNum) => { console.log("ChildComponent rendering!"); return <p>ChildComponent: {num}</p>; }; export default ChildComponent; ``` In the example above, we have created a `ChildComponent`, it accepted a prop from `App`, as you can see the result here, everytime we type a number, both re-render. ![](https://i.imgur.com/XfEmRx9.png) We can use `memo` to skip re-render when props are not changed. Since the prop (1234) was hardcoded, it never changed, therefore, only `App` re-render while typing. ![](https://i.imgur.com/lEfk6no.png) **Note:** `memo` is a **HOC(Higher order component)**, so we should wrap the entire component and React uses **shadow comparison**, therefore, it will compare the state, since prop 1234 is not changed, hence, `ChildComponent` is not going to re-render. ### How about pass a function or an object? ```typescript! // App.tsx import { useState } from "react"; import ChildComponent from "./components/childComponent"; export default function App() { const [value, setValue] = useState<string | number>(""); const sayHello =() => { console.log("Hello"); } console.log("App redering!"); return ( <div className="App"> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <p>App : {value}</p> <ChildComponent num="1234" sayHello={sayHello}/> </div> ); } ``` ```typescript! // ChildComponent.tsx import React, { memo } from "react"; type TNum = { num: string; sayHello: () => void; }; const ChildComponent = memo(({ num }: TNum) => { console.log("ChildComponent rendering!"); return <p>ChildComponent: {num}</p>; }); export default ChildComponent; ``` Even we did not actually execute it and we used `memo` , it still triggered re-render. When we type a character, re-render happened, prop 1234 was the same value, but function wasn't, we should see each function has different address in the memory, eventhough it looked the same, therefore, it triggered both `App` and `ChildComponent` re-render. ![](https://i.imgur.com/3wTxaC3.png) > Reference: > [React doc-memo](https://beta.reactjs.org/reference/react/memo) --- ## useCallback That's take the example above, if we don't want function `sayHello()` to be triggered, we can use `useCallback`. Just like `useEffect`, it takes a second param **dependency**, if we want to trigger `sayHello()` when value changes, we can add `value` as a dependency. ```javascript const sayHello = useCallback(() => { // logic here },[value]) ``` Now, we just need to use an empty array to kind of **lock** the function reference(address). ```typescript! import { useState, useCallback } from "react"; import ChildComponent from "./components/childComponent"; export default function App() { const [value, setValue] = useState<string | number>(""); const sayHello = useCallback(() => { console.log("Hello"); },[]); console.log("App redering!"); return ( <div className="App"> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <p>App : {value}</p> <ChildComponent num="1234" sayHello={sayHello}/> </div> ); } ``` ![](https://i.imgur.com/2INcvKX.png) > Reference: > [React doc - useCallback](https://beta.reactjs.org/reference/react/useCallback) --- ## useMemo When should we use **useMemo**? **useMemo** is meant to be used to optimize performance, therefore, we can use **useMemo** for executing an extremely computational expensive operation. ```typescript! import { useState, useMemo } from "react"; import "./styles.css"; // import ChildComponent from "./components/childComponent"; const expensiveCal = (num: number) => { for (let i = 0; i < 100000000000000; i++) {} return num * 10; }; export default function App() { const [value, setValue] = useState<string | number>(""); console.log("App redering!"); const expensiveValue = expensiveCal(10); return ( <div className="App"> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <p>App : {value}</p> <p>expensiveValue: {expensiveValue}</p> </div> ); } ``` Try to execute it and see the result in the browser, it should become slow. ```typescript! import { useState, useMemo } from "react"; import "./styles.css"; // import ChildComponent from "./components/childComponent"; const expensiveCal = (num: number) => { for (let i = 0; i < 100000000000000; i++) {} return num * 10; }; export default function App() { const [value, setValue] = useState<string | number>(""); console.log("App redering!"); const expensiveValue = useMemo(() => { return expensiveCal(10); },[]); return ( <div className="App"> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <p>App : {value}</p> <p>expensiveValue: {expensiveValue}</p> </div> ); } ```