@@ -4,7 +4,7 @@ title: 如同快照的 State
4
4
5
5
<Intro >
6
6
7
- state 變數或許和可讀寫的普通 JavaScript 變數看起來很像。然而,state 的行為更像是一張快照(snapshot)。設定 state 並不會改變你已有的 state 變數,而是會觸發重新 render。
7
+ State 變數或許和可讀寫的普通 JavaScript 變數看起來很像。然而,state 的行為更像是一張快照(snapshot)。設定 state 並不會改變你已有的 state 變數,而是會觸發重新 render。
8
8
9
9
</Intro >
10
10
@@ -14,13 +14,15 @@ state 變數或許和可讀寫的普通 JavaScript 變數看起來很像。然
14
14
* state 更新的時機和方式
15
15
* state 在設定後並未立即更新的原因
16
16
* event handler 是如何取得 state 的「快照」
17
+
17
18
</YouWillLearn >
18
19
19
20
## 設定 state 會觸發 render {/* setting-state-triggers-renders* /}
20
21
21
22
你可能會認為使用者介面會直接對點擊等使用者事件做出改變以作為回應。在 React 裡,它的運作方式和這種思維模型有點不同。在前一章,你看過來自 React 的[ 設定 state 來請求重新 render] ( /learn/render-and-commit#step-1-trigger-a-render ) 。這意味著介面若要為特定的事件做出回應,則需要* 更新 state* 。
22
23
23
24
在此範例中,當你點擊「傳送」,` setIsSent(true) ` 會通知 React 重新 render UI:
25
+
24
26
<Sandpack >
25
27
26
28
``` js
@@ -43,7 +45,7 @@ export default function Form() {
43
45
value= {message}
44
46
onChange= {e => setMessage (e .target .value )}
45
47
/ >
46
- < button type= " submit" > 傳送 < / button>
48
+ < button type= " submit" > Send < / button>
47
49
< / form>
48
50
);
49
51
}
@@ -61,15 +63,15 @@ label, textarea { margin-bottom: 10px; display: block; }
61
63
62
64
以下是當你點擊按鈕時所發生的事情:
63
65
64
- 1 . 執行` onSubmit ` event handler。
66
+ 1 . 執行 ` onSubmit ` event handler。
65
67
2 . ` setIsSent(true) ` 將 ` isSent ` 設定為 ` true ` ,並安排新的一次 render。
66
68
3 . React 根據 ` isSent ` 新的值,重新 render component。
67
69
68
70
接著就讓我們來仔細看看 state 和 rendering 之間的關係吧!
69
71
70
72
## Rendering 會即時生成一張快照 {/* rendering-takes-a-snapshot-in-time* /}
71
73
72
- [ 「Rendering」] ( /learn/render-and-commit#step-2-react-renders-your-components ) 意味著 React 正在呼叫你的 component -- 它其實就是一個函式。函式回傳的 JSX 就像是一張 UI 的即時快照。它的 props、event handler 和區域變數都是** 利用當下 render 的 state** 計算出來的。
74
+ [ 「Rendering」] ( /learn/render-and-commit#step-2-react-renders-your-components ) 意味著 React 正在呼叫你的 component, 它其實就是一個函式。函式回傳的 JSX 就像是一張 UI 的即時快照。它的 props、event handler 和區域變數都是** 利用當下 render 的 state** 計算出來的。
73
75
74
76
與照片或電影畫面不同的是,你所回傳的 UI「快照」是具有互動性的。它包含了像是 event handler 的邏輯,明確說明要如何針對輸入做出回應。React 會更新畫面以符合這張快照,並連結 event handler。因此,按下按鈕將會觸發 JSX 裡的 click handler。
75
77
@@ -80,20 +82,21 @@ label, textarea { margin-bottom: 10px; display: block; }
80
82
3 . 接著,React 更新畫面,使畫面與你回傳的快照相符。
81
83
82
84
<IllustrationBlock sequential >
83
- <Illustration caption="React 執行函式 " src="/images/docs/illustrations/i_render1.png" />
84
- <Illustration caption="計算快照 " src="/images/docs/illustrations/i_render2.png" />
85
- <Illustration caption="更新 DOM tree" src="/images/docs/illustrations/i_render3.png" />
85
+ <Illustration caption="React executing the function " src="/images/docs/illustrations/i_render1.png" />
86
+ <Illustration caption="Calculating the snapshot " src="/images/docs/illustrations/i_render2.png" />
87
+ <Illustration caption="Updating the DOM tree" src="/images/docs/illustrations/i_render3.png" />
86
88
</IllustrationBlock >
87
89
88
- state 是 component 的記憶,它和那種函式回傳後就消失的一般變數不同。state 其實「存在於」React 本身 - 如同放在架子上!- 在函式之外。當 React 呼叫 component,它會替那一次 render 拍一張 state 快照。component 回傳的 UI 快照內的 JSX 裡有最新的 props 和 event handler,全都是** 使用那一次 render 的 state 值** 所計算出來的。
90
+ State 是 component 的記憶,它和那種函式回傳後就消失的一般變數不同。State 其實「存在於」React 本身 - 如同放在架子上!- 在函式之外。當 React 呼叫 component,它會是你特定 render 的 state 快照。Component 回傳的 UI 快照內的 JSX 裡有最新的 props 和 event handler,全都是** 使用那一次 render 的 state 值** 所計算出來的。
89
91
90
92
<IllustrationBlock sequential >
91
- <Illustration caption =" 你通知 React 更新 state" src =" /images/docs/illustrations/i_state-snapshot1.png " />
92
- <Illustration caption =" React 更新 state 值 " src =" /images/docs/illustrations/i_state-snapshot2.png " />
93
- <Illustration caption =" React 將 state 值的快照傳入 component 裡 " src =" /images/docs/illustrations/i_state-snapshot3.png " />
93
+ <Illustration caption =" You tell React to update the state" src =" /images/docs/illustrations/i_state-snapshot1.png " />
94
+ <Illustration caption =" React updates the state value " src =" /images/docs/illustrations/i_state-snapshot2.png " />
95
+ <Illustration caption =" React passes a snapshot of the state value into the component " src =" /images/docs/illustrations/i_state-snapshot3.png " />
94
96
</IllustrationBlock >
95
97
96
- 以下是一個簡單範例,用來展示其運作方式。在此範例中,你可能會預期點擊「+3」按鈕將遞增計數器三次,因為它呼叫了三次 ` setNumber(number + 1) ` 。
98
+ 以下是一個簡單範例,用來呈現其運作方式。在此範例中,你可能會預期點擊「+3」按鈕將遞增計數器三次,因為它呼叫了三次 ` setNumber(number + 1) ` 。
99
+
97
100
看看當你點擊「+3」按鈕會發生什麼事:
98
101
99
102
<Sandpack >
@@ -126,7 +129,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
126
129
127
130
注意,` number ` 在每次點擊只會遞增一次!
128
131
129
- ** 設定 state 只會為* 下一次* render 改變 state 。** 在第一次 render 中,` number ` 為 ` 0 ` 。這是為什麼在* 該次 render 的* onClick handler 中,即便在呼叫了 ` setnumber(number + 1) ` 後, ` number ` 的值仍然為 ` 0 ` 的原因:
132
+ ** 設定 state 只會為* 下一次* render 改變 state。** 在第一次 render 中,` number ` 為 ` 0 ` 。這是為什麼在* 該次 render 的* onClick handler 中,即便在呼叫了 ` setnumber(number + 1) ` 後, ` number ` 的值仍然為 ` 0 ` 的原因:
130
133
131
134
``` js
132
135
< button onClick= {() => {
@@ -145,11 +148,9 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
145
148
3 . ` setNumber(number + 1) ` : ` number ` 為 ` 0 ` ,因此 ` setNumber(0 + 1) ` 。
146
149
- React 準備在下一次 render 將 ` number ` 更改為 ` 1 ` 。
147
150
148
-
149
151
雖然呼叫了 ` setNumber(number + 1) ` 三次,在* 這一次 render 的* event handler 內的 ` number ` 一直都是 ` 0 ` ,所以等同於你把 state 設定為 ` 1 ` 三次。這就是為什麼在 event handler 執行結束後,React 用等於 ` 1 ` 而非 ` 3 ` 的 ` number ` 來重新 render component。
150
152
151
- 你也可以透過在心裡將程式碼中的 state 變數替換為它們的值來視覺化這一切。
152
- 由於在* 這一次 render* 中,state 變數 ` number ` 的值為 ` 0 ` ,它的 event handler 看起來就像是這樣:
153
+ 你也可以透過在心裡將程式碼中的 state 變數替換為它們的值來視覺化這一切。由於在* 這一次 render* 中,state 變數 ` number ` 的值為 ` 0 ` ,它的 event handler 看起來就像是這樣:
153
154
154
155
``` js
155
156
< button onClick= {() => {
@@ -169,9 +170,10 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
169
170
}}> + 3 < / button>
170
171
```
171
172
172
- 這就是為什麼再次點擊按鈕會將計數器設置為 ` 2 ` ,然後在下一次點擊時會設置為 ` 3 ` ,依此類推。
173
+ 這就是為什麼再次點擊按鈕會將計數器設定為 ` 2 ` ,然後在下一次點擊時會設定為 ` 3 ` ,依此類推。
173
174
174
175
## 隨著時間改變的 state {/* state-over-time* /}
176
+
175
177
嗯,那真是有趣。試著猜猜看點擊這個按鈕會彈出什麼提示框:
176
178
177
179
<Sandpack >
@@ -203,13 +205,12 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
203
205
204
206
如果你使用之前提到的替換法,你可以猜到提示框會顯示「0」:
205
207
206
-
207
208
``` js
208
209
setNumber (0 + 5 );
209
210
alert (0 );
210
211
```
211
212
212
- 但要是你在提示框上設置計時器 ,使其在 component 重新 render _ 之後_ 才觸發呢?那麼它會顯示「5」還是「0」?猜猜看!
213
+ 但要是你在提示框上設定計時器 ,使其在 component 重新 render _ 之後_ 才觸發呢?那麼它會顯示「5」還是「0」?猜猜看!
213
214
214
215
<Sandpack >
215
216
@@ -248,6 +249,7 @@ setTimeout(() => {
248
249
alert (0 );
249
250
}, 3000 );
250
251
```
252
+
251
253
儲存在 React 裡的 state 在提示框執行時可能已改變,但它是用使用者與其互動當下的 state 快照來安排的!
252
254
253
255
** 在同一次 render 裡,state 變數的值永遠不會改變** ,就算它的 event handler 的程式碼是非同步的。在* 該次 render 的* ` onClick ` 內,即使在呼叫 ` setNumber(number + 5) ` 之後,` number ` 的值仍然為 ` 0 ` 。當 React 透過呼叫 component 來替 UI「拍攝快照」時,state 的值「固定不變」。
@@ -291,7 +293,7 @@ export default function Form() {
291
293
value= {message}
292
294
onChange= {e => setMessage (e .target .value )}
293
295
/ >
294
- < button type= " submit" > 傳送 < / button>
296
+ < button type= " submit" > Send < / button>
295
297
< / form>
296
298
);
297
299
}
@@ -304,15 +306,18 @@ label, textarea { margin-bottom: 10px; display: block; }
304
306
</Sandpack >
305
307
306
308
** React 會使 state 值在同一次 render 內的 event handler 保持「固定不變」。** 你不需要擔心 state 在程式碼執行時有所異動。
307
- 但要是你希望在重新 render 之前讀取最新的 state 呢?你將會需要使用 [ state 的更新函數] ( /learn/queueing-a-series-of-state-updates ) ,這會下一章節中介紹!
309
+
310
+ 但要是你希望在重新 render 之前讀取最新的 state 呢?你將會需要使用 [ state 的更新函式] ( /learn/queueing-a-series-of-state-updates ) ,這會下一章節中介紹!
311
+
308
312
<Recap >
313
+
309
314
* 設定 state 會請求一次新的 render。
310
315
* React 將 state 儲存在 component 外,好比在架子上一樣。
311
316
* 當你呼叫 ` useState ` ,React 會* 為該次 render* 拍一張 state 的快照。
312
317
* 變數和 event handler 不會在重新 render 時「存活」。每次 render 都有自己的 event handler。
313
318
* 每次 render(和其內部的函式)始終會「看到」React 為* 該次* render 所提供的 state 快照。
314
319
* 你可以在內心替换 event handler 中的 state,類似於替換被 render 的 JSX。
315
- * 過去創建的 event handler 保有它們被創建的那一次 render 中的 state 值。
320
+ * 過去建立的 event handler 保有它們被建立的那一次 render 中的 state 值。
316
321
317
322
</Recap >
318
323
@@ -321,6 +326,7 @@ label, textarea { margin-bottom: 10px; display: block; }
321
326
<Challenges >
322
327
323
328
#### 實作紅綠燈 {/* implement-a-traffic-light* /}
329
+
324
330
以下是一個紅綠燈 component,按按鈕可以切換它的狀態:
325
331
326
332
<Sandpack >
@@ -356,13 +362,14 @@ h1 { margin-top: 20px; }
356
362
357
363
</Sandpack >
358
364
359
- 請在 click handler 裡添加一個 ` alert ` 。當燈是綠色的並顯示「Walk」時,點擊按鈕應顯示「Stop is next」。當燈是紅色的並顯示「Stop」時,點擊按鈕應顯示「Walk is next」。
365
+ 請在 click handler 裡加入一個 ` alert ` 。當燈是綠色的並顯示「Walk」時,點擊按鈕應顯示「Stop is next」。當燈是紅色的並顯示「Stop」時,點擊按鈕應顯示「Walk is next」。
360
366
361
367
無論你將 ` alert ` 放在呼叫 ` setWalk ` 之前還是之後,是否會有不同呢?
362
368
363
369
<Solution >
364
370
365
371
` alert ` 看起來應該像這樣:
372
+
366
373
<Sandpack >
367
374
368
375
``` js
@@ -422,6 +429,7 @@ alert(walk ? 'Stop is next' : 'Walk is next');
422
429
```
423
430
424
431
因此,點擊「Change to Stop」時,會安排一次把 ` walk ` 設定為 ` false ` 的 render,並跳出「Stop is next」的提示框。
432
+
425
433
</Solution >
426
434
427
- </Challenges >
435
+ </Challenges >
0 commit comments