DrillLab
第 67 / 105 道67 / 105 · #336

什么是 Pure Component

What are Pure Component

先自己答,再往下看Answer it yourself first

一句话:React.PureComponent自带一个浅比较shouldComponentUpdate—— props 和 state 浅比较都没变就跳过渲染。 函数组件的对应物是React.memo

「浅比较」是本题全部的考点。它对每个 prop 用Object.is 比一层。 所以:

  • 传对象/数组/函数字面量 → 优化完全失效, 因为每次渲染都是新引用。
  • 内部深层改了对象 → 不会重渲染, 因为引用没变 —— 界面就不更新了。

所以 PureComponent /memo 和「不可变更新」是一对: 你必须每次造新对象, 浅比较才能正确地判断出「变了」。反过来,如果你就地改对象, 加了 memo 反而会制造 bug。

会追问:「怎么让 memo 真正生效?」—— 对象和数组用 useMemo、 函数用 useCallback稳住引用。三个必须配套用, 只加 memo 往往一点用没有 (见 #346)。
「是不是所有组件都该 memo?」—— 不是。浅比较本身也有成本,props 多且经常变的组件, 加了反而更慢

In one line: React.PureComponent ships with a shallow-comparison shouldComponentUpdate — if a shallow compare of props and state finds nothing changed, it skips the render. The function-component equivalent is React.memo.

The shallow compare is the whole point of this question. It runs Object.is on each prop, one level deep. So:

  • Pass an object, array or function literal and the optimisation is dead, because every render creates a new reference.
  • Mutate something deep inside an object and it will not re-render, because the reference never changed — and the UI just stops updating.

So PureComponent / memo and immutable updates come as a pair: you have to build a new object every time for the shallow compare to see a change. The reverse holds too — mutate in place and adding memo manufactures bugs.

Follow-up: “How do you make memo actually work?” — stabilise the references: useMemo for objects and arrays, useCallback for functions. The three go together; memo on its own often does nothing at all (see #346).
“Should every component be memoised?” — no. The shallow compare costs something too, so a component with many frequently changing props gets slower, not faster.

JSXmemo 生效的前提示意Illustrative
1const Row = React.memo(function Row({ item, onPick }) {});
2
3// ✗ memo 白加:两个 prop 每次都是新引用
4<Row item={{ ...raw }} onPick={() => pick(raw.id)} />
5
6// ✓ 稳住引用,memo 才有意义
7const item = useMemo(() => ({ ...raw }), [raw]);
8const onPick = useCallback((id) => pick(id), [pick]);
9<Row item={item} onPick={onPick} />
1const Row = React.memo(function Row({ item, onPick }) {});
2
3// ✗ memo achieves nothing: both props are a new reference every render
4<Row item={{ ...raw }} onPick={() => pick(raw.id)} />
5
6// ✓ keep the references stable, and memo starts to mean something
7const item = useMemo(() => ({ ...raw }), [raw]);
8const onPick = useCallback((id) => pick(id), [pick]);
9<Row item={item} onPick={onPick} />