成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

淺談reacthooks閉包陷阱

3403771864 / 417人閱讀

  本文不會(huì)過多講解基礎(chǔ)知識(shí),更多說的是在使用useRef如何能擺脫 這個(gè) “閉包陷阱” ? 

  react hooks 的“閉包陷阱” 基本每個(gè)開發(fā)員都有遇見,這是很令人抓狂的。

  (以下react示范demo,均為react 16.8.3 版本)

  列一個(gè)具體的場(chǎng)景:

  function App(){
  const [count, setCount] = useState(1);
  useEffect(()=>{
  setInterval(()=>{
  console.log(count)
  }, 1000)
  }, [])
  }

  其實(shí)結(jié)果都回事1。不相信就可以看。咋樣是不是很懵。我們先淺談一些hooks其他的特性。

  1、一個(gè)熟悉的閉包場(chǎng)景

  首先從一個(gè)各位jser都很熟悉的場(chǎng)景入手。

  for ( var i=0; i<5; i++ ) {
  setTimeout(()=>{
  console.log(i)
  }, 0)
  }

   這樣打印的都是5的原因了?,F(xiàn)在就直接貼出使用閉包打印 0...4的代碼:

  for ( var i=0; i<5; i++ ) {
  (function(i){
  setTimeout(()=>{
  console.log(i)
  }, 0)
  })(i)
  }

  這個(gè)原理其實(shí)就是使用閉包,定時(shí)器的回調(diào)函數(shù)去引用立即執(zhí)行函數(shù)里定義的變量,形成閉包保存了立即執(zhí)行函數(shù)執(zhí)行時(shí) i 的值,異步定時(shí)器的回調(diào)函數(shù)才如我們想要的打印了順序的值。

  其實(shí),useEffect的哪個(gè)場(chǎng)景的原因,跟這個(gè),簡(jiǎn)直是一樣的,useEffect閉包陷阱場(chǎng)景的出現(xiàn),是 react 組件更新流程以及useEffect的實(shí)現(xiàn)的自然而然結(jié)果。

  2 淺談hooks原理,理解useEffect 的 “閉包陷阱” 出現(xiàn)原因

  這里有很多內(nèi)容,不細(xì)說,大家可以自己深入了解。

       首先,可能都聽過react的 Fiber 架構(gòu),其實(shí)一個(gè) Fiber節(jié)點(diǎn)就對(duì)應(yīng)的是一個(gè)組件。對(duì)于classComponent而言,有state是一件很正常的事情,F(xiàn)iber對(duì)象上有一個(gè)memoizedState用于存放組件的state。

  ok,現(xiàn)在看 hooks 所針對(duì)的FunctionComponnet。 無論開發(fā)者怎么折騰,一個(gè)對(duì)象都只能有一個(gè)state屬性或者memoizedState屬性,可是,誰(shuí)知道可愛的開發(fā)者們會(huì)在FunctionComponent里寫上多少個(gè)useState,useEffect等等 ? 所以,react用了鏈表這種數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)FunctionComponent里面的 hooks。比如:

  function App(){
  const [count, setCount] = useState(1)
  const [name, setName] = useState('chechengyi')
  useEffect(()=>{
  }, [])
  const text = useMemo(()=>{
  return 'ffffd'
  }, [])
  }

  在組件第一次渲染的時(shí)候,為每個(gè)hooks都創(chuàng)建了一個(gè)對(duì)象

  type Hook = {
  memoizedState: any,
  baseState: any,
  baseUpdate: Update<any, any> | null,
  queue: UpdateQueue<any, any> | null,
  next: Hook | null,
  };

  最終形成了一個(gè)鏈表。

1.jpg

  這個(gè)對(duì)象的memoizedState屬性就是用來存儲(chǔ)組件上一次更新后的state,next毫無疑問是指向下一個(gè)hook對(duì)象。在組件更新的過程中,hooks函數(shù)執(zhí)行的順序是不變的,就可以根據(jù)這個(gè)鏈表拿到當(dāng)前hooks對(duì)應(yīng)的Hook對(duì)象,函數(shù)式組件就是這樣擁有了state的能力。這里就不展開細(xì)說。

  其實(shí)這刻意解釋就是為什么不能將hooks寫到if else語(yǔ)句中了把?因?yàn)檫@樣可能會(huì)導(dǎo)致順序錯(cuò)亂,導(dǎo)致當(dāng)前hooks拿到的不是自己對(duì)應(yīng)的Hook對(duì)象。

  useEffect接收了兩個(gè)參數(shù),一個(gè)回調(diào)函數(shù)和一個(gè)數(shù)組。數(shù)組里面就是useEffect的依賴,當(dāng)為 [] 的時(shí)候,回調(diào)函數(shù)只會(huì)在組件第一次渲染的時(shí)候執(zhí)行一次。如果有依賴其他項(xiàng),react 會(huì)判斷其依賴是否改變,如果改變了就會(huì)執(zhí)行回調(diào)函數(shù)。說回最初的場(chǎng)景:

  function App(){
  const [count, setCount] = useState(1);
  useEffect(()=&gt;{
  setInterval(()=&gt;{
  console.log(count)
  }, 1000)
  }, [])
  function click(){ setCount(2) }
  }

     現(xiàn)在構(gòu)建,組件第一次渲染執(zhí)行App(),執(zhí)行useState設(shè)置了初始狀態(tài)為1,所以此時(shí)的count為1。然后執(zhí)行了useEffect,回調(diào)函數(shù)執(zhí)行,設(shè)置了一個(gè)定時(shí)器每隔 1s 打印一次count。

  當(dāng)click函數(shù)被觸發(fā)了,調(diào)用setCount(2)肯定會(huì)觸發(fā)react的更新,更新到當(dāng)前組件的時(shí)候也是執(zhí)行App(),之前說的鏈表已經(jīng)形成了哈,此時(shí)useState將Hook對(duì)象 上保存的狀態(tài)置為2, 那么此時(shí)count也為2了。然后在執(zhí)行useEffect由于依賴數(shù)組是一個(gè)空的數(shù)組,所以此時(shí)回調(diào)并不會(huì)被執(zhí)行。

  但又有一個(gè)新問題。這次更新的過程中根本就沒有涉及到這個(gè)定時(shí)器,這個(gè)定時(shí)器還在堅(jiān)持的,默默的,每隔1s打印一次count。 注意這里打印的count,是組件第一次渲染的時(shí)候App()時(shí)的count,count的值為1,因?yàn)樵诙〞r(shí)器的回調(diào)函數(shù)里面被引用了,形成了閉包一直被保存。

  2 難道真的要在依賴數(shù)組里寫上的值,才能拿到新鮮的值?

  仿佛都習(xí)慣性都去認(rèn)為,只有在依賴數(shù)組里寫上我們所需要的值,才能在更新的過程中拿到最新鮮的值。那么看一下這個(gè)場(chǎng)景:

 

 function App() {
  return <Demo1 />
  }
  function Demo1(){
  const [num1, setNum1] = useState(1)
  const [num2, setNum2] = useState(10)
  const text = useMemo(()=>{
  return `num1: ${num1} | num2:${num2}`
  }, [num2])
  function handClick(){
  setNum1(2)
  setNum2(20)
  }
  return (
  <div>
  {text}
  <div><button onClick={handClick}>click!</button></div>
  </div>
  )
  }

  text是一個(gè)useMemo,它的依賴數(shù)組里面只有num2,沒有num1,卻同時(shí)使用了這兩個(gè)state。當(dāng)點(diǎn)擊button 的時(shí)候,num1和num2的值都改變了。那么,只寫明了依賴num2的 text 中能否拿到 num1 最新鮮的值呢?

  如果你裝了react的 eslint 插件,這里也許會(huì)提示你錯(cuò)誤,因?yàn)樵趖ext中你使用了 num1 卻沒有在依賴數(shù)組中添加它。 但是執(zhí)行這段代碼會(huì)發(fā)現(xiàn),是可以正常拿到num1最新鮮的值的。

  其實(shí)這要回過頭看看之前說的第一點(diǎn)“閉包陷阱”問題,也就不難理解。

  現(xiàn)在再重復(fù)一遍,這個(gè)依賴數(shù)組存在的意義,是react為了判定,在本次更新中,是否需要執(zhí)行其中的回調(diào)函數(shù),這里依賴了的num2,而num2改變了?;卣{(diào)函數(shù)自然會(huì)執(zhí)行, 這時(shí)形成的閉包引用的就是最新的num1和num2,所以,自然能夠拿到新鮮的值。問題的關(guān)鍵,在于回調(diào)函數(shù)執(zhí)行的時(shí)機(jī),閉包就像是一個(gè)照相機(jī),把回調(diào)函數(shù)執(zhí)行的那個(gè)時(shí)機(jī)的那些值保存了下來。之前說的定時(shí)器的回調(diào)函數(shù)我想就像是一個(gè)從1000年前穿越到現(xiàn)代的人,雖然來到了現(xiàn)代,但是身上的血液、頭發(fā)都是1000年前的。

  3 為什么使用useRef能夠每次拿到新鮮的值?

  看看下面代碼: 

 var A = {name: 'chechengyi'}
  var B = A
  B.name = 'baobao'
  console.log(A.name) // baobao

  對(duì),這就是這個(gè)場(chǎng)景成立的最根本原因。

  也就是說,在組件每一次渲染的過程中。 比如ref = useRef()所返回的都是同一個(gè)對(duì)象,每次組件更新所生成的ref指向的都是同一片內(nèi)存空間, 那么當(dāng)然能夠每次都拿到最新鮮的值了。其實(shí)與犬夜叉中一口古井連接了現(xiàn)代世界與500年前的戰(zhàn)國(guó)時(shí)代,這個(gè)同一個(gè)對(duì)象也將這些個(gè)被保存于不同閉包時(shí)機(jī)的變量了聯(lián)系了起來。

  使用一個(gè)例子或許好理解一點(diǎn): 

 /* 將這些相關(guān)的變量寫在函數(shù)外 以模擬react hooks對(duì)應(yīng)的對(duì)象 */
  let isC = false
  let isInit = true; // 模擬組件第一次加載
  let ref = {
  current: null
  }
  function useEffect(cb){
  // 這里用來模擬 useEffect 依賴為 [] 的時(shí)候只執(zhí)行一次。
  if (isC) return
  isC = true
  cb()
  }
  function useRef(value){
  // 組件是第一次加載的話設(shè)置值 否則直接返回對(duì)象
  if ( isInit ) {
  ref.current = value
  isInit = false
  }
  return ref
  }
  function App(){
  let ref_ = useRef(1)
  ref_.current++
  useEffect(()=>{
  setInterval(()=>{
  console.log(ref.current) // 3
  }, 2000)
  })
  }
  // 連續(xù)執(zhí)行兩次 第一次組件加載 第二次組件更新
  App()
  App()

  所以,提出一個(gè)合理的設(shè)想。只要我們能保證每次組件更新的時(shí)候,useState返回的是同一個(gè)對(duì)象的話?我們也能繞開閉包陷阱這個(gè)情景嗎? 動(dòng)手操作最有力。

  function App() {
  // return <Demo1 />
  return <Demo2 />
  }
  function Demo2(){
  const [obj, setObj] = useState({name: 'chechengyi'})
  useEffect(()=>{
  setInterval(()=>{
  console.log(obj)
  }, 2000)
  }, [])
  function handClick(){
  setObj((prevState)=> {
  var nowObj = Object.assign(prevState, {
  name: 'baobao',
  age: 24
  })
  console.log(nowObj == prevState)
  return nowObj
  })
  }
  return (
  <div>
  <div>
  <span>name: {obj.name} | age: {obj.age}</span>
  <div><button onClick={handClick}>click!</button></div>
  </div>
  </div>
  )
  }

  簡(jiǎn)單說下這段代碼,在執(zhí)行setObj的時(shí)候,傳入的是一個(gè)函數(shù)。這種用法就不用我多說了把?然后Object.assign返回的就是傳入的第一個(gè)對(duì)象??們貉灾?,就是在設(shè)置的時(shí)候返回了同一個(gè)對(duì)象。

  執(zhí)行這段代碼發(fā)現(xiàn),確實(shí)點(diǎn)擊button后,定時(shí)器打印的值也變成了:

  {
  name: 'baobao',
  age: 24
  }

  4 完畢

  其實(shí)“閉包陷阱” 淺談 react hooks 更是一種總結(jié),掌握知識(shí)、項(xiàng)目經(jīng)驗(yàn)都是缺一不可的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/128260.html

相關(guān)文章

  • 「學(xué)習(xí)筆記」ReactHooks入門

    showImg(https://segmentfault.com/img/remote/1460000019714462?w=1682&h=911);

    legendaryedu 評(píng)論0 收藏0
  • JavaScript深入淺出

    摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當(dāng)作缺點(diǎn)提及,但是只要善于運(yùn)用,其實(shí)基于原型的繼承模型比傳統(tǒng)的類繼承還要強(qiáng)大。中文指南基本操作指南二繼續(xù)熟悉的幾對(duì)方法,包括,,。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。 怎樣使用 this 因?yàn)楸救藢儆趥吻岸?,因此文中只看懂?8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...

    blair 評(píng)論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開緩存網(wǎng)頁(yè)性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫法...

    dailybird 評(píng)論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開緩存網(wǎng)頁(yè)性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫法...

    hellowoody 評(píng)論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁(yè)面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁(yè)瞬開緩存網(wǎng)頁(yè)性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁(yè)面渲染優(yōu)化 理論 | HTML寫法...

    wwolf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<