最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

react?hooks閉包陷阱切入淺談

 更新時(shí)間:2022年07月11日 14:23:44   作者:Chechengyi  
這篇文章主要介紹了從react?hooks閉包陷阱切入淺談react?hooks,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

首先,本文并不會(huì)講解 hooks 的基本用法, 本文從 一個(gè)hooks中 “奇怪”(其實(shí)符合邏輯) 的 “閉包陷阱” 的場(chǎng)景切入,試圖講清楚其背后的因果。同時(shí),在許多 react hooks 奇技淫巧的文章里,也能看到 useRef 的身影,那么為什么使用 useRef 又能擺脫 這個(gè) “閉包陷阱” ? 我想搞清楚這些問(wèn)題,將能較大的提升對(duì) react hooks 的理解。

react hooks 一出現(xiàn)便受到了許多開(kāi)發(fā)人員的追捧,或許在使用react hooks 的時(shí)候遇到 “閉包陷阱” 是每個(gè)開(kāi)發(fā)人員在開(kāi)發(fā)的時(shí)候都遇到過(guò)的事情,有的兩眼懵逼、有的則穩(wěn)如老狗瞬間就定義到了問(wèn)題出現(xiàn)在何處。

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

你一定遭遇過(guò)以下這個(gè)場(chǎng)景:

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

在這個(gè)定時(shí)器里面去打印 count 的值,會(huì)發(fā)現(xiàn),不管在這個(gè)組件中的其他地方使用 setCountcount 設(shè)置為任何值,還是設(shè)置多少次,打印的都是1。是不是有一種,盡管歷經(jīng)千帆,我記得的還是你當(dāng)初的模樣的感覺(jué)? hhh... 接下來(lái),我將盡力的嘗試將我理解的,為什么會(huì)發(fā)生這么個(gè)情況說(shuō)清楚,并且淺談一些hooks其他的特性。如果有錯(cuò)誤,希望各位同學(xué)能救救孩子,不要讓我?guī)еe(cuò)誤的認(rèn)知活下去了。。。

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

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

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

想寶寶我剛剛畢業(yè)的那一年,這道題還是一道有些熱門(mén)的面試題目。而如今...

我就不說(shuō)為什么最終,打印的都是5的原因了。直接貼出使用閉包打印 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)原因

其實(shí),很不想在寫(xiě)這篇文章的過(guò)程中,牽扯到react原理這方面的東西,因?yàn)檎娴氖翘w了(其實(shí)主要原因是菜,自己也只是掌握的囫圇吞棗),你要明白這個(gè)大概的過(guò)程,你得明白支撐起這個(gè)大概的一些重要的點(diǎn)。

首先,可能都聽(tīng)過(guò)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。 無(wú)論開(kāi)發(fā)者怎么折騰,一個(gè)對(duì)象都只能有一個(gè) state 屬性或者 memoizedState 屬性,可是,誰(shuí)知道可愛(ài)的開(kāi)發(fā)者們會(huì)在 FunctionComponent 里寫(xiě)上多少個(gè) useState,useEffect 等等 ? 所以,react用了鏈表這種數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ) FunctionComponent 里面的 hooks。比如:

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

在組件第一次渲染的時(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è)鏈表。

這個(gè)對(duì)象的memoizedState屬性就是用來(lái)存儲(chǔ)組件上一次更新后的 state,next毫無(wú)疑問(wèn)是指向下一個(gè)hook對(duì)象。在組件更新的過(guò)程中,hooks函數(shù)執(zhí)行的順序是不變的,就可以根據(jù)這個(gè)鏈表拿到當(dāng)前hooks對(duì)應(yīng)的Hook對(duì)象,函數(shù)式組件就是這樣擁有了state的能力。當(dāng)前,具體的實(shí)現(xiàn)肯定比這三言兩語(yǔ)復(fù)雜很多。

所以,知道為什么不能將hooks寫(xiě)到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ù)。說(shuō)回最初的場(chǎng)景:

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

好,開(kāi)動(dòng)腦袋開(kāi)始想象起來(lái),組件第一次渲染執(zhí)行 App(),執(zhí)行 useState 設(shè)置了初始狀態(tài)為1,所以此時(shí)的 count 為1。然后執(zhí)行了 useEffect,回調(diào)函數(shù)執(zhí)行,設(shè)置了一個(gè)定時(shí)器每隔 1s 打印一次 count。

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

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

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

仿佛都習(xí)慣性都去認(rèn)為,只有在依賴數(shù)組里寫(xiě)上我們所需要的值,才能在更新的過(guò)程中拿到最新鮮的值。那么看一下這個(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,沒(méi)有num1,卻同時(shí)使用了這兩個(gè)state。當(dāng)點(diǎn)擊button 的時(shí)候,num1和num2的值都改變了。那么,只寫(xiě)明了依賴num2的 text 中能否拿到 num1 最新鮮的值呢?

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

如果理解了之前第一點(diǎn)說(shuō)的“閉包陷阱”問(wèn)題,肯定也能理解這個(gè)問(wèn)題。

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

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

大白話說(shuō):因?yàn)槌跏蓟?useRef 執(zhí)行之后,返回的都是同一個(gè)對(duì)象。寫(xiě)到這里寶寶又不禁回憶起剛學(xué)js那會(huì)兒,捧著紅寶書(shū)啃時(shí)候的場(chǎng)景了:

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

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

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

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

    /* 將這些相關(guān)的變量寫(xiě)在函數(shù)外 以模擬react hooks對(duì)應(yīng)的對(duì)象 */
	let isC = false
	let isInit = true; // 模擬組件第一次加載
	let ref = {
		current: null
	}
	function useEffect(cb){
		// 這里用來(lái)模擬 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ì)象的話?我們也能繞開(kāi)閉包陷阱這個(gè)情景嗎? 試一下吧。

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

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

{
    name: 'baobao',
    age: 24 
}

4 完畢

通過(guò)一次“閉包陷阱” 淺談 react hooks 全文再此就結(jié)束了。 反正寫(xiě)完了這篇文章,我對(duì) hooks 的認(rèn)識(shí)是比以前深了,更多關(guān)于react hooks閉包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React中props驗(yàn)證不足的問(wèn)題及解決方案

    React中props驗(yàn)證不足的問(wèn)題及解決方案

    在?React?開(kāi)發(fā)中,props?是組件間通信的重要方式,通過(guò)?props,父組件可以向子組件傳遞數(shù)據(jù)和回調(diào)函數(shù),然而,如果對(duì)?props?的驗(yàn)證不足,可能會(huì)導(dǎo)致類型錯(cuò)誤、運(yùn)行時(shí)錯(cuò)誤或難以調(diào)試的問(wèn)題,本文將探討?React?中?props?驗(yàn)證不足的常見(jiàn)問(wèn)題,并提供解決方案
    2025-03-03
  • Electron打包React生成桌面應(yīng)用方法詳解

    Electron打包React生成桌面應(yīng)用方法詳解

    這篇文章主要介紹了React+Electron快速創(chuàng)建并打包成桌面應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-12-12
  • React?錯(cuò)誤邊界Error?Boundary使用示例解析

    React?錯(cuò)誤邊界Error?Boundary使用示例解析

    這篇文章主要為大家介紹了React?錯(cuò)誤邊界Error?Boundary使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 詳解基于webpack搭建react運(yùn)行環(huán)境

    詳解基于webpack搭建react運(yùn)行環(huán)境

    本篇文章主要介紹了詳解基于webpack搭建react運(yùn)行環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • React Hooks與setInterval的踩坑問(wèn)題小結(jié)

    React Hooks與setInterval的踩坑問(wèn)題小結(jié)

    本文主要介紹了React Hooks與setInterval的踩坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 使用reactjs優(yōu)化了進(jìn)度條頁(yè)面性能提高70%

    使用reactjs優(yōu)化了進(jìn)度條頁(yè)面性能提高70%

    這篇文章主要介紹了使用reactjs優(yōu)化了進(jìn)度條后頁(yè)面性能提高了70%的操作技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • React中Refs的使用場(chǎng)景及核心要點(diǎn)詳解

    React中Refs的使用場(chǎng)景及核心要點(diǎn)詳解

    在使用?React?進(jìn)行開(kāi)發(fā)過(guò)程中,或多或少使用過(guò)?Refs?進(jìn)行?DOM?操作,這篇文章主要介紹了?Refs?功能和使用場(chǎng)景以及注意事項(xiàng),希望對(duì)大家有所幫助
    2023-07-07
  • react antd checkbox實(shí)現(xiàn)全選、多選功能

    react antd checkbox實(shí)現(xiàn)全選、多選功能

    目前好像只有table組件有實(shí)現(xiàn)表格數(shù)據(jù)的全選功能,如果說(shuō)對(duì)于list,card,collapse等其他組件來(lái)說(shuō),需要自己結(jié)合checkbox來(lái)手動(dòng)實(shí)現(xiàn)全選功能,這篇文章主要介紹了react antd checkbox實(shí)現(xiàn)全選、多選功能,需要的朋友可以參考下
    2024-07-07
  • React?函數(shù)式組件和類式組件詳情

    React?函數(shù)式組件和類式組件詳情

    這篇文章主要介紹了React函數(shù)式組件和類式組件詳情,React是組件化的的JS庫(kù),組件化也是React的核心思想,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • 詳解React中如何拆分組件

    詳解React中如何拆分組件

    這篇文章主要為大家詳細(xì)介紹了React中拆分組件的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們掌握React有一定的幫助,需要的小伙伴可以參考一下
    2023-12-12

最新評(píng)論

于都县| 岳阳市| 南丰县| 明星| 靖远县| 黄山市| 资阳市| 淮安市| 唐河县| 海南省| 攀枝花市| 武强县| 长乐市| 福安市| 台前县| 柘城县| 双辽市| 浮山县| 大庆市| 准格尔旗| 土默特左旗| 阿瓦提县| 同德县| 博乐市| 陕西省| 崇义县| 丰台区| 南木林县| 嘉荫县| 民丰县| 马山县| 吉林省| 茂名市| 湖州市| 台南县| 西林县| 沿河| 中方县| 朝阳县| 台北县| 潮州市|