1. 程式人生 > >React setState 的一些`坑`

React setState 的一些`坑`

  • setState

今天發現有很多文章在說setState的坑,吐槽之聲也不少.其實我就碰到過一個.

setState不會立即改變資料

// name is ""
this.setState({
    name: "name"
})
console.log(`name is ${this.state.name}`)

這段程式碼沒有按我的預料輸出name.

所以如果需要獲取,就需要在回撥函式裡去做.

this.setState({
    name: "name"
}, () => {
  console.log(`name is ${this
.state.name}`) })

這樣才會如你所預料那般的輸出.

對於上面的這個問題倒是還好.因為setState是放在一個佇列裡非同步去處理的, 所以在沒有成功改變之前,輸出的就是之前的值.
在回撥裡顯示正確的資料,那是因為callback的原因.

setState多次,re-render一次

這個是我始料未及的了,我一直以為每次setState都會造成一次re-render.其實並不是這樣.

componentDidMount(){
  this.setState((prevState, props) => ({count: this.state.count + 1
})) // 1 this.setState((prevState, props) => ({count: this.state.count + 1})) // 2 this.setState((prevState, props) => ({count: this.state.count + 1})) // 3 this.setState({name: "xiaohesong"}) // 4 } render(){ console.log("render") return(<h1>SetState</h1>) }

可以發現,這裡只會出現render兩次,而不是想象中的4

+1(初始化的render).
WTF! Why?

好吧,還是得尋找原因不是?

我們之前說他是塞進一個佇列裡去做非同步的處理的,就是說他是把我們這4setState操作放到了一個佇列裡,然後batch操作.啥啥啥?恩,還是來程式碼闡述下比較好.

  this.setState((prevState, props) => ({count: this.state.count + 1})) // 1
  this.setState((prevState, props) => ({count: this.state.count + 1})) // 2
  this.setState((prevState, props) => ({count: this.state.count + 1})) // 3
  this.setState({name: "xiaohesong"}) // 4
}

上面的這段程式碼裡的這四個會被塞進佇列裡進行批量操作.批量操作?

Object.assign(state, {count: this.state.count + 1}, {count: this.state.count + 1}, ..., {name: "xiaohesong"})

如果把上面的程式碼換成非同步的呢?

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

可以發現,如果改成這樣,也會觸發re-render. 可是這是為啥setTimeout裡的兩次this.state.count會成功,而不是像上面一樣,淺合併成一個呢?
這個還得繼續探索.

總結

  1. setState操作,預設情況下是每次呼叫, 都會re-render一次,除非你手動shouldComponentUpdatefalse.
    react為了減少rerender的次數,會進行一個淺合併.將多次re-render減少到一次re-render.

  2. setState之後,無法立即獲取到this.state的值,是因為在setState的時候,他只會把操作放到佇列裡.

參考連結

  1. 原文連結
  2. anhuiliujun react Api之setState與forceUpdate
  3. setState() Gate