As a means of performance optimization, the useMemo cache function component is generally used to calculate the performance consumption:

function App() {
  const memoizedValue = useMemo(
    () = > computeExpensiveValue(a, b),
    [a, b]
  );
  // ...
}
Copy the code

The new memoizedValue is recalculated only after the dependency changes.

Have you ever wondered what would happen if you used useMemo to cache the return value of a function component?

For example

We have a global context — AppContext.

As the students were lazy, as the project went through iterations, new contexts were placed in AppContext, resulting in more and more content in AppContext.

Now we have a Tree component that will render a big, expensive component for ExpensiveTree.

function Tree() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme;

  return <ExpensiveTree className={theme} />;
}
Copy the code

This component internally depends on the theme state in the AppContext.

The Tree is re-render for every unrelated state update to the AppContext, and the ExpensiveTree component is also re-render.

Now that the optimization task has been handed to you, what should you do?

Optimize ExpensiveTree

That’s where useMemo comes in:

function Tree() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme;

  return useMemo(() = > {
    return <ExpensiveTree className={theme} />;
  }, [theme])
}
Copy the code

We will return the ExpensiveTree as the useMemo return value, theme as the dependency.

ExpensiveTree will only render after the theme change, even if the AppContext changes causing the Tree to render repeatedly.

The principle of analytic

To understand why this works, there are three things to know:

  1. What is the return value of useMemo

  2. What is the return value of the function component

  3. When does the React component render

Answer the first question: useMemo stores the return value of the first parameter (function) in the corresponding fiber of the component, and only calls the first parameter (function) again to compute a new value after the dependency (second parameter) changes.

Answer the second question: the return value of the function component is a JSX object.

The same function component is called multiple times, returning multiple JSX objects (even though the props are the same, but JSX is the new reference).

According to the above two answers, we can draw the conclusion that:

The above useMemo usage actually caches an entire JSX object in fiber corresponding to the function component

Third, function components need to satisfy the following conditions to render:

  1. oldProps === newProps

The props were all updated.

  1. The component context does not change

  2. workInProgress.type === current.type

Fiber. type does not change before and after component update, for example div does not change to P.

  1. ! includesSomeLane(renderLanes, updateLanes)

Currently there are no updates on Fiber, or there are updates with low priority.

When does the React component render

When we don’t use useMemo wrapping to return the value, each Tree Render returns a new JSX object.

So for ExpensiveTree, oldProps! = = newProps.

ExpensiveTree Internal context unchanged, satisfied

ExpensiveTree update before and after the type is ExpensiveTree

Look again at 4: ExpensiveTree inside no status update, meet

So when we wrapped the ExpensiveTree with useMemo, we returned the same JSX object after each Tree render, satisfying the first item.

For this reason, ExpensiveTree does not render.

conclusion

The useMemo usage mentioned in this article is not reflected in the official documentation, but is introduced by Dan in #15156.

React is more flexible than Vue and requires more attention to detail during development. To fully understand React, you may need to learn some source code.