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

關于useState的一切

開發 前端
通過本文,我們了解了useState的完整執行過程。一起來看一下吧。

作為React開發者,你能答上如下兩個問題么:

    1.  對于如下函數組件: 

  1. function App() {  
  2.   const [num, updateNum] = useState(0);  
  3.   window.updateNum = updateNum; 
  4.   return num;  

調用window.updateNum(1)可以將視圖中的0更新為1么?

    2.  對于如下函數組件: 

  1. function App() {  
  2.   const [num, updateNum] = useState(0);  
  3.   function increment() {  
  4.     setTimeout(() => {  
  5.       updateNum(num + 1);  
  6.     }, 1000);  
  7.   }  
  8.   return <p onClick={increment}>{num}</p> 

在1秒內快速點擊p5次,視圖上顯示為幾? 

  1. 👉向右滑動展示答案                                             1. 可以  
  2.                                                             2. 顯示為1 

其實,這兩個問題本質上是在問:

  •  useState如何保存狀態?
  •  useState如何更新狀態?

本文會結合源碼,講透如上兩個問題。

這些,就是你需要了解的關于useState的一切。

hook如何保存數據

FunctionComponent的render本身只是函數調用。

那么在render內部調用的hook是如何獲取到對應數據呢?

比如:

  •  useState獲取state
  •  useRef獲取ref
  •  useMemo獲取緩存的數據

答案是:

每個組件有個對應的fiber節點(可以理解為虛擬DOM),用于保存組件相關信息。

每次FunctionComponent render時,全局變量currentlyRenderingFiber都會被賦值為該FunctionComponent對應的fiber節點。

所以,hook內部其實是從currentlyRenderingFiber中獲取狀態信息的。

多個hook如何獲取數據

我們知道,一個FunctionComponent中可能存在多個hook,比如: 

  1. function App() {  
  2.   // hookA  
  3.   const [a, updateA] = useState(0);  
  4.   // hookB 
  5.   const [b, updateB] = useState(0);  
  6.   // hookC  
  7.   const ref = useRef(0); 
  8.    return <p></p> 

那么多個hook如何獲取自己的數據呢?

答案是:

currentlyRenderingFiber.memoizedState中保存一條hook對應數據的單向鏈表。

對于如上例子,可以理解為: 

  1. const hookA = {  
  2.   // hook保存的數據  
  3.   memoizedState: null,  
  4.   // 指向下一個hook  
  5.   next: hookB  
  6.   // ...省略其他字段  
  7. }; 
  8. hookB.next = hookC 
  9. currentlyRenderingFiber.memoizedState = hookA

當FunctionComponent render時,每執行到一個hook,都會將指向currentlyRenderingFiber.memoizedState鏈表的指針向后移動一次,指向當前hook對應數據。

這也是為什么React要求hook的調用順序不能改變(不能在條件語句中使用hook) —— 每次render時都是從一條固定順序的鏈表中獲取hook對應數據的。

useState執行流程

我們知道,useState返回值數組第二個參數為改變state的方法。

在源碼中,他被稱為dispatchAction。

每當調用dispatchAction,都會創建一個代表一次更新的對象update: 

  1. const update = {  
  2.   // 更新的數據  
  3.   action: action,  
  4.   // 指向下一個更新  
  5.   next: null  
  6. }; 

對于如下例子 

  1. function App() {  
  2.   const [num, updateNum] = useState(0);  
  3.   function increment() {  
  4.     updateNum(num + 1);  
  5.   }  
  6.   return <p onClick={increment}>{num}</p> 

調用updateNum(num + 1),會創建: 

  1. const update = {  
  2.   // 更新的數據  
  3.   action: 1,  
  4.   // 指向下一個更新  
  5.   next: null  
  6.   // ...省略其他字段  
  7. }; 

如果是多次調用dispatchAction,例如: 

  1. function increment() {  
  2.   // 產生update1  
  3.   updateNum(num + 1);  
  4.   // 產生update2  
  5.   updateNum(num + 2);  
  6.   // 產生update3  
  7.   updateNum(num + 3);  }

那么,update會形成一條環狀鏈表。 

  1. update3 --next--> update1  
  2.   ^                 |  
  3.   |               update2  
  4.   |______next_______|                       

 這條鏈表保存在哪里呢?

既然這條update鏈表是由某個useState的dispatchAction產生,那么這條鏈表顯然屬于該useState hook。

我們繼續補充hook的數據結構。 

  1. const hook = {  
  2.   // hook保存的數據  
  3.   memoizedState: null,  
  4.   // 指向下一個hook  
  5.   next: hookForB  
  6.   // 本次更新以baseState為基礎計算新的state  
  7.   baseState: null,  
  8.   // 本次更新開始時已有的update隊列 
  9.    baseQueue: null,  
  10.   // 本次更新需要增加的update隊列  
  11.   queue: null,  
  12. }; 

其中,queue中保存了本次更新update的鏈表。

在計算state時,會將queue的環狀鏈表剪開掛載在baseQueue最后面,baseQueue基于baseState計算新的state。

在計算state完成后,新的state會成為memoizedState。

為什么更新不基于memoizedState而是baseState,是因為state的計算過程需要考慮優先級,可能有些update優先級不夠被跳過。所以memoizedState并不一定和baseState相同。更詳細的解釋見React技術揭秘[1]

回到我們開篇第一個問題: 

  1. function App() {  
  2.   const [num, updateNum] = useState(0);  
  3.   window.updateNum = updateNum;  
  4.   return num;  

調用window.updateNum(1)可以將視圖中的0更新為1么?

我們需要看看這里的updateNum方法的具體實現: 

  1. updateNum === dispatchAction.bind(null, currentlyRenderingFiber, queue); 

可見,updateNum方法即綁定了currentlyRenderingFiber與queue(即hook.queue)的dispatchAction。

上文已經介紹,調用dispatchAction的目的是生成update,并插入到hook.queue鏈表中。

既然queue作為預置參數已經綁定給dispatchAction,那么調用dispatchAction就步僅局限在FunctionComponent內部了。

update的action

第二個問題 

  1. function App() {  
  2.   const [num, updateNum] = useState(0); 
  3.    function increment() {  
  4.     setTimeout(() => {  
  5.       updateNum(num + 1);  
  6.     }, 1000);  
  7.   }  
  8.   return <p onClick={increment}>{num}</p> 

在1秒內快速點擊p5次,視圖上顯示為幾?

我們知道,調用updateNum會產生update,其中傳參會成為update.action。

在1秒內點擊5次。在點擊第五次時,第一次點擊創建的update還沒進入更新流程,所以hook.baseState還未改變。

那么這5次點擊產生的update都是基于同一個baseState計算新的state,并且num變量也還未變化(即5次update.action(即num + 1)為同一個值)。

所以,最終渲染的結果為1。

useState與useReducer

那么,如何5次點擊讓視圖從1逐步變為5呢?

由以上知識我們知道,需要改變baseState或者action。

其中baseState由React的更新流程決定,我們無法控制。

但是我們可以控制action。

action不僅可以傳值,也可以傳函數。 

  1. // action為值  
  2. updateNum(num + 1);  
  3. // action為函數  
  4. updateNum(num => num + 1); 

在基于baseState與update鏈表生成新state的過程中: 

  1. let newState = baseState 
  2. let firstUpdate = hook.baseQueue.next;  
  3. let update = firstUpdate
  4. // 遍歷baseQueue中的每一個update  
  5. do {  
  6.   if (typeof update.action === 'function') {  
  7.     newState = update.action(newState);  
  8.   } else {  
  9.     newState = action 
  10.   }  
  11. } while (update !== firstUpdate) 

可見,當傳值時,由于我們5次action為同一個值,所以最終計算的newState也為同一個值。

而傳函數時,newState基于action函數計算5次,則最終得到累加的結果。

如果這個例子中,我們使用useReducer而不是useState,由于useReducer的action始終為函數,所以不會遇到我們例子中的問題。

事實上,useState本身就是預置了如下reducer的useReducer。 

  1. function basicStateReducer(state, action) {  
  2.   return typeof action === 'function' ? action(state) : action;  

總結

通過本文,我們了解了useState的完整執行過程。 

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2018-11-23 11:17:24

負載均衡分布式系統架構

2021-02-19 23:08:27

軟件測試軟件開發

2021-02-28 09:47:54

軟件架構軟件開發軟件設計

2020-10-14 08:04:28

JavaScrip

2021-05-28 07:12:59

Python閉包函數

2022-08-21 17:35:31

原子多線程

2023-04-20 10:15:57

React組件Render

2022-04-02 09:38:00

CSS3flex布局方式

2023-02-10 08:44:05

KafkaLinkedIn模式

2023-07-10 10:36:17

人工智能AI

2018-01-05 14:23:36

計算機負載均衡存儲

2021-08-09 14:40:02

物聯網IOT智能家居

2022-08-17 06:25:19

偽共享多線程

2023-04-12 14:04:48

光纖網絡

2018-01-17 09:15:52

負載均衡算法

2023-10-12 07:06:32

2020-11-17 10:38:40

云計算工具技術

2023-10-12 09:42:44

2023-02-27 15:47:31

2022-09-01 15:26:45

物聯網人工智能傳感器
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久夜夜 | 亚洲免费一区 | av日日操 | 激情综合五月 | 国内自拍视频在线观看 | 亚洲精品4 | 久久青| 欧美一区二区三区国产 | 在线播放一区二区三区 | 99久久精品国产一区二区三区 | 午夜激情视频在线 | 日韩电影一区二区三区 | 久久久久久99 | 九九在线视频 | 精品一区二区三区在线观看 | 国产精品免费看 | 精品国产一区二区三区久久影院 | 欧美一区二区三区在线播放 | .国产精品成人自产拍在线观看6 | 欧美日韩1区2区3区 欧美久久一区 | 久久九九网站 | 九九热re | 天天射美女| 亚洲国产一区二区三区 | 久久国内精品 | 在线观看黄视频 | 精品1区2区| 亚州一区二区三区 | 亚洲一区二区久久 | 欧美在线资源 | 一区二区三区四区不卡视频 | 久草资源 | 中文字幕人成乱码在线观看 | 91久久精品视频 | 亚洲一区二区精品视频在线观看 | 亚洲成人天堂 | 日本黄色大片免费看 | 日本一区二区高清不卡 | 国产在视频一区二区三区吞精 | 中文字幕三区 | 欧美在线视频一区二区 |