React學習 —— 受控組件、生命周期、PureComponent

受控組件與非受控組件

受控組件:

在 HTML 中,表單元素(如<input><textarea><select>)之類的表單元素通常自己維護 state,并根據用戶輸入進行更新。而在 React 中,可變狀態(mutable state)通常保存在組件的 state 屬性中,并且只能通過使用 setState()來更新。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

該組件實現了一個點擊提交,打印名字的功能。
有時使用受控組件會很麻煩,因為你需要為數據變化的每種方式都編寫事件處理函數(因為數據都存在state里,所以需要編寫事件處理函數更新state,就比如這里都handleChange),并通過一個 React 組件傳遞所有的輸入 state。

非受控組件

要編寫一個非受控組件,而不是為每個狀態更新都編寫數據處理函數,你可以 使用 ref 來從 DOM 節點中獲取表單數據。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

這里和受控組件都區別就在于,我們并沒有給input賦值(value={this.state.xxx}),數據本身都是存儲在dom節點本身的,每次我們需要獲取或者改變的時候我們應該直接去通過ref操作dom節點,而非設置state。

默認值

如果是受控組件,value就可以充當默認值,而非受控組件由于沒有可以傳遞的地方本應該是除了直接操作dom之外沒有設置默認值的方法的,但是react提供了封裝,我們可以在dom上添加default值:

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <label>
        Name:
        <input
          defaultValue="Bob"
          type="text"
          ref={this.input} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

生命周期

常用的生命周期方法

本節中的方法涵蓋了創建 React 組件時能遇到的絕大多數用例。想要更好了解這些方法,可以參考生命周期圖譜

render()

只能返回:React、fragments、Portals、字符串或者數值、布爾類型或 null。

constructor()

通常,在 React 中,構造函數僅用于以下兩種情況:

  • 通過給 this.state 賦值對象來初始化內部 state
  • 事件處理函數綁定實例
    如果不做這些處理,則不需要constructor()
    避免將 props 的值復制給 state!這是一個常見的錯誤!!
constructor(props) {
 super(props);
 // 不要這樣做
 this.state = { color: props.color };
}

componentDidMount()

會在組件掛載后(插入 DOM 樹中)立即調用。
這個方法是比較適合添加訂閱的地方。如果添加了訂閱,請不要忘記在 componentWillUnmount() 里取消訂閱。

你可以在 componentDidMount() 里可以直接調用 setState()。它將觸發額外渲染,但此渲染會發生在瀏覽器更新屏幕之前。如此保證了即使在 render() 兩次調用的情況下,用戶也不會看到中間狀態。請謹慎使用該模式,因為它會導致性能問題。通常,你應該在 constructor() 中初始化 state。如果你的渲染依賴于 DOM 節點的大小或位置,比如實現 modals 和 tooltips 等情況下,你可以使用此方式處理

componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 會在更新后會被立即調用。首次渲染不會執行此方法。
當組件更新后,可以在此處對 DOM 進行操作。如果你對更新前后的 props 進行了比較,也可以選擇在此處進行網絡請求。(例如,當 props 未發生變化時,則不會執行網絡請求)。

componentWillUnmount()

componentWillUnmount() 會在組件卸載及銷毀之前直接調用。在此方法中執行必要的清理操作,例如,清除 timer,取消網絡請求或清除在 componentDidMount() 中創建的訂閱等。
componentWillUnmount() 中不應調用 setState(),因為該組件將永遠不會重新渲染。組件實例卸載后,將永遠不會再掛載它。

shouldComponentUpdate()

當 props 或 state 發生變化時,shouldComponentUpdate() 會在渲染執行之前被調用。返回值默認為 true。首次渲染或使用 forceUpdate() 時不會調用該方法。
此方法僅作為性能優化的方式而存在。不要企圖依靠此方法來“阻止”渲染,因為這可能會產生 bug。

我們不建議在 shouldComponentUpdate() 中進行深層比較或使用 JSON.stringify()。這樣非常影響效率,且會損害性能。

其他API

setState()

setState(updater[, callback])

將 setState() 視為請求而不是立即更新組件的命令。為了更好的感知性能,React 會延遲調用它,然后通過一次傳遞更新多個組件。React 并不會保證 state 的變更會立即生效。
setState() 并不總是立即更新組件。它會批量推遲更新。這使得在調用 setState() 后立即讀取 this.state 成為了隱患。為了消除隱患,請使用 componentDidUpdate 或者 setState 的回調函數(setState(updater, callback)),這兩種方式都可以保證在應用更新后觸發。如需基于之前的 state 來設置當前的 state,請閱讀下述關于參數 updater 的內容。

this.setState((state, props) => {
  return {counter: state.counter + props.step};
});

updater 函數中接收的 state 和 props 都保證為最新。updater 的返回值會與 state 進行淺合并。
有關更多詳細信息,請參閱:

forceUpdate()

默認情況下,當組件的 state 或 props 發生變化時,組件將重新渲染。如果 render() 方法依賴于其他數據,則可以調用 forceUpdate() 強制讓組件重新渲染。
調用 forceUpdate() 將致使組件調用 render() 方法,此操作會跳過該組件的 shouldComponentUpdate()。

ReactDOM

react-dom 的 package 提供了可在應用頂層使用的 DOM(DOM-specific)方法,如果有需要,你可以把這些方法用于 React 模型以外的地方。不過一般情況下,大部分組件都不需要使用這個模塊。

React.Component與React.PureComponent

React.PureComponentReact.Component 很相似。兩者的區別在于 React.Component 并未實現 shouldComponentUpdate(),而 React.PureComponent 中以淺層對比 prop 和 state 的方式來實現了該函數。

shouldComponentUpdate在剛剛的生命周期中也說過,當props或者state發生變化時,shouldComponentUpdate() 會在渲染執行之前被調用。
返回默認值true,首次渲染和 forceUpdate() 時不會調用該方法。

如果 shouldComponentUpdate() 返回 false,則不會調用 UNSAFE_componentWillUpdate()render()componentDidUpdate()
而React.Component的shouldComponentUpdate默認就是返回true的,React.PureComponent是實現一套邏輯,淺比較props和state,并減少了跳過必要更新的可能性。

什么是淺比較?
對于基本類型(primitives),例如數字或者布爾值,來說,淺拷貝將會檢查其值是否相同,例如1與1相等,true與true相等。對于引用類型的變量,例如復雜的javascript對象或者數組,來說,淺拷貝將僅僅檢查它們的引用值是否相等。這意味著,對于引用類型的變量來說,如果我們只是更新了其中的一個元素,例如更新了數組中某一位置的值,那么更新前后的數組仍是相等的。

因此意味著相比于Component,PureCompoent的性能表現將會更好。但使用PureCompoent要求滿足如下條件:

  • props和state都不可變
  • props和state沒有層級
  • 如果數據改變無法反應在淺拷貝上,應該調用forceUpdate更新。

React.PureComponent 中的 shouldComponentUpdate() 僅作對象的淺層比較。如果對象中包含復雜的數據結構,則有可能因為無法檢查深層的差別,產生錯誤的比對結果。僅在你的 props 和 state 較為簡單時,才使用 React.PureComponent,或者在深層數據結構發生變化時調用 forceUpdate() 來確保組件被正確地更新。

參考文獻

https://react.docschina.org/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 作為一個合格的開發者,不要只滿足于編寫了可以運行的代碼。而要了解代碼背后的工作原理;不要只滿足于自己的程序...
    六個周閱讀 8,526評論 1 33
  • 生命周期流程圖簡單如下: 組件讓你把用戶界面分成獨立的,可重復使用的部分,并且將每個部分分開考慮。React.Co...
    Simple_Learn閱讀 1,100評論 0 0
  • 說在前面 關于 react 的總結過去半年就一直碎碎念著要搞起來,各(wo)種(tai)原(lan)因(le)。心...
    陳嘻嘻啊閱讀 6,917評論 7 41
  • 40、React 什么是React?React 是一個用于構建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,058評論 0 1
  • 安裝: 概述 React起源于FaceBook的內部項目,因為該公司對市場上所有的JavaScript MVC框架...
    姒沝無痕閱讀 738評論 0 0