Demystifying useMemo and useCallback hooks

·

3 min read

Demystifying useMemo and useCallback hooks

React useMemo Hook

useMemo returns a memoized value.

What is memoization? You might ask, well it's prevents the reconstruction of objects during DOM re-renders. Almost like caching a value so that during DOM re-renders, it can serve the cached value.

Often useMemo is used to keep expensive and resource intensive functions from running during DOM re-renders.

let's take an example:

const heavyfunction = (number) => {
for(let i=0; i<20000; i++){
number += 1;
}
return number;
}

function Example() {
const [count, setCount] = useState(0);
const heavyValue = heavyfunction(0)
return (<div>
<button onClick={()=>setCount(c=>c-1)}>-</button>
<h3>Count: {count}</h3>
<button onClick={()=>setCount(c=>c+1)}>+</button>
</div>)
}

In this example heavyfunction will run every time there is a count state change and subsequent DOM re-render. That's unnecessary if you want heavyfuntion to run during first DOM render. Well, we can use useMemo to memoize the heavyValue. Here's how:

const heavyfunction = (number) => {
for(let i=0; i<20000; i++){
number += 1;
}
return number;
}

function Example() {
const [count, setCount] = useState(0);
const heavyValue = useMemo(()=>heavyfunction(0), [])
return (<div>
<button onClick={()=>setCount(c=>c-1)}>-</button>
<h3>Count: {count}</h3>
<button onClick={()=>setCount(c=>c+1)}>+</button>
</div>)
}

As you can see that useMemo can accept a dependency array just like useEffect and will run only if the values in array change. If the dependency array is empty, it will only run once on first DOM render.

React useCallback Hook

useCallback hook returns a memoized function.

Example:

function Example() {
const [count, setCount] = useState(0);
const [tasks, setTasks] = useState([])
const addTask = () => {
setTasks(ts => [...ts, "Task"])
}
return (<div>
<Tasks setTask={addTask} tasks={tasks}>
<button onClick={()=>setCount(c=>c-1)}>-</button>
<h3>Count: {count}</h3>
<button onClick={()=>setCount(c=>c+1)}>+</button>
</div>)
}


function Tasks ({addTask, tasks}) {
console.log('Tasks component rendered...')
  return (
  <>
  <h3>All the tasks:</h3>
  {tasks.map((task, index) => {
   <span key={index}>{task}</span>
  })}
  <button onClick={addTask}>Add Task</button>
  </>
)}

Here you can see, every time we press increment or decrement, the Tasks component re-renders which is unnecessary. This is due to recreation of addTask function and that leads to re-render of Tasks component, how do we prevent this behavior? Well, enter useCallback Hook and it takes a dependency array just like useMemo:

function Example() {
const [count, setCount] = useState(0);
const [tasks, setTasks] = useState([])
const addTask = useCallback(() => setTasks(ts => [...ts, "Task"]), [tasks])

return (<div>
<Tasks setTask={addTask} tasks={tasks}>
<button onClick={()=>setCount(c=>c-1)}>-</button>
<h3>Count: {count}</h3>
<button onClick={()=>setCount(c=>c+1)}>+</button>
</div>)
}


function Tasks ({addTask, tasks}) {
console.log('Tasks component rendered...')
  return (
  <>
  <h3>All the tasks:</h3>
  {tasks.map((task, index) => {
   <span key={index}>{task}</span>
  })}
  <button onClick={addTask}>Add Task</button>
  </>
)}

Boom, now Tasks component will only re-render when tasks change and that is what we want. This hook is very useful in a dashboard design.

About BoomLabs:

BoomLabs is an agency providing new age web solutions. Our services include:

  • Web Design and Development
  • SEO (For SaaS and E-commerce businesses)
  • Content writing using AI.