React-setState、HOC、Portal簡(jiǎn)單介紹

1、setState(newState,callback)

React中可以通過(guò)修改state,通知React進(jìn)行Update的方法
callback用途,接收更新后的state,可以繼續(xù)操作

一般情況下 是一個(gè)異步方法
但是在定時(shí)器以及原生DOM事件會(huì)是一個(gè)同步方法

異步方法的好處
setState設(shè)計(jì)為異步,可以顯著的提升性能;
如果每次調(diào)用setState都進(jìn)行一次更新,那么意味著render函數(shù)會(huì)被頻繁調(diào)用,界面重新渲染,這樣效率是很低的;
最好的辦法應(yīng)該是獲取到多個(gè)更新,之后進(jìn)行批量更新;
如果同步更新了state,但是還沒(méi)有執(zhí)行render函數(shù),那么state和props不能保持同步;
state和props不能保持一致性,會(huì)在開(kāi)發(fā)中產(chǎn)生很多的問(wèn)題;

在調(diào)用setState后直接使用新的state是不建議的
this.setState({id:'123'});
console.log(this.state.id) // 這時(shí)候id不一定是 123

應(yīng)該寫(xiě)成
this.setState({id:'123'},(state)=>{
console.log(state.id) // 這時(shí)候 id===123
})

異步變同步(不推薦)

class MyComponent extend React.Component{

  constructor(){
    this.state={
      id:'haha'
    }
  }

  componentDidMount(){
    document.getElelemtnById('btn').addEventListener('clcik',()=>{
      this.setState({id:'123'});
      console.log(this.state.id) // 這時(shí)候id是 123
    })
  }

updateWithsetTimeout = ()=>{
  setTimeOut(()=>{
    this.setState({id:'123'});
    console.log(this.state.id) // 這時(shí)候id是 123
  },0)
}

render(){
  return (
    <>
      <button onClick={this.updateWithsetTimeout}>setTimeOut</button>
      <button id="btn">js function </button>
    </>
  )
}
}

2、受控組件

在React 中,可變狀態(tài)(mutable state)通常保存在組件的state 屬性中,并且只能通過(guò)使用setState()來(lái)更新。
我們將兩者結(jié)合起來(lái),使React的state成為“唯一數(shù)據(jù)源”;
渲染表單的React 組件還控制著用戶(hù)輸入過(guò)程中表單發(fā)生的操作;
被React 以這種方式控制取值的表單輸入元素就叫做“受控組件”;
由于在表單元素上設(shè)置了value屬性,因此顯示的值將始終為this.state.value,這使得React 的state 成為唯一數(shù)據(jù)源。
由于handleUsernameChange在每次按鍵時(shí)都會(huì)執(zhí)行并更新React 的state,因此顯示的值將隨著用戶(hù)輸入而更新。

class FormComponent extends React.Component{
  constructor(){
    this.state = {
      inputValue:'',
    }
  }

  onChange = (e) =>{
    const value = e.target.value;
    this.setState({inputValue:value});
  }

  render(){
    return (
      <input type="text" value={this.state.inputValue} onChange={this.onChange} />
    )
  }
}

3、非受控組件

如果要使用非受控組件中的數(shù)據(jù),那么我們需要使用ref來(lái)從DOM節(jié)點(diǎn)中獲取表單數(shù)據(jù)
在非受控組件中通常使用defaultValue來(lái)設(shè)置默認(rèn)值

class FormComponent extends React.Component{

  constructor(){
    this.inputRef = React.createRef();
  }

  onBtnClick = () =>{
    console.log(
      `input - ${this.inputRef.current.value}`
    );
  }

  render(){
    return (
      <>
        <input defaultValue="haha" ref={this.inputRef} />
        <button onClick={this.onBtnClick}>確定</button>
      </>
    )
  }
}

4、高階組件

至少滿(mǎn)足以下條件之一:
接受一個(gè)或多個(gè)函數(shù)作為輸入;
輸出一個(gè)函數(shù);

高階組件的英文是Higher-Order Components,簡(jiǎn)稱(chēng)為HOC;
官方的定義:高階組件是參數(shù)為組件,返回值為新組件的函數(shù);

我們可以進(jìn)行如下的解析:
首先,高階組件本身不是一個(gè)組件,而是一個(gè)函數(shù);
其次,這個(gè)函數(shù)的參數(shù)是一個(gè)組件,返回值也是一個(gè)組件;

function higherOrderComponent(component){
  class Component extends React.Component{
    render (){
      {/* 需要將接收的props進(jìn)行向下傳遞 */}
      return <component {...this.props}/>
    }
  }
  Component.displayName = "EnhancedComponent";
  return Component;
}
const EnhancedComponent = higherOrderComponent(WarppedComponent);

應(yīng)用場(chǎng)景一:props 增強(qiáng)
1、不改變?cè)写a情況下,擴(kuò)增props react-redux的connect就是這個(gè)作用
2、利用高階組件進(jìn)行Context共享(減少業(yè)務(wù)組件在使用時(shí)對(duì)Context.Consumer 編寫(xiě)不友好的問(wèn)題)

應(yīng)用場(chǎng)景二:渲染判斷鑒權(quán)
加一層判斷,控制組件是否渲染

應(yīng)用場(chǎng)景三:生命周期劫持
虛空增加的一層父組件,可以在原需要組件進(jìn)行渲染的時(shí)候,根具特定的生命周期進(jìn)行一些類(lèi)似于 熱點(diǎn)、渲染時(shí)間、錯(cuò)誤邊界捕獲等記錄。

HOC的意義

我們會(huì)發(fā)現(xiàn)利用高階組件可以針對(duì)某些React代碼進(jìn)行更加優(yōu)雅的處理。
其實(shí)早期的React有提供組件之間的一種復(fù)用方式是mixin,目前已經(jīng)不再建議使用:
Mixin可能會(huì)相互依賴(lài),相互耦合,不利于代碼維護(hù)
不同的Mixin中的方法可能會(huì)相互沖突pMixin非常多時(shí),組件是可以感知到的,甚至還要為其做相關(guān)處理,這樣會(huì)給代碼造成滾雪球式的復(fù)雜性

當(dāng)然,HOC也有自己的一些缺陷:

HOC需要在原組件上進(jìn)行包裹或者嵌套,如果大量使用HOC,將會(huì)產(chǎn)生非常多的嵌套,這讓調(diào)試變得非常困難;
HOC可以劫持props,在不遵守約定的情況下也可能造成沖突;

Hooks的出現(xiàn),是開(kāi)創(chuàng)性的,它解決了很多React之前的存在的問(wèn)題
比如this指向問(wèn)題、比如hoc的嵌套復(fù)雜度問(wèn)題等等;

5、Portals的使用

通常來(lái)講,當(dāng)你從組件的render方法返回一個(gè)元素時(shí),該元素將被掛載到DOM節(jié)點(diǎn)中離其最近的父節(jié)點(diǎn):
Portals提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的 DOM 節(jié)點(diǎn)的方案
ReactDOM.createPortal(child, container)
第一個(gè)參數(shù)(child)是任何可渲染的React 子元素,例如一個(gè)元素,字符串或fragment;
第二個(gè)參數(shù)(container)是一個(gè)DOM元素;

即使child子元素被掛載到其他DOM結(jié)點(diǎn)下,只是DOM級(jí)掛載,但是在Fiber樹(shù)上,它仍屬于原Parent結(jié)點(diǎn)的子節(jié)點(diǎn)。

現(xiàn)象級(jí)掛載
  就是從HTML代碼結(jié)構(gòu)上可以看到是確實(shí)被挪移到目標(biāo)結(jié)點(diǎn)下
  目標(biāo)結(jié)點(diǎn)通過(guò)添加事件監(jiān)聽(tīng),也能捕獲child子元素對(duì)應(yīng)冒泡的事件
仍屬于Parent原結(jié)點(diǎn)
  1、仍然可以使用Context,不脫離作用域范圍。
  2、在父結(jié)點(diǎn)上添加的React事件(onClick),在子節(jié)點(diǎn)上發(fā)生時(shí)事件時(shí),仍能被冒泡捕獲到。
  3、但是通過(guò)elemnt.addEventListener 這種方式就不能再捕獲對(duì)應(yīng)事件!因?yàn)镈OM元素真的被移走了!
    <Parent onClick>
      <Portal>
        <Button />
      </Portal>
    </Parent>

即使這里的Portal被移動(dòng)到其他DOM結(jié)點(diǎn)下,當(dāng)點(diǎn)擊Button時(shí),仍能被Parent的onClick事件所捕獲

代碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容