點燈坊

失くすものさえない今が強くなるチャンスよ

如何使用 useState() 取得正確 State ?

Sam Xiao's Avatar 2019-09-29

React 的 setState()useState() 的 Setter,其實都不是立即更新 State,因此直接從 State 取值,並不是最好的做法。

Version

macOS Mojave 10.14.6
WebStorm 2019.2.3
Node 12.11.0
Yarn 1.19.0
create-react-app 3.1.2
React 16.10.1

Class Component

錯誤寫法

App.js

import React, { Component } from 'react';

export default class extends Component {
  state = {
    count: 0,
  };

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

  render() {
    return (
      <div>
        <button onClick={ this.addCount }>+</button>
        <div>{ this.state.count }</div>
      </div>
    );
  }
}

export default App;

第 8 行

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

其實 setState() 並不會立即更新 state,所以直接使用 side effect 從 this.state 不一定會抓到正確值。

正確寫法

App.js

import React, { Component } from 'react';

export default class extends Component {
  state = {
    count: 0,
  };

  addCount = () => {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  };

  render() {
    return (
      <div>
        <button onClick={ this.addCount }>+</button>
        <div>{ this.state.count }</div>
      </div>
    );
  }
}

第 8 行

addCount = () => {
  this.setState(prevState => ({
    count: prevState.count + 1
  }));
}

正確寫法應該在 setState() 使用沒有 side effect 的 pure function 為 callback,其 prevState 才會傳回更新後正確值。

Function Component

錯誤寫法

App.js

import React, { useState } from 'react';

export default () => {
  let [count, setCount] = useState(0);
  
  let addCount = () => setCount(count + 1);

  return (
    <div>
      <button onClick={ addCount }>+</button>
      <div>{ count }</div>
    </div>
  );
};

第 6 行

let addCount = () => setCount(count + 1);

其實 setCount() 並不會立即更新 state,所以直接使用 side effect 從 count 不一定會抓到正確值。

正確寫法

App.js

import React, { useState } from 'react';

export default () => {
  let [count, setCount] = useState(0);

  let addCount = () => setCount(prevCount => prevCount + 1);

  return (
    <div>
      <button onClick={ addCount }>+</button>
      <div>{ count }</div>
    </div>
  );
};

export default App;

第 6 行

let addCount = () => setCount(prevCount => prevCount + 1);

正確寫法應該在 setCount() setter 使用沒有 side effect 的 callback,其 prevCount argument 才會傳回更新後正確值。

Conclusion

  • Class component 的 setState() 傳回的是 state object;而 function component 的 setter 傳回的是 state variable
  • 無論是 class component 的 setState(),或者是 function component 的 setter,其 API 都是使用 callback 來表現 state 不會立即修改,而是修改後傳回正確值,語意甚佳

Reference

Reed Barger, Use Previous State with useState