成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

UseState與UseReducer性能居然有區別?

開發 前端
UseState與UseReducer性能應該完全一致才對。但實際上,他們的性能并不一樣。本文就來聊聊他們的細微差別。

大家好,我卡頌。

稍微深入了解過useState的同學都知道 —— useState其實是預置了reducer的useReducer。具體來講,他預置的reducer實現如下:

function basicStateReducer(state, action) {
// $FlowFixMe: Flow doesn't like mixed types
return typeof action === 'function' ? action(state) : action;
}

那按理來說,useState與useReducer性能應該完全一致才對。但實際上,他們的性能并不一樣。本文就來聊聊他們的細微差別。

一個嚴重的bug

在v18之前,特定場景下,useReducer存在一個嚴重的bug。假設我們要掛載如下App組件:

bug復現地址[1]

function App() {
const [disabled, setDisabled] = React.useState(false);
return (
<>
<button onClick={() => setDisabled((prev) => !prev)}>Disable</button>
<div>{`Disabled? ${disabled}`}</div>
<CounterReducer disabled={disabled} />
</>
);
}

通過點擊按鈕,可以切換disabled狀態,并將disabled作為props傳遞給CounterReducer組件。

CounterReducer組件的實現如下:

function CounterReducer({ disabled }) {
const [count, dispatch] = useReducer((state) => {
if (disabled) {
return state;
}
return state + 1;
}, 0);
return (
<>
<button onClick={dispatch}>reducer + 1</button>
<div>{`Count ${count}`}</div>
</>
);
}

count?狀態初始為0,當disabled props為true?時,點擊「reducer + 1按鈕」后count不會變化。

圖片

disabled為true時,多次點擊后count仍顯示0

當disabled props為false?時,點擊「reducer + 1按鈕」后count會加1。

圖片

disabled為false時,點擊后count加1

現在問題來了,當disabled props為true?時(此時count?為0),我們點擊「reducer + 1按鈕」5次,然后再點擊「Disable按鈕」(disabled props?會變為false?),此時count為多少呢?

按照代碼邏輯,改變disabled對count不會造成影響,所以他應該保持原始狀態不變(即為0)。

圖片

但在v18之前,他會變成5。

圖片

但是,如果我們用useState實現同樣邏輯的useReducer:

function CounterState({ disabled }) {
const [count, dispatch] = useState(0);

function dispatchAction() {
dispatch((state) => {
if (disabled) {
return state;
}
return state + 1;
});
}

return (
<>
<button onClick={dispatchAction}>state + 1</button>
<div>{`Count ${count}`}</div>
</>
);
}

就能取得符合預期的效果。

所以說,useReducer的實現在特殊場景下是有bug的(v18之前)。

bug是如何產生的

產生這個bug的原因在于React內部的一種被稱為eager state的性能優化策略。

簡單的說,對于類似如下這樣的,即使多次觸發更新,但狀態的最終結果不變的情況(在如下例子中??count??始終為0):

function App() {
const [count, dispatch] = useState(0);
return <button onClick={() => dispatch(0)}>點擊</button>;
}

App組件是沒有必要render的。這就省去了render的性能開銷。

要命中eager state,有個嚴格的前提 —— 狀態更新前后不變。

我們知道,React中有兩種更新狀態的方式:

  1. 傳遞新的狀態。
// 定義狀態
const [count, dispatch] = useState(0);

// 更新狀態
dispatch(100)
  1. 傳遞更新狀態的函數。
// 定義狀態
const [count, dispatch] = useState(0);

// 更新狀態
dispatch(oldState => oldState + 100)

那么,對于方式1,要保證狀態不變很簡單,只需要全等比較變化前后的狀態,如果他們一致就能進入eager state策略。

對于方式2,就略微復雜點,需要同時滿足2個條件:

  1. 「狀態更新函數」本身不變。
  2. 通過「狀態更新函數」計算出的新狀態也不變。

比如,下述代碼就同時滿足2個條件,但如果將change放到App內就不滿足條件1(App組件每次render時都會創建新的change函數):

// 狀態更新函數本身不變
function change(oldState) {
// 新狀態也不變
return oldState;
}

function App() {
const [count, dispatch] = useState(0);

// 狀態更新函數每次render都會變化
// function change(oldState) {
// 新狀態不變
// return oldState;
// }

return <button onClick={() => dispatch(change)}>點擊</button>;
}

類似的情況,在useState的實現中,雖然他是預置了reducer的useReducer,但他預置的reducer的引用是不變的,所以用他實現的文章開篇的例子可以命中優化策略。

useReducer在特定場景下的bug就與此相關。并不是說bug產生的原因是useReducer一定沒命中優化策略,而是說相比于useState,他命中優化策略很不穩定。

v18之后的改變

既然bug?來源于不穩定的性能優化策略,在沒有完美的解決方案之前,React?是如何在v18?中修復這個bug的呢?

答案是 —— 移除useReducer?的eager state?策略。也就是說,在任何情況下,useReducer?都不再有useState存在的這個性能優化策略了。

這就導致在特定場景下,useReducer?的性能弱于useState。

比如在這個v18在線示例[2]中,同樣的邏輯用useState?實現,不會有冗余的render?,而useReducer會有。

總結

在考慮性能優化時,如果useState與useReducer都能滿足需要,或許useState是更好的選擇。

參考資料

[1]bug復現地址:https://codesandbox.io/s/vigorous-dhawan-mqv463。

[2]v18在線示例:https://codesandbox.io/s/blazing-cdn-pzcpz6?file=/src/App.js:509-519。

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2015-07-30 09:20:26

微軟Android Lau

2022-03-18 14:09:52

ReactJavaScript

2019-08-09 15:07:33

TomcatJaegerSpringBoot

2022-03-29 20:10:27

React狀態管理

2020-08-17 09:22:30

字符串子串對象

2020-12-17 10:23:41

死鎖LinuxLockdep

2025-02-28 09:30:00

?DeepSeekDeepGEMMAI

2024-03-18 09:24:12

RocketMQ消息模型分布式

2021-08-03 22:26:46

Go函數分頁

2023-05-25 10:03:40

2022-01-04 09:24:32

Python Excel 表格

2024-06-14 10:26:30

2021-06-25 10:04:47

DevOpsDevSecOps開發

2021-07-12 10:18:35

互聯網數據代碼

2019-02-12 11:07:49

2021-10-08 18:33:05

微信后臺移動應用

2024-01-09 16:14:39

RustGo切片

2020-12-22 13:46:48

APISKD

2011-08-08 14:09:55

dhcpbootp

2020-09-14 09:01:43

VMware vSANSAN網絡虛擬化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美涩| 密室大逃脱第六季大神版在线观看 | 黄色网址在线播放 | 久久久www成人免费精品张筱雨 | 色网在线看 | 日韩av看片 | 国产亚洲精品精品国产亚洲综合 | 久产久精国产品 | 久久综合狠狠综合久久 | 亚洲精品久久久久久下一站 | 久久人体| 日本三级电影免费观看 | 精品国产一二三区 | 国外成人在线视频 | 成人在线国产 | 欧美中文一区 | 欧美一区二区三区 | 国产欧美视频一区二区 | 成人免费在线视频 | 成人免费淫片aa视频免费 | 成人av高清在线观看 | 国产中文字幕在线 | 在线观看av不卡 | 久久精品一区二区三区四区 | 91精品国产91久久久久久吃药 | 国产精品日本一区二区在线播放 | 国产一区日韩在线 | 免费一区 | 国产激情精品 | 亚洲资源在线 | 99精品福利视频 | 亚洲国产成人精品女人久久久 | 国产一级在线 | 欧美一区不卡 | 粉嫩av久久一区二区三区 | 在线亚洲免费视频 | 欧美爱爱视频网站 | 亚洲精品99 | 午夜国产| 国产午夜在线 | 成人在线视 |