DEV Community

Linh Bui
Linh Bui

Posted on

React: How does useMemo and useCallback work ?

How does useMemo and useCallback work ?

When render a component, React will process the whole Component code. There might be several computed values and functions that its values only depends on few dependencies and does not need to re-calculate everytime.

If these values require a lot of time to calculate, it will let the render process down as consequent.

In order to improve performance, React produce useMemo and useCallback.
Implement it quite simple:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Enter fullscreen mode Exit fullscreen mode
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
Enter fullscreen mode Exit fullscreen mode

The way it work are similar to each other and quite easy to understand.

💥 All the code reference I mentioned in this article was translated to javascriptand cut-off **comments* and warning parts just for shorter code and more clear.

Click View Source to see the original code*

1.Mount Phase (First Render)

function mountMemo(nextCreate, deps){
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}
Enter fullscreen mode Exit fullscreen mode

View Source

function mountCallback(callback, deps){
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  hook.memoizedState = [callback, nextDeps];
  return callback;
}
Enter fullscreen mode Exit fullscreen mode

View Source

When component was rendered on first time, useMemo will execute the create function (nextCreate) to get return value and then store the value and dependency list into memoizedState, meanwhile useCallback store all of its inputs.

2.Update Phase

function updateMemo(nextCreate, deps) {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
      const prevDeps = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}
Enter fullscreen mode Exit fullscreen mode

View Source

function updateCallback(callback, deps) {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (prevState !== null) {
    if (nextDeps !== null) {
      const prevDeps = prevState[1];
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        return prevState[0];
      }
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}
Enter fullscreen mode Exit fullscreen mode

View Source

When re-render a component, useMemo and useCallback will compare old dependency (that was already memoized) with the new one.

If one of those are null or they are difference (result from areHookInputsEqual), useMemo and useCallback will store and return the new value.

Otherwise, return memoized value.

Compare hook dependency function

function areHookInputsEqual(nextDeps, prevDeps) {
  if (prevDeps === null) {
    return false;
  }
  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}
Enter fullscreen mode Exit fullscreen mode

View Source

React will use Object.is to compare previos and next value of dependency. You may find the difference of its behavior here.

function is(x, y) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
  );
}
const objectIs = typeof Object.is === 'function' ? Object.is : is;
export default objectIs;
Enter fullscreen mode Exit fullscreen mode

View Source

FYI

🎉 As React announcement in July 2022, there might be a new Compiler that auto generate useMemo and useCallback for us.

You can read this post here: https://reactjs.org/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.html

Top comments (2)

Collapse
 
binjuhor profile image
Kiem Hoang

Thank for sharing

Collapse
 
hungpein profile image
Nguyen Van Hung

Best post I've ever seen