DEV Community

Azizi Yazit
Azizi Yazit

Posted on

Performance optimizations in React

In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again
~wikipedia

React used Virtual DOM to reduce browser reflow and repaint using diff algorithm called "Reconciliation". "Reconciliation" is fast but when we have too many of them (re-renders), its too costly and we should plan for optimization.

React have 2 built-in hooks to prevent unnecessary renders in component that rely on reference equality. And to avoid recompute expensive calculations. Those hooks is useMemo and useCallback.

Reference equality means that two object references refer to the same underlying object.

An experienced JavaScript developer know that below code is return false

{} === {} // false
[] === [] // false
() => {} === () => {} // false
Enter fullscreen mode Exit fullscreen mode

Example of expensive calculation is checking for prime number

function isPrime (num) {
  if (num <= 1) {
    return true
  } else if (num <= 3) {
    return true
  } else if (num%2 === 0 || num%3 === 0) {
    return false
  }

  let i = 5
  while (i*i <= num) {
    if (num%i === 0 || num%(i+2) === 0) {
      return false
    }
    i += 6
  }
  return true
}
Enter fullscreen mode Exit fullscreen mode

without memoization, I can't imagine that the function above run/triggered every render.

useMemo hook

useMemo will return a memoized version of the value (all types including function) that only changes if one of the dependencies has changed.

Here is example use-case of useMemo

the memoizedTasks only triggered a render when there is a changes in tasks state. You can see the console.log only triggered when tasks state changed.

Any changes to others state in statisticModuleContext will not causing re-render of TodoList.

useCallback hook

useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.

And both useMemo and useCallback can behave similarly:

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

However, usually useCallback used for parent-child component.

parent-child component

When we have identified a parent component that have a high possibility to re-renders a lots, we can use useCallback to prevent unnecessary re-render of the child.

Consider below code examples (i took the example from Kent C Dodds article as to me its the most clear example)

DualCounter is the parent component that have CountButton as a children. When DualCounter is re-rendered, its functions (increment1 and increment2) will be renew/reinitiated and causing re-render of the CountButton. This is because the increment1 and increment2 is the child dependency.

By using useCallback and without function dependency changes, we be able to prevent the unnecessary re-render of the CountButton as the functions have been memoized.

Please aware that optimization comes with price and should only be used on a VALID use-cases as we mentioned above.

React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things
~Kent C Dodds

Discussion (0)