为何React.memo()会持续渲染组件?——关于JavaScript原始值与引用值的疑问
Great question! Your initial understanding is completely correct—the difference in how React.memo() behaves with string props vs. object/array props is directly tied to JavaScript’s primitive vs. reference value handling. Let’s break this down, address your follow-up questions, and clarify why React.memo() is still incredibly useful.
Why the Rendering Difference Happens
JavaScript has two categories of values, and they’re compared very differently:
- Primitive values (strings, numbers, booleans,
null,undefined,Symbol): These are compared by their actual value. When you writeconst a = "Amr"andconst b = "Amr",a === breturnstruebecause their values are identical. - Reference values (objects, arrays, functions): These are compared by their memory reference. Even if two objects/arrays have identical content—like
const obj1 = { firstName: "Amr" }andconst obj2 = { firstName: "Amr" }—obj1 === obj2returnsfalsebecause they’re stored as separate references in memory.
In your experiment:
- When passing a string prop: The parent component re-renders, but the string value stays the same (same primitive value), so
React.memo()’s default shallow comparison detects no change, and the child doesn’t re-render. - When passing an object/array prop: Every time the parent re-renders, you create a new object (
const name = { firstName: "Amr" }) with a fresh memory reference. Even though the content is identical,React.memo()’s default check looks for matching references (they don’t match), so the child re-renders.
Your custom areEqual function confirms this perfectly: it logs "not equal" for objects/arrays because the references differ, even when their content matches.
Is React.memo() Useless? Absolutely Not!
Your confusion here is totally understandable, but React.memo() is a powerful performance optimization tool—you just need to use it correctly with reference values. Here are common fixes to make it work as intended:
1. Cache Reference Values with useMemo
Wrap your object/array in useMemo to preserve its reference between parent re-renders, unless specific dependencies change:
import { useMemo } from "react"; import Child from "./Child"; export default function App() { // Only creates a new object if dependencies (empty array here) change const name = useMemo(() => ({ firstName: "Amr" }), []); return <Child name={name} />; }
Now, the name object’s reference stays consistent across parent re-renders, so React.memo() will skip child re-renders unless the object’s content actually changes.
2. Customize the areEqual Comparison
Instead of comparing references, compare the actual content of the props. For your example, update your areEqual function to check the object’s value:
function areEqual(prevProps, nextProps) { // Compare the content instead of references return prevProps.name.firstName === nextProps.name.firstName; }
Now, even if the parent passes a new object reference, as long as the firstName value is the same, React.memo() will skip the child re-render.
3. Split Reference Values into Primitive Props
If possible, pass individual primitive values instead of entire objects/arrays. This lets React.memo()’s default comparison work without extra setup:
// Parent component export default function App() { const firstName = "Amr"; return <Child firstName={firstName} />; } // Child component const Child = ({ firstName }) => { return <h2>{firstName}</h2>; }; export default React.memo(Child);
When to Use React.memo()
It’s most valuable when:
- Your child component is expensive to render (e.g., complex data visualizations, long lists, or components with heavy calculations).
- The component receives props that don’t change frequently.
For simple components (like your <h2> example), the performance gain might be negligible, but it’s harmless to use React.memo() anyway.
Final Check
Your core understanding of primitive vs. reference values driving React.memo()’s behavior is 100% correct. The "issue" you noticed is just how JavaScript works—React.memo() doesn’t fail here; it’s following the language’s rules. With the fixes above, you can leverage it to optimize your app’s performance effectively.
内容的提问来源于stack exchange,提问作者Amr Khaled

