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:
-
What is the return value of useMemo
-
What is the return value of the function component
-
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:
- oldProps === newProps
The props were all updated.
-
The component context does not change
-
workInProgress.type === current.type
Fiber. type does not change before and after component update, for example div does not change to P.
- ! 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.