咱們前面提到過,一個組件的顯示形態是能夠由它數據狀態和配置參數決定的。一個組件能夠擁有本身的狀態,就像一個點贊按鈕,能夠有「已點贊」和「未點贊」狀態,而且能夠在這兩種狀態之間進行切換。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') )
在 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
的時候,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
,可是實際上組件只會從新渲染一次,而不是三次;這是由於在 React.js 內部會把 JavaScript 事件循環中的消息隊列的同一個消息中的 setState
都進行合併之後再從新渲染組件。
深層的原理並不須要過多糾結,你只須要記住的是:在使用 React.js 的時候,並不須要擔憂屢次進行 setState
會帶來性能問題。