Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
React.memo 如何使用浅比较跳过未变更子组件的渲染。
默认情况下,每当父组件重新渲染时,React 都会重新渲染子组件 — 无论子组件自身的 props 是否发生变化。React.memo 是一个高阶组件,它包裹函数组件并记忆化其渲染输出。它对新旧 props 进行浅比较,如果相等则跳过重新渲染。
当父组件的 state 变化时,React 调用父组件函数,然后处理所有返回的 JSX 子组件 — 即使它们的 props 完全相同也会调用其函数。这种情况沿树向下级联。
React.memo(Component) 返回一个新组件。在调用 Component 函数之前,React 检查新 props 是否与上一次 props 浅相等。如果是,React 复用上一次的渲染输出 — 完全跳过函数调用。
对于每个 prop 键,React 比较 oldProp === newProp。基础类型(字符串、数字、布尔值)按值比较。对象和函数按引用比较 — props 中的新对象字面量 '{''}' 总会使相等性检查失败。
React.memo(Component, arePropsEqual) 接受第二个参数 — 一个自定义比较函数。返回 true 跳过重新渲染(props 相等),返回 false 允许重新渲染。适用于深度相等性检查或忽略特定 props。
React.memo 只跳过由 prop 变化引起的重新渲染。如果组件调用了 useContext(),当 context 值变化时它仍会重新渲染 — 无论 React.memo 如何。
记忆化组件渲染输出的 HOC。当 props 与上一次渲染浅相等时跳过重新渲染。
使用 Object.is() 比较每个 prop。对基础类型有效。对象和函数必须在渲染之间保持相同引用。
当 React.memo 确定 props 相等时,返回上一次渲染输出并跳过组件函数的调用。
在渲染之间保持相同的对象/函数引用。React.memo 与非基础类型 props 正确工作的必要条件。
输出与上一次渲染完全相同的重新渲染。当 props 在逻辑上没有变化时,React.memo 可以防止这种情况。
1// Without memo — re-renders every time parent renders2function ExpensiveList({ items }) {3 return items.map(item => <Item key={item.id} {...item} />);4}56// With memo — skips re-render if items reference is stable7const ExpensiveList = React.memo(function ExpensiveList({ items }) {8 return items.map(item => <Item key={item.id} {...item} />);9});1011function Parent() {12 const [count, setCount] = useState(0);1314 // ❌ New array every render — memo never helps15 const items = [{ id: 1, name: 'Apple' }];1617 // ✅ Stable reference — memo works as expected18 const items = useMemo(() => [{ id: 1, name: 'Apple' }], []);1920 return (21 <>22 <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>23 <ExpensiveList items={items} /> {/* skips re-render when only count changes */}24 </>25 );26}
在拥有复杂组件的大型 React 树中,不必要的重新渲染积累成真实的性能问题 — 交互缓慢、丢帧、动画卡顿。React.memo 让你有针对性地防止特定组件重新渲染,在应用其余部分自由更新时保持昂贵子树的稳定。
A Slack-like app renders a message list. When a user types in the input box, the entire list of 1000+ message components re-renders because the parent (ChatRoom) updates on every keystroke.
Each keystroke updates the parent's `inputValue` state, triggering a re-render. Without React.memo, all 1000 Message components re-render even though none of their props changed. Each Message component does string formatting and date calculations — the combined cost creates visible lag.
Wrap Message in React.memo: `const Message = React.memo(({ text, author, timestamp }) => { ... })`. Since the message props are primitive values (strings, numbers), React.memo's shallow comparison correctly identifies that nothing changed. The 1000 messages are skipped entirely — only the input re-renders.
Takeaway: React.memo shines when a parent re-renders frequently but a child's props rarely change. The ideal candidates are list items, complex charts, and any component with expensive rendering where the parent updates for unrelated reasons.
A MapWidget receives a `coordinates` object `{ lat: 40.7, lng: -74.0 }`. Despite wrapping in React.memo, it still re-renders because the parent creates a new coordinates object each render.
React.memo uses shallow comparison by default: `oldCoords !== newCoords` is always true because each render creates a new object, even if lat/lng values are identical. Shallow comparison checks reference equality, not value equality.
Provide a custom comparison: `React.memo(MapWidget, (prev, next) => prev.coordinates.lat === next.coordinates.lat && prev.coordinates.lng === next.coordinates.lng)`. Now React.memo compares the actual values instead of the object reference. Alternatively, flatten the props: `<MapWidget lat={40.7} lng={-74.0} />` — primitives compare by value automatically.
Takeaway: Use custom areEqual functions for components that receive objects or arrays as props and can't be easily memoized upstream. But first, consider if you can flatten the props to primitives or memoize the object with useMemo in the parent — these are simpler and more maintainable.