Jeff的隨手筆記

學習當一個前端工程師

0%

『Day 13』React State 的快照特性

前言

在剛開始接觸 React 的時候,我常常被 State 的一些特性搞得一頭霧水,特別是一開始當我把它當作普通的 JavaScript 變數使用時,總會遇到一些意想不到的情況,例如:當你想要連續更新好幾次 State 時,卻發現畫面的更新跟你預期的不太一樣?
這是因為 React 的 State 有一個特別的機制,稱為「快照(Snapshot)」。今天就讓我們一起來了解這個概念吧!

什麼是 Snapshot?

可以把 Snapshot(快照) 想像成 React 在某個時間點記錄下的 State 的靜態畫面。每次渲染時,React 都會創建一個快照來保存當前的元件狀態,而這個快照是 不可改變的(Immutable)

在同一次渲染中,這個快照中的 State 值是不會隨著 setState 的呼叫而改變的,只有在下一次渲染時,快照才會更新為最新的狀態。

假設我們有以下程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Counter() {
const [count, setCount] = useState(0);

return (
<>
<h1>{count}</h1>
<button onClick={() => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}}>點我加三</button>
</>
);
}

當我們點擊按鈕時,你可能會期待 count 會加 3,但實際上它只會加 1!這是因為:

  1. 在點擊按鈕時,React 提供了一個當下的快照,此時 count 是 0

  2. 這三次的 setCount 都是基於這個快照在運作:

    1
    2
    3
    setCount(0 + 1)// 基於快照中的 0
    setCount(0 + 1)// 還是基於快照中的 0
    setCount(0 + 1)// 依然是基於快照中的 0
  3. 所以這三次 setCount 其實都是告訴 React:「把 count 設定為 1」

  4. React 在下一次渲染時,就會使用最後收到的值,也就是 1

這就像是在拍照一樣,不管你在快門按下後做了什麼動作,照片裡永遠都是按下快門那一刻的畫面。在這裡的快照也是一樣,在這次渲染中,count 的值就固定是 0,不會因為呼叫了 setCount 就立即改變。

React 的渲染機制與快照

React 的渲染機制可以分為以下幾個步驟:

  1. 調用元件函式,生成新的 JSX 結構。
  2. 比較新舊的 JSX 結構,計算需要更新的部分。
  3. 更新畫面,並生成新的 State 快照。

每次渲染時,React 都會創建一個全新的快照,這個快照包含:

  • 當前的 State 值
  • 事件處理函式
  • 當前渲染中使用到的其他變數

非同步操作與 Snapshot

快照特性在非同步操作中也非常重要。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function MessageForm() {
const [message, setMessage] = useState('哈囉');

return (
<>
<input
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button onClick={() => {
setTimeout(() => {
alert(`要傳送的訊息:${message}`);
}, 3000);
}}>延遲傳送</button>
</>
);
}

當我們點擊「延遲傳送」按鈕後:

  1. React 會記住當下的 message 值
  2. 即使我們在這 3 秒內修改了輸入框的內容
  3. 3 秒後跳出的 alert 視窗還是會顯示「哈囉」

這是因為事件處理函式「記住」了它創建時的快照,而非動態追蹤最新的 State 值。

總結

React 中的 state 是由 React 核心統一管理的狀態快照,每次 setState 不是直接修改值,而是觸發重新渲染,產生新的快照,並且在同一次渲染中 state 值保持不變,這樣的設計讓狀態管理更可預測且容易追蹤。