React: How does useMemo and useCallback work ?
Tags: react, javascript
Published: 2022-10-24 03:24:07

# 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.<br>
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: 
```javascript
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
```
```javascript
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
```

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

*:collision:	All the code reference I mentioned in this article was translated to `javascript`and cut-off **comments** and **warning parts** just for shorter code and more clear.<br>
Click **View Source** to see the original code*

## **1.Mount Phase (First Render)**
```typescript
function mountMemo(nextCreate, deps){
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}
```
[View Source](https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHooks.new.js#L1899-L1908)

```typescript
function mountCallback(callback, deps){
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  hook.memoizedState = [callback, nextDeps];
  return callback;
}
```
[View Source](https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHooks.new.js#L1899-L1908)

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**
```typescript
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;
}
```
[View Source](https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHooks.new.js#L1910-L1929)

```typescript
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;
}
```
[View Source](https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHooks.new.js#L1883-L1897)

When re-render a component, `useMemo` and `useCallback` will compare old dependency (that was already memoized) with the new one.<br>
If one of those are `null` or they are difference (result from `areHookInputsEqual`), `useMemo` and `useCallback` will store and return the new value. <br>
Otherwise, return memoized value.

**Compare hook dependency function**
```javascript
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;
}
```
[View Source](https://github.com/facebook/react/blob/v18.2.0/packages/react-reconciler/src/ReactFiberHooks.new.js#L327-L372)

React will use `Object.is` to compare previos and next value of dependency. You may find the difference of its behavior [here.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#comparing_equality_methods)
```javascript
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;
```
[View Source](https://github.com/facebook/react/blob/v18.2.0/packages/shared/objectIs.js)


### FYI
:tada: As React announcement in July 2022, there might be a new Compiler that auto generate `useMemo` and `useCallback` for us. <br>
You can read this post here: [https://reactjs.org/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.html](https://reactjs.org/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.html)