組件的 state 和 setState

state

咱們前面提到過,一個組件的顯示形態是能夠由它數據狀態和配置參數決定的。一個組件能夠擁有本身的狀態,就像一個點贊按鈕,能夠有「已點贊」和「未點贊」狀態,而且能夠在這兩種狀態之間進行切換。React.js 的 state 就是用來存儲這種可變化的狀態的。css

咱們仍是拿點贊按鈕作例子,它具備已點贊和未點贊兩種狀態。那麼就能夠把這個狀態存儲在 state 中。修改 src/index.js 爲:html

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'

class LikeButton extends Component {
  constructor () {
    super()
    this.state = { isLiked: false }
  }

  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }

  render () {
    return (
      <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked ? '取消' : '點贊'} 👍
      </button>
    )
  }
}
...

isLiked 存放在實例的 state 對象當中,這個對象在構造函數裏面初始化。這個組件的 render 函數內,會根據組件的 state 的中的isLiked不一樣顯示「取消」或「點贊」內容。而且給 button 加上了點擊的事件監聽。react

最後構建一個 Index ,在它的 render 函數內使用 LikeButton 。而後把 Index渲染到頁面上:緩存

...
class Index extends Component {
  render () {
    return (
      <div>
        <LikeButton />
      </div>
    )
  }
}

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

setState 接受對象參數

在 handleClickOnLikeButton 事件監聽函數裏面,你們能夠留意到,咱們調用了 setState 函數,每次點擊都會更新 isLiked 屬性爲 !isLiked,這樣就能夠作到點贊和取消功能。dom

setState 方法由父類 Component 所提供。當咱們調用這個函數的時候,React.js 會更新組件的狀態 state ,而且從新調用 render 方法,而後再把 render 方法所渲染的最新的內容顯示到頁面上。函數

注意,當咱們要改變組件的狀態的時候,不能直接用 this.state = xxx 這種方式來修改,若是這樣作 React.js 就沒辦法知道你修改了組件的狀態,它也就沒有辦法更新頁面。因此,必定要使用 React.js 提供的 setState 方法,它接受一個對象或者函數做爲參數。性能

傳入一個對象的時候,這個對象表示該組件的新狀態。但你只須要傳入須要更新的部分就能夠了,而不須要傳入整個對象。例如,假設如今咱們有另一個狀態 name :this

...
  constructor (props) {
    super(props)
    this.state = {
      name: 'Tomy',
      isLiked: false
    }
  }

  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }
...

由於點擊的時候咱們並不須要修改 name,因此只須要傳入 isLiked 就好了。Tomy 仍是那個 Tomy,而 isLiked 已經不是那個 isLiked 了。spa

setState 接受函數參數

這裏還有要注意的是,當你調用 setState 的時候,React.js 並不會立刻修改 state。而是把這個對象放到一個更新隊列裏面,稍後纔會從隊列當中把新的狀態提取出來合併到 state 當中,而後再觸發組件更新。這一點要好好注意。能夠體會一下下面的代碼:code

...
  handleClickOnLikeButton () {
    console.log(this.state.isLiked)
    this.setState({
      isLiked: !this.state.isLiked
    })
    console.log(this.state.isLiked)
  }
...

你會發現兩次打印的都是 false,即便咱們中間已經 setState 過一次了。這並非什麼 bug,只是 React.js 的 setState 把你的傳進來的狀態緩存起來,稍後纔會幫你更新到 state 上,因此你獲取到的仍是原來的 isLiked

因此若是你想在 setState 以後使用新的 state 來作後續運算就作不到了,例如:

...
  handleClickOnLikeButton () {
    this.setState({ count: 0 }) // => this.state.count 仍是 undefined
    this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN
    this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN
  }
...

上面的代碼的運行結果並不能達到咱們的預期,咱們但願 count 運行結果是 3 ,但是最後獲得的是 NaN。可是這種後續操做依賴前一個 setState 的結果的狀況並不罕見。

這裏就天然地引出了 setState 的第二種使用方式,能夠接受一個函數做爲參數。React.js 會把上一個 setState 的結果傳入這個函數,你就可使用該結果進行運算、操做,而後返回一個對象做爲更新 state 的對象:

...
  handleClickOnLikeButton () {
    this.setState((prevState) => {
      return { count: 0 }
    })
    this.setState((prevState) => {
      return { count: prevState.count + 1 } // 上一個 setState 的返回是 count 爲 0,當前返回 1
    })
    this.setState((prevState) => {
      return { count: prevState.count + 2 } // 上一個 setState 的返回是 count 爲 1,當前返回 3
    })
    // 最後的結果是 this.state.count 爲 3
  }
...

這樣就能夠達到上述的利用上一次 setState 結果進行運算的效果。

setState 合併

上面咱們進行了三次 setState,可是實際上組件只會從新渲染一次,而不是三次;這是由於在 React.js 內部會把 JavaScript 事件循環中的消息隊列的同一個消息中的 setState 都進行合併之後再從新渲染組件。

深層的原理並不須要過多糾結,你只須要記住的是:在使用 React.js 的時候,並不須要擔憂屢次進行 setState 會帶來性能問題。

相關文章
相關標籤/搜索