本文參考自 Dan Abramov 的文章 Before You memo()(Overreacted),並整理成學習筆記。
在 React 中,當我們發現某個畫面效能不好時,第一個直覺常常是「加上 React.memo
就能解決」。
但事實上,memo
並不是最佳的第一步。這篇文章要教我們在「急著用 memo 前」應該先檢查的幾件事。
Dan 提出兩個比 memo
更基礎的思考方向:
Move State Down(把 state 往下移)
Lift Content Up(把不會變的 UI 提出去)
換句話說,在用 memo
前,應該先想想:是不是因為 state 放錯地方或 UI 沒有切分好,導致不必要的重新渲染?
function App() {
const [color, setColor] = useState("red");
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p style={{ color }}>Hello, world!</p>
<ExpensiveTree /> {/* 很重的 component */}
</div>
);
}
這裡的問題是:每次 color
改變,整個 App
都要 re-render,包括 ExpensiveTree
,即使它根本沒用到 color
。
function App() {
return (
<>
<ColorForm />
<ExpensiveTree />
</>
);
}
function ColorForm() {
const [color, setColor] = useState("red");
return (
<>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p style={{ color }}>Hello, world!</p>
</>
);
}
現在,只有 ColorForm
會重新渲染,ExpensiveTree
不受影響。
function App() {
return (
<ColorPicker>
<ExpensiveTree />
</ColorPicker>
);
}
function ColorPicker({ children }) {
const [color, setColor] = useState("red");
return (
<div style={{ color }}>
<input value={color} onChange={(e) => setColor(e.target.value)} />
{children}
</div>
);
}
只要 children
本身不依賴 color
,就不會因為 color
變動而被重新渲染。
中文
我不會一開始就用 React.memo。
在加上 memo 之前,我會先調整元件結構,像是把 state 往下移,或者把穩定的 UI 往上抽出來。
另外,React.memo 本身也有一些缺點:
- 它需要比較 props,這本身也有開銷。
- 如果 props 是物件或 callback,每次 render 都產生新參考,就會讓 memo 失效。
- 過度使用 memo 會讓程式碼更難閱讀與維護。
所以我只會在 真的很昂貴、而且 props 穩定的元件 上使用 React.memo。
英文
I don’t use
React.memo
as the first step.
Before adding memo, I restructure components by moving state down or lifting stable UI up.
Also,React.memo
itself has trade-offs:
- It still has a cost because React needs to compare props.
- It can be broken if props are objects or callbacks that change every render.
- Too much memo makes code harder to read and maintain.
So I use it only for expensive components where props are stable.
React.memo
不是效能優化的第一步。memo
會更有效率。