參考文章
把一系列 state 更新加入隊列
設(shè)置組件 state 會把一次重新渲染加入隊列。但有時可能會希望在下次渲染加入隊列之前對 state 的值執(zhí)行多次操作。為此,了解 React 如何批量更新 state 會很有幫助。
React 會對 state 更新進行批處理
在下面的示例中,可能會認為點擊 “+3” 按鈕會使計數(shù)器遞增三次,因為它調(diào)用了 setNumber(number + 1)
三次:
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
但是,每一次渲染的 state 值都是固定的,因此無論調(diào)用多少次 setNumber(1)
,在第一次渲染的事件處理函數(shù)內(nèi)部的 number
值總是 0
:
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
但是這里還有另外一個影響因素需要討論。React 會等到事件處理函數(shù)中的 所有 代碼都運行完畢再處理 state 更新。 這就是為什么重新渲染只會發(fā)生在所有這些 setNumber()
調(diào)用 之后 的原因。
這讓你可以更新多個 state 變量——甚至來自多個組件的 state 變量——而不會觸發(fā)太多的 重新渲染。但這也意味著只有在事件處理函數(shù)及其中任何代碼執(zhí)行完成 之后,UI 才會更新。這種特性也就是 批處理,它會使 React 應(yīng)用運行得更快。它還會幫你避免處理只更新了一部分 state 變量的令人困惑的“半成品”渲染。
React 不會跨 多個 需要刻意觸發(fā)的事件(如點擊)進行批處理——每次點擊都是單獨處理的。請放心,React 只會在一般來說安全的情況下才進行批處理。這可以確保,例如,如果第一次點擊按鈕會禁用表單,那么第二次點擊就不會再次提交它。
在下次渲染前多次更新同一個 state
這是一個不常見的用例,但是如果想在下次渲染之前多次更新同一個 state,可以像 setNumber(n => n + 1)
這樣傳入一個根據(jù)隊列中的前一個 state 計算下一個 state 的 函數(shù),而不是像 setNumber(number + 1)
這樣傳入 下一個 state 值。這是一種告訴 React “用 state 值做某事”而不是僅僅替換它的方法。
現(xiàn)在嘗試遞增計數(shù)器:
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
在這里,n => n + 1
被稱為 更新函數(shù)。當將它傳遞給一個 state 設(shè)置函數(shù)時:
- React 會將此函數(shù)加入隊列,以便在事件處理函數(shù)中的所有其他代碼運行后進行處理。
- 在下一次渲染期間,React 會遍歷隊列并給你更新之后的最終 state。
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
下面是 React 在執(zhí)行事件處理函數(shù)時處理這幾行代碼的過程:
-
setNumber(n => n + 1)
:n => n + 1
是一個函數(shù)。React 將它加入隊列。 -
setNumber(n => n + 1)
:n => n + 1
是一個函數(shù)。React 將它加入隊列。 -
setNumber(n => n + 1)
:n => n + 1
是一個函數(shù)。React 將它加入隊列。
當在下次渲染期間調(diào)用 useState
時,React 會遍歷隊列。之前的 number
state 的值是 0
,所以這就是 React 作為參數(shù) n
傳遞給第一個更新函數(shù)的值。然后 React 會獲取上一個更新函數(shù)的返回值,并將其作為 n
傳遞給下一個更新函數(shù),以此類推:
更新隊列 | n |
返回值 |
---|---|---|
n => n + 1 |
0 |
0 + 1 = 1 |
n => n + 1 |
1 |
1 + 1 = 2 |
n => n + 1 |
2 |
2 + 1 = 3 |
React 會保存 3
為最終結(jié)果并從 useState
中返回。
這就是為什么在上面的示例中點擊“+3”正確地將值增加“+3”
如果在替換 state 后更新 state 會發(fā)生什么
這個事件處理函數(shù)會怎么樣?你認為 number
在下一次渲染中的值是什么?
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>增加數(shù)字</button>
</>
)
}
這是事件處理函數(shù)告訴 React 要做的事情:
-
setNumber(number + 5)
:number
為0
,所以setNumber(0 + 5)
。React 將 “替換為5
” 添加到其隊列中。 -
setNumber(n => n + 1)
:n => n + 1
是一個更新函數(shù)。 React 將 該函數(shù) 添加到其隊列中。
在下一次渲染期間,React 會遍歷 state 隊列:
更新隊列 | n |
返回值 |
---|---|---|
“替換為 5 ” |
0 (未使用) |
5 |
n => n + 1 |
5 |
5 + 1 = 6 |
React 會保存 6
為最終結(jié)果并從 useState
中返回。
注意:setState(x)
實際上會像 setState(n => x)
一樣運行,只是沒有使用 n
!
如果在更新 state 后替換 state 會發(fā)生什么
讓我們再看一個例子。你認為 number
在下一次渲染中的值是什么?
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>增加數(shù)字</button>
</>
)
}
以下是 React 在執(zhí)行事件處理函數(shù)時處理這幾行代碼的過程:
-
setNumber(number + 5)
:number
為0
,所以setNumber(0 + 5)
。React 將 “替換為5
” 添加到其隊列中。 -
setNumber(n => n + 1)
:n => n + 1
是一個更新函數(shù)。React 將該函數(shù)添加到其隊列中。 -
setNumber(42)
:React 將 “替換為42
” 添加到其隊列中。
在下一次渲染期間,React 會遍歷 state 隊列:
更新隊列 | n |
返回值 |
---|---|---|
“替換為 5 ” |
0 (未使用) |
5 |
n => n + 1 |
5 |
5 + 1 = 6 |
“替換為 42 ” |
6 (未使用) |
42 |
然后 React 會保存 42
為最終結(jié)果并從 useState
中返回。
總而言之,以下是可以考慮傳遞給 setNumber
state 設(shè)置函數(shù)的內(nèi)容:
-
一個更新函數(shù)(例如:
n => n + 1
)會被添加到隊列中。 -
任何其他的值(例如:數(shù)字
5
)會導(dǎo)致“替換為5
”被添加到隊列中,已經(jīng)在隊列中的內(nèi)容會被忽略。
事件處理函數(shù)執(zhí)行完成后,React 將觸發(fā)重新渲染。在重新渲染期間,React 將處理隊列。更新函數(shù)會在渲染期間執(zhí)行,因此 更新函數(shù)必須是 純函數(shù) 并且只 返回 結(jié)果。不要嘗試從它們內(nèi)部設(shè)置 state 或者執(zhí)行其他副作用。在嚴格模式下,React 會執(zhí)行每個更新函數(shù)兩次(但是丟棄第二個結(jié)果)以便幫助你發(fā)現(xiàn)錯誤。
命名慣例
通??梢酝ㄟ^相應(yīng) state 變量的第一個字母來命名更新函數(shù)的參數(shù):文章來源:http://www.zghlxwxcb.cn/news/detail-645465.html
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);
如果你喜歡更冗長的代碼,另一個常見的慣例是重復(fù)使用完整的 state 變量名稱,如 setEnabled(enabled => !enabled)
,或使用前綴,如 setEnabled(prevEnabled => !prevEnabled)
。文章來源地址http://www.zghlxwxcb.cn/news/detail-645465.html
摘要
- 設(shè)置 state 不會更改現(xiàn)有渲染中的變量,但會請求一次新的渲染。
- React 會在事件處理函數(shù)執(zhí)行完成之后處理 state 更新。這被稱為批處理。
- 要在一個事件中多次更新某些 state,可以使用
setNumber(n => n + 1)
更新函數(shù)。
到了這里,關(guān)于React 18 state 狀態(tài)更新函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!