【React系列】狀態(State)和生命週期

在上一篇中寫過,組件能夠分爲函數式組件和類組件,而且更新組件的方法也給出了經過傳入ReactDOM.render()方法進行更新。可是這種方式並不能很好地進行封裝成獨立功能的組件,一些操做會由外部進行控制。而咱們理想中的組件應該是一個功能獨立的個體,只是不一樣場合不一樣的數據纔會出現不一樣。
而這就就關聯到了咱們此次的主題---狀態(State)

狀態(State)

什麼是狀態

狀態(state) 和 屬性(props) 相似,都是一個組件所須要的一些數據集合,可是它是私有的,而且由組件自己徹底控制,能夠認爲它是組件的「私有屬性(或者是局部屬性)」。數組

函數式組件轉化爲類組件

若是想要使用狀態(State)的話,則須要咱們在構建組件的時候是要以類組件爲形式的。
針對直接以類組件形式構造的組件不須要變化,那麼針對函數式組件該如何轉化呢?異步

  • 建立一個繼承自 React.Component 類的 ES6 class 同名類。
  • 添加一個名爲 render() 的空方法。
  • 把原函數中的全部內容移至 render() 中。
  • 在 render() 方法中使用 this.props 替代 props。
  • 刪除保留的空函數聲明。

例如:函數

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

上面示例就是一個簡單的類組件Clock。性能

類組件中添加State

1) 替換 render() 方法中的 this.props.date 爲 this.state.date:優化

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

2) 添加一個 類構造函數(class constructor) 初始化 this.state:this

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
❤ 注意咱們如何將 props 傳遞給基礎構造函數:
constructor(props) {
  super(props); ***
  this.state = {date: new Date()};
}

3) 移除 <Clock /> 元素中的 date 屬性:code

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

經過上面的步驟就能夠實現添加State到類組件了,最終結果:component

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

如何修改State

State的初始化和常規的賦值形式是同樣的,可是若是咱們須要更新相關數據的話,則須要經過setState方法進行修改,如:對象

this.setState({
  date: new Date()
});

正確地使用 State(狀態)

關於如何正確使用State以及setState() 有三個方面須要注意:繼承

不要直接修改 state(狀態)

正如上面提到的如何修改State那樣,在初始化後,若是修改的話只能經過setState方法進行修改。

state(狀態) 更新多是異步的

React 爲了優化性能,有可能會將多個 setState() 調用合併爲一次更新。

由於 this.props 和 this.state 多是異步更新的,你不能依賴他們的值計算下一個state(狀態)。
如:

// 錯誤
this.setState({
  counter: this.state.counter + this.props.increment,
});

要彌補這個問題,使用另外一種 setState() 的形式,它接受一個函數而不是一個對象。這個函數將接收前一個狀態做爲第一個參數,應用更新時的 props 做爲第二個參數:

// 正確
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

state(狀態)更新會被合併

當你調用 setState(), React 將合併你提供的對象到當前的狀態中。因此當State是一個多鍵值的結構時,能夠單獨更新其中的一個,此時會進行「差分」更新,不會影響其餘的屬性值。

生命週期方法

什麼是生命週期

生命週期就是指一個對象的生老病死。

而組件的生命週期則是這個組件從建立到銷燬的一個過程。在這個過程當中會有不一樣的階段,從而會產生一些對應的生命週期函數來供咱們使用,以便可以進行一些渲染、更新等處理。

能夠簡單分爲幾個階段:

  • 初始化階段

    • getDefaultProps:獲取實例的默認屬性(即便沒有生成實例,組件的第一個實例被初始化CreateClass的時候調用,只調用一次,)
    • getInitialState:獲取每一個實例的初始化狀態(每一個實例本身維護)
    • componentWillMount:組件即將被裝載、渲染到頁面上(render以前最好一次修改狀態的機會)
    • render:組件在這裏生成虛擬的DOM節點(只能訪問this.props和this.state;只有一個頂層組件,也就是說render返回值值職能是一個組件;不容許修改狀態和DOM輸出)
    • componentDidMount:組件真正在被裝載以後,能夠修改DOM
  • 運行中狀態

    • componentWillReceiveProps:組件將要接收到屬性的時候調用(趕在父組件修改真正發生以前,能夠修改屬性和狀態)
    • shouldComponentUpdate:組件接受到新屬性或者新狀態的時候(能夠返回false,接收數據後不更新,阻止render調用,後面的函數不會被繼續執行了)
    • componentWillUpdate:不能修改屬性和狀態
    • componentDidUpdate:能夠修改DOM
  • 銷燬階段

    • componentWillUnmount:開發者須要來銷燬(組件真正刪除以前調用,好比計時器和事件監聽器)

數據向下流動

不管做爲父組件仍是子組件,它都沒法獲悉一個組件是否有狀態,同時也不須要關心另外一個組件是定義爲函數組件仍是類組件。

這就是 state(狀態) 常常被稱爲 本地狀態 或 封裝狀態的緣由。 它不能被擁有並設置它的組件 之外的任何組件訪問。

一個組件能夠選擇將 state(狀態) 向下傳遞,做爲其子組件的 props(屬性):

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

這一般稱爲一個「從上到下」,或者「單向」的數據流。任何 state(狀態) 始終由某個特定組件全部,而且從該 state(狀態) 導出的任何數據 或 UI 只能影響樹中 「下方」 的組件。

若是把組件樹想像爲 props(屬性) 的瀑布,全部組件的 state(狀態) 就如同一個額外的水源匯入主流,且只能隨着主流的方向向下流動。

結語

在 React 應用中,一個組件是不是有狀態或者無狀態的,被認爲是組件的一個實現細節,隨着時間推移可能發生改變。你能夠在有狀態的組件中使用無狀態組件,反之亦然。

相關文章
相關標籤/搜索