Understanding React `setState` 翻譯

Zwe1發表於2018-05-19

瞭解React中的setState

reacr元件通常都包含狀態。狀態可以是任何東西,想象一下使用場景:一個使用者是否進行了登陸,根據賬戶的啟用狀態來展示準確的使用者名稱。或者 是一系列部落格文章。或者一個模態框是否開啟以及其中的哪個標籤是啟用狀態的。包含狀態的React元件其渲染也依賴於這些狀態。當元件的狀態發生改變,元件也會 發生改變。

setState的工作機制

setState()是在初始化狀態之後唯一合法的更新狀態的方式。比如我們有一個搜尋元件,並且想展示使用者提交的搜尋內容。
如此設定:

import React, { Component } from 'react'

class Search extends Component {
  constructor(props) {
    super(props)

    state = {
      searchTerm: ''
    }
  }
}
複製程式碼

我們用一個空字串來初始化狀態,並且需要通過呼叫setState()來更新狀態searchTerm

setState({ searchTerm: event.target.value })
複製程式碼

此處,我們向setState()傳遞了一個物件。這個物件包含了我們想要更新的這一部分狀態,此處,也就是searchItem的值。React獲取這個值,然後將它合併到需要它的物件中。這有點像Search元件在向searchTerm狀態索取它所需要的值,setState()給了它這個答案。

setState

這開啟了一個React中叫做reconciliation(調諧)的過程。調諧過程是React更新DOM的方式,根據狀態的改變來改變元件。當setState()觸發時,React建立了一個包含元件中可響應元素(根據更新後的狀態)的新樹。通過這課樹與前樹的比較來確定Search元件的UI如何根據狀態的改變而響應式的發生變化。React知道要實現哪些更改,並且只會在必要時更新DOM的某些部分。這就是React很快的原因。

上面聽起來有些多,所以總結成一些這些:

  • 我們有一個展示搜尋項的搜尋元件。
  • 搜尋專案前為控。
  • 使用者提交了搜尋內容。
  • 搜尋值被捕獲然後被setState()儲存到值中。
  • 調諧進行然後React察覺到了值的變化。
  • React指示搜尋元件更新值然後將搜尋項進行合併。

調諧過程沒必要改變整棵樹,除非一種情況發生,像下面這種樹的根節點發生了變化。

// old
<div>
  <Search />
</div>

// new
<span>
  <Search />
</span>
複製程式碼

所有的<div>標籤變成了<span>標籤導致整棵元件樹被重新整理。

一條經驗法則:永遠不要直接改變state。使用setState()來改變狀態。像下面片段這樣直接修改state是無法讓元件重新渲染的。

// do not do this
this.state = {
  searchTerm: event.target.value
}
複製程式碼

向'setState()'傳遞一個函式

為了進一步的演示這個想法,讓我們建立一個簡單的計數器,通過點選來增加減少數字。

讓我們來註冊元件然後為UI定義標記。

class App extends React.Component {

state = { count: 0 }

handleIncrement = () => {
  this.setState({ count: this.state.count + 1 })
}

handleDecrement = () => {
  this.setState({ count: this.state.count - 1 })
}
  render() {
    return (
      <div>
        <div>
          {this.state.count}
        </div>
        <button onClick={this.handleIncrement}>Increment by 1</button>
        <button onClick={this.handleDecrement}>Decrement by 1</button>
      </div>
    )
  }
}
複製程式碼

這裡,計數器只是簡單的在每次點選後增加或者減少1。

假如如果我們想要每次增減3呢?我們或許會像下面這樣嘗試在handleIncrementhandleDecrement呼叫三次setState():

handleIncrement = () => {
  this.setState({ count: this.state.count + 1 })
  this.setState({ count: this.state.count + 1 })
  this.setState({ count: this.state.count + 1 })
}

handleDecrement = () => {
  this.setState({ count: this.state.count - 1 })
  this.setState({ count: this.state.count - 1 })
  this.setState({ count: this.state.count - 1 })
}
複製程式碼

如果你自己在家裡程式設計,你可能驚訝的發現它並沒有效果。

上面的程式碼片段相當於:

Object.assign(  
  {},
  { count: this.state.count + 1 },
  { count: this.state.count + 1 },
  { count: this.state.count + 1 },
)
複製程式碼

Object.assign()用於將資料從源物件拷貝到目標物件。如果拷貝的源物件有相同的鍵,像上例這樣,後面的值會覆蓋前面的值。這裡有一個簡單的Object.assign()案例:

let count = 3

const object = Object.assign({}, 
  {count: count + 1}, 
  {count: count + 2}, 
  {count: count + 3}
);

console.log(object);
// output: Object { count: 6 }
複製程式碼

所以呼叫不是執行了三次,而是一次。我們可以通過傳遞一個函式給setState()來修復這個問題。正如你傳遞給setState()一個物件,同樣的也可以傳遞函式,這是解決上述情況的辦法。

我們可以這樣寫handleIncrement函式:

handleIncrement = () => {
  this.setState((prevState) => ({ count: prevState.count + 1 }))
  this.setState((prevState) => ({ count: prevState.count + 1 }))
  this.setState((prevState) => ({ count: prevState.count + 1 }))
}
複製程式碼

現在我們就可以通過一次點選來增加數量三次了。

在這個案例中,React不會對其進行合併,而是根據函式的順序執行,然後在函式全部執行完之後更新狀態。這會將計數更新到3而不是1.

使用更新器訪問之前的狀態

當我們構建React應用時,經常會遇到需要通過之前的狀態來己算現在的狀態。我們並不能夠在總是呼叫setState()後,通過this.state拿到準確的狀態值,結果是它(this.state)總是和螢幕上渲染的狀態相同。

讓我們回到計數的例子中來看看它是如何工作的。假設我們有一個每次將計數遞減1的函式。函式像下面這樣:

changeCount = () => {
  this.setState({ count: this.state.count - 1})
}
複製程式碼

我們想要的是能夠減去3,changeCount()函式被繫結在點選事件上的呼叫了3次,像這樣:

handleDecrement = () => {
  this.changeCount()
  this.changeCount()
  this.changeCount()
}
複製程式碼

每次減少的按鈕被點選,計數減少了1而不是3。這是因為this.state.count直到元件被重新渲染之後才進行了更新。解決方式是使用更新器。更新器允許你訪問當前狀態並立即使用它來更新其他專案。所以我們的changeCount()函式應該是這個樣子:

changeCount = () => {
  this.setState((prevState) => {
    return { count: prevState.count - 1}
  })
}
複製程式碼

現在我們不再依賴於this.state的結果了。count的狀態彼此依賴,所以我們現在可以在每次呼叫changeCount()之後獲取到準確的狀態值。

setState()被視為非同步————也就是說,不要總是期待呼叫setState()之後,狀態就會被改變。

總結

在使用setState()時,你需要知道非常重要的幾點:

  • 一定使用setState()來更新元件狀態
  • setState()可以接收物件或者函式
  • 當多次更新狀態時,傳遞一個函式
  • 不要想在呼叫setState()後立即能夠使用this.state獲取準確的狀態,代替的使用更新器來完成。

原文連線

https://css-tricks.com/understanding-react-setstate/?utm_source=ponyfoo+weekly&utm_medium=email&utm_campaign=113

相關文章