React.js 小書 Lesson10 - 組件的 state 和 setState


React.js 小書 Lesson10 - 組件的 state 和 setState

本文作者:胡子大哈
本文原文:http://huziketang.com/books/react/lesson10

轉載請注明出處,保留原文鏈接以及作者信息

在線閱讀:http://huziketang.com/books/react


state

我們前面提到過,一個組件的顯示形態是可以由它數據狀態和配置參數決定的。一個組件可以擁有自己的狀態,就像一個點贊按鈕,可以有“已點贊”和“未點贊”狀態,并且可以在這兩種狀態之間進行切換。React.js 的 state 就是用來存儲這種可變化的狀態的。

[圖片上傳失敗...(image-9c2e52-1510226132684)]

我們還是拿點贊按鈕做例子,它具有已點贊和未點贊兩種狀態。那么就可以把這個狀態存儲在 state 中。修改 src/index.js 為:

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 加上了點擊的事件監聽。

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

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

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

setState 接受對象參數

handleClickOnLikeButton 事件監聽函數里面,大家可以留意到,我們調用了 setState 函數,每次點擊都會更新 isLiked 屬性為 !isLiked,這樣就可以做到點贊和取消功能。

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

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

傳入一個對象的時候,這個對象表示該組件的新狀態。但你只需要傳入需要更新的部分就可以了,而不需要傳入整個對象。例如,假設現在我們有另外一個狀態 name

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

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

因為點擊的時候我們并不需要修改 name,所以只需要傳入 isLiked 就行了。Tomy 還是那個 Tomy,而 isLiked 已經不是那個 isLiked 了。

setState 接受函數參數

這里還有要注意的是,當你調用 setState 的時候,React.js 并不會馬上修改 state。而是把這個對象放到一個更新隊列里面,稍后才會從隊列當中把新的狀態提取出來合并到 state 當中,然后再觸發組件更新。這一點要好好注意。可以體會一下下面的代碼:

...
  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 會帶來性能問題。

下一節中我們將介紹《React.js 小書 Lesson11 - 配置組件的 props》

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

推薦閱讀更多精彩內容