欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React state狀態(tài)屬性用法講解

 更新時(shí)間:2022年11月14日 10:22:54   作者:學(xué)學(xué)學(xué)無(wú)止境  
React將組件(component)看成一個(gè)狀態(tài)機(jī)(State Machines),通過(guò)其內(nèi)部自定義的狀態(tài)(State)和生命周期(Lifecycle)實(shí)現(xiàn)并與用戶交互,維持組件的不同狀態(tài)

類組件中的state

setState的用法

React項(xiàng)目中UI改變來(lái)源于state的改變,類組件中setState是更新組件,渲染視圖的主要方式

基本用法

setState(obj,callback)
  • 第一個(gè)參數(shù):當(dāng)obj是一個(gè)對(duì)象,即為即將合并的state;如果obj是一個(gè)函數(shù),那么當(dāng)組件的state和props將作為參數(shù),返回值用于合并新的state
  • 第二個(gè)參數(shù)callback:callback為一個(gè)函數(shù),函數(shù)執(zhí)行上下文中可以獲取當(dāng)前setState更新后的最新的值,可以作為依賴state變化的副作用函數(shù),可以用來(lái)做一些基本的DOM操作
/* 第一個(gè)參數(shù)為function類型 */
this.setState((state,props)=>{
    return {number:}
})
/* 第一個(gè)參數(shù)為object類型 */
this.setState({ number:1 },()=>{
    console.log(this.state.number) //獲取最新的number
})

加入一次事件中觸發(fā)一次如上的setState,在React底層主要做了哪些事呢?

  • 首先,setState會(huì)產(chǎn)生當(dāng)前更新的優(yōu)先級(jí)------產(chǎn)生更新優(yōu)先級(jí)
  • 接下來(lái)React會(huì)從fiber Root根部fiber向下調(diào)和子節(jié)點(diǎn),調(diào)和階段將對(duì)比發(fā)生更新的彼方,更新對(duì)比expirationTime,找到發(fā)生更新的組件,合并state,然后觸發(fā)render函數(shù),得到最新的UI視圖,完成render階段--------對(duì)比
  • 接下來(lái)到commit階段,commit階段,替換真實(shí)DOM,完成此次更新流程。--------替換真實(shí)DOM
  • 此時(shí)仍然在commit階段,會(huì)執(zhí)行setState中callback函數(shù),如上的()=>{console.log(this.state.number)} ,到此為止就完成了一次setState的過(guò)程。

更新的流層圖如下:

要記住一個(gè)主要任務(wù)的先后順序,這對(duì)于弄清渲染過(guò)程會(huì)有幫助:

render階段render函數(shù)執(zhí)行--->commit階段真實(shí)DOM替換--->setState回調(diào)函數(shù)執(zhí)行callback

類組件如何限制state更新視圖

對(duì)于類組件如何顯示state帶來(lái)的更新作用呢?

  • pureComponet可以對(duì)state和props進(jìn)行淺比較,如果沒有發(fā)生變化,那么組件就不會(huì)更新
  • shouldComponentUpdate生命周期可以通過(guò)判斷前后state變化來(lái)決定組件需不需要更新,需要更新返回true,否則返回false

setState原理揭秘

知其然,知其所以然,下面將介紹setState的底層邏輯,要弄清楚state的更新機(jī)制,所以接下來(lái)要從兩個(gè)方向分析

  • 一是揭秘enqueueSetState到底做了什么?
  • 二是React底層是如何進(jìn)行批量更新的?

首先,這里極簡(jiǎn)了一下enqueueSetState的代碼,如下:

enqueueSetState(){
    //每次調(diào)用setState,react都會(huì)創(chuàng)建一個(gè)update里面保存了如下
    const update= createUpdate(expirationTime,suspenseConfig)
    //callback 可以理解為setState回調(diào)函數(shù),第二個(gè)參數(shù)
    callback && (update.callback=callback)
    //enqueuUpdate 把當(dāng)前的update 傳入當(dāng)前fier ,待更新隊(duì)列中
    enqueuUpdate(fiber,update)
    //開始調(diào)度更新
    scheduleUpdateOnFiber(fiber,expirationTime)
}

enqueueSetState作用實(shí)際很簡(jiǎn)單,就是創(chuàng)建一個(gè)update,然后放入當(dāng)前的fiber對(duì)象的待更新隊(duì)列中,最后開啟調(diào)度更新,進(jìn)入上述講到的更新流程。

那么問(wèn)題來(lái)了,React的batchUpdate批量更新是什么時(shí)候加上去的呢?

這就要提前聊到事件系統(tǒng)了,正常的state更新,UI交互,都離不開用戶的事件,比如點(diǎn)擊事件,表單輸入等,React是采用事件合成的形式,每一個(gè)事件都是由React事件系統(tǒng)統(tǒng)一調(diào)度的,那么State批量更新正是和事件系統(tǒng)息息相關(guān)的。

//在legcy模式下,所有的事件都將經(jīng)過(guò)此函數(shù)統(tǒng)一處理
function dispatchEventFoLegacyPluginEventSystem(){
    //handleTopLevel 事件處理函數(shù)
    batchEventUpdates(handleTopLvele,bookKeeping)
}

batchEventUpdates方法具體如下:

batchEventUpdate(fn,a){
    //開啟批量更新
    isBatchingEventUpdates=true;
    try{
        //這里執(zhí)行了事處理函數(shù),比如在一次點(diǎn)擊事件中觸發(fā)setState,那么它將在這個(gè)函數(shù)執(zhí)行
        return batchEventUpdateImpl(fn,a,b);
    }finally{
        //try里面的return 不會(huì)影響finally執(zhí)行
        //完成一次事件,批量更新
        isBatchingEventUpdates=false
    }
}

如上分析出流程圖,在React事件執(zhí)行之前通過(guò)isBatchEventUpdates=true打開開關(guān)開啟事件批量更新,當(dāng)該事件結(jié)束,再通過(guò)isBactchEventUpdates=false;關(guān)閉開關(guān),然后在scheduleUpdateOnFiber中根據(jù)開關(guān)來(lái)確定是否進(jìn)行批量更新

舉個(gè)例子,如下組件中這么寫:

import React, { Component } from 'react';
export default class Test extends Component {
  state={number:0}
  handleClick=()=>{
    this.setState({number:this.state.number+1},()=>{
      console.log('callback1',this.state.number)
    })
    console.log(this.state.number)
    this.setState({number:this.state.number+1},()=>{
      console.log('callback2',this.state.number)
    })    
    console.log(this.state.number)
    this.setState({number:this.state.number+1},()=>{
      console.log('callback3',this.state.number)
    })
    console.log(this.state.number)
  }
  render() {
    return (
      <div> 
        {this.state.number}
        <button onClick={this.handleClick}>number++</button>
      </div>
    );
  }
}

點(diǎn)擊打印結(jié)果:0,0,0 callback1 1 ,callback2 1,callback3 1

如上代碼,在整個(gè)React上下文執(zhí)行棧中會(huì)變成這樣:

那么,為什么異步操作里面的批量更新規(guī)則會(huì)被打破呢?比如用promise或者setTime在handleClick中這么寫:

handleClick=()=>{
  setTimeout(()=>{
    this.setState({number:this.state.number+1},()=>{
      console.log('callback1',this.state.number)
    })
    console.log(this.state.number)
    this.setState({number:this.state.number+1},()=>{
      console.log('callback2',this.state.number)
    })    
    console.log(this.state.number)
    this.setState({number:this.state.number+1},()=>{
      console.log('callback3',this.state.number)
    })
    console.log(this.state.number)
  })
}

打?。篶allback1 1,1,callback2 2,2,callback3 3 ,3

那么整個(gè)React上下文執(zhí)行棧就會(huì)變成如圖這樣

所以批量更新規(guī)則被打破。

那么,如果在如上異步環(huán)境下,繼續(xù)開啟批量更新模式呢?

React-Dom提供了批量更新方法unstable_batchChedUpdates,可以去手動(dòng)批量更新,可以將上述setTimeout里面的內(nèi)容作如下修改:

import ReactDom from 'react-dom';
const {unstable_batchedUpdates}=ReactDom
  setTimeout(()=>{
    unstable_batchedUpdates(()=>{
      this.setState({number:this.state.number+1},()=>{
        console.log('callback1',this.state.number)
      })
      console.log(this.state.number)
      this.setState({number:this.state.number+1},()=>{
        console.log('callback2',this.state.number)
      })    
      console.log(this.state.number)
      this.setState({number:this.state.number+1},()=>{
        console.log('callback3',this.state.number)
      })
      console.log(this.state.number)
    })
  })

點(diǎn)擊打印結(jié)果:0,0,0 callback1 1 ,callback2 1,callback3 1

在實(shí)際工作中,unstable_batchChedUpdates可以用于Ajax數(shù)據(jù)交互之后,合并多次setState,或者是多次useState。原因很簡(jiǎn)單,所有的數(shù)據(jù)交互都是在異步環(huán)境下,如果沒有批量更新處理,一次數(shù)據(jù)交互多次改變state會(huì)促使視圖多次渲染。

那么如何提升更新優(yōu)先級(jí)呢?

React-dom提供了flushSync,flushSync可以將回調(diào)函數(shù)中的更新任務(wù),放在一個(gè)比較高的優(yōu)先級(jí)中。React設(shè)定了很多不同優(yōu)先級(jí)的更新任務(wù),如果一次更新任務(wù)在flushSync回調(diào)內(nèi)部,那么將獲得一個(gè)比較高優(yōu)先級(jí)的更新。

函數(shù)組件中的state

React-hooks發(fā)布之后,useState可以使函數(shù)組件像類組件一樣擁有state,也就是說(shuō)名函數(shù)組件可以通過(guò)useState來(lái)改變UI視圖。那么useState到底應(yīng)該如何使用,底層優(yōu)勢(shì)怎么運(yùn)行的呢?

useState的用法

const [state,setState] = useState(initData)
  • state,目的是提供給UI,作為渲染視圖的數(shù)據(jù)源
  • setState改變state的函數(shù),可以理解為推動(dòng)函數(shù)組件渲染的渲染函數(shù)
  • initData有兩種情況,第一種情況是非函數(shù),將作為state的初始化的值,第二種情況是函數(shù),函數(shù)返回值作為作為useState初始化的值

initData為函數(shù)的情況:

const [number,setNumber]= React.useState(0)

initData為函數(shù)的情況:

  const [number,setNumber]=React.useState(()=>{
    //props中的a=1 state為0-1隨機(jī)數(shù)
    //props中a=2 state為1-10的隨機(jī)數(shù)
    //否則 state為1-100的隨機(jī)數(shù)
    if(props.a===1) return Math.random()
    if(props.a===2) return Math.ceil(Math.random()*10)
    return Math.ceil(Math.random()*100)
  })

對(duì)于setState參數(shù),也有兩種情況:

  • 第一種是非函數(shù)情況,此時(shí)將作為新的值,賦予state,作為下一次渲染使用;
  • 第二種是函數(shù)的情況,如果setState的參數(shù)是一個(gè)函數(shù),這里可以稱它為reducer,reducer參數(shù),是上一次返回最新的state,返回值作為新的state

setState參數(shù)是一個(gè)非函數(shù)的情況:

  const [number,setNumber]= React.useState(0)
  const handleClick=()=>{
    setNumber(1)
    setNumber(2)
    setNumber(3)
  }

setState參數(shù)是一個(gè)函數(shù)的情況:

  const [number,setNumber]= React.useState(0)
  const handleClick=()=>{
    setNumber((state)=>{
      return state+1//0+1=1
    })
    setNumber(8)//8
    setNumber((state)=>{
      return state+1//8+1=9
    })
  }

如何監(jiān)聽state的變化

類組件中的setState中,有第二個(gè)參數(shù)callback或是生命周期函數(shù)componentDidUpdate可以檢測(cè)監(jiān)聽到state改變或是組件更新。

那么在函數(shù)組件中,如何監(jiān)聽state變化呢?這個(gè)時(shí)候就需要useEffect出場(chǎng)了,通常可以把state作為依賴項(xiàng)傳入useEffect第二個(gè)參數(shù)deps,但是注意useEffect初始化是會(huì)默認(rèn)執(zhí)行一遍。

import React,{useEffect, useState} from 'react'
import ReactDom from 'react-dom'
export default function Test() {
  const [number,setNumber]= React.useState(0)
  const handleClick=()=>{
    ReactDom.flushSync(()=>{
      setNumber(2)
    })
    setNumber(1)
    setTimeout(()=>{
      setNumber(3)
    })
  }
  useEffect(()=>{
    console.log('變化',number)
  },[number])
  console.log(number)
  return (
    <button onClick={handleClick}>text1</button>
  )
}

執(zhí)行結(jié)果:

setState(dispatch)更新特點(diǎn)

上述講到的批量更新和flushSync,在函數(shù)組件中,dispatch更新效果和類組件是一樣的,但是useState有一點(diǎn)值得注意,就是當(dāng)?shù)蹛塾酶淖僺tate的函數(shù)dispatch,在本次函數(shù)執(zhí)行上下文中,是獲取不到state的值的,舉例如下:

  const [number,setNumber]= React.useState(0)
  const handleClick=()=>{
    ReactDom.flushSync(()=>{
      setNumber(2)
      console.log(number)
    })
    setNumber(1)
    console.log(number)
    setNumber(()=>{
      setNumber(3)
      console.log(number)
    })
  }

結(jié)果:0 0 0

原因很簡(jiǎn)單,函數(shù)組件更新就是函數(shù)的執(zhí)行,在函數(shù)一次執(zhí)行過(guò)程中,函數(shù)內(nèi)部所有變量重新生命,所以改變的state,只有在下一次函數(shù)組件執(zhí)行時(shí)才會(huì)更新,所以在如同上一個(gè)函數(shù)執(zhí)行上下文中,number一直為0,無(wú)論怎么打印,都拿不到最新的state。

useState的注意事項(xiàng)

在使用useState的dispatchAction更新state的時(shí)候,記得不要傳入相同的state,這樣會(huì)使視圖不更新,比如下面:

  const [state,dispatchState]= React.useState({
    name:'aline'
  })
  const handleClick=()=>{
    state.name='aline'
    dispatchState(state)//直接改變state,在內(nèi)存中執(zhí)行的地址沒有變
  }

上述例子為什么沒有更新呢?是因?yàn)樵趗seState的dispatchAction處理邏輯中,會(huì)淺比較state兩次,發(fā)現(xiàn)state相同,不會(huì)開啟更新調(diào)度任務(wù)。其中demo中兩次state指向了相同的內(nèi)存空間,所以默認(rèn)為state相等,就不會(huì)發(fā)生視圖更新了

解決問(wèn)題:把上述的dispatchState改成dispatch({...state})根本解決了問(wèn)題,淺拷貝了對(duì)象,重新開啟了內(nèi)存空間。

總結(jié)

類組件中的setState和函數(shù)組件中的useState有什么異同?

相同點(diǎn)

首先從原理角度出發(fā),setState和useState更新視圖,底層都調(diào)用了scheduleUpdateOnFiber方法,而且時(shí)間驅(qū)動(dòng)情況下都有批量更新規(guī)則

不同點(diǎn)

  • 再不是pureComponent組件模式下,setState不會(huì)淺比較兩次的state的值,只有調(diào)用setState,在沒有其他優(yōu)化手段的前提下,會(huì)執(zhí)行更新,但是useState中的dispatchAction會(huì)默認(rèn)比較兩次state是否相同,然后決定是否更新組件
  • setState有專門監(jiān)聽state變化的回調(diào)函數(shù)callback,著這個(gè)回調(diào)函數(shù)中可以獲取到最新的值,而在函數(shù)組件中,只能通過(guò)useEffect來(lái)執(zhí)行state變化引起副作用。
  • setState在頂層處理state的邏輯主要是和舊state進(jìn)行合并操作,而useState則是替換,及重新賦值

到此這篇關(guān)于React state狀態(tài)屬性用法講解的文章就介紹到這了,更多相關(guān)React state狀態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • react中使用antd及immutable示例詳解

    react中使用antd及immutable示例詳解

    這篇文章主要為大家介紹了react中使用antd及immutable示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • react:swr接口緩存案例代碼

    react:swr接口緩存案例代碼

    useSWR 是一個(gè) React Hooks,是 HTTP 緩存庫(kù) SWR 的核心方法之一,SWR 是一個(gè)輕量級(jí)的 React Hooks 庫(kù),通過(guò)自動(dòng)緩存數(shù)據(jù)來(lái)實(shí)現(xiàn) React 的數(shù)據(jù)獲取,本文給大家介紹react:swr接口緩存案例詳解,感興趣的朋友一起看看吧
    2023-11-11
  • React中react-redux和路由詳解

    React中react-redux和路由詳解

    這篇文章主要介紹了React中react-redux和路由詳解,redux早期被設(shè)計(jì)成可以在各個(gè)框架中使用,因此在不同的框架中使用的時(shí)候,要引入相應(yīng)的插件,感興趣的朋友可以繼續(xù)學(xué)習(xí)下面文章
    2022-08-08
  • 手動(dòng)用webpack搭建第一個(gè)ReactApp的示例

    手動(dòng)用webpack搭建第一個(gè)ReactApp的示例

    本篇文章主要介紹了手動(dòng)用webpack搭第一個(gè) ReactApp的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • React Native AsyncStorage本地存儲(chǔ)工具類

    React Native AsyncStorage本地存儲(chǔ)工具類

    這篇文章主要為大家分享了React Native AsyncStorage本地存儲(chǔ)工具類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • 快速搭建React的環(huán)境步驟詳解

    快速搭建React的環(huán)境步驟詳解

    本篇文章主要介紹了快速搭建React的步驟詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • React Zustand狀態(tài)管理庫(kù)的使用詳解

    React Zustand狀態(tài)管理庫(kù)的使用詳解

    Zustand是一個(gè)為React和瀏覽器環(huán)境設(shè)計(jì)的輕量級(jí)狀態(tài)管理庫(kù),由Vercel開發(fā),它特點(diǎn)包括輕量級(jí)、易用性、靈活性、可組合性和性能優(yōu)化,支持多種狀態(tài)管理模式和中間件,適合中小型項(xiàng)目,Zustand還支持TypeScript,提供類型安全的支持
    2024-09-09
  • react-pdf?打造在線簡(jiǎn)歷生成器的示例代碼

    react-pdf?打造在線簡(jiǎn)歷生成器的示例代碼

    本文主要介紹了react-pdf?打造在線簡(jiǎn)歷生成器的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • react使用CSS實(shí)現(xiàn)react動(dòng)畫功能示例

    react使用CSS實(shí)現(xiàn)react動(dòng)畫功能示例

    這篇文章主要介紹了react使用CSS實(shí)現(xiàn)react動(dòng)畫功能,結(jié)合實(shí)例形式分析了react使用CSS實(shí)現(xiàn)react動(dòng)畫功能具體步驟與實(shí)現(xiàn)方法,需要的朋友可以參考下
    2020-05-05
  • 使用React-Window實(shí)現(xiàn)虛擬滾動(dòng)效果的示例代碼

    使用React-Window實(shí)現(xiàn)虛擬滾動(dòng)效果的示例代碼

    React-Window?是一個(gè)為?React?應(yīng)用程序中高效渲染大數(shù)據(jù)集而設(shè)計(jì)的庫(kù),它基于窗口化或虛擬化的原則運(yùn)行,本文將使用React-Window實(shí)現(xiàn)虛擬滾動(dòng)效果,感興趣的可以了解下
    2024-01-01

最新評(píng)論