JavaScript
es6
React

Reactのイベントハンドラ中のfetchのイベントハンドラでthisを使うときはアロー関数を使う

jsは初心者なんだけど、せっかくなのでReact使ってちょっとしたデモページを作ろうとして少しはまった話。

以下のようなクラスで、submitされたらとあるREST APIを叩いてレスポンスで特定の要素の表示を切り替えたいと思ったのだけど、fetchのハンドラの最後でsetStateが見つからないと言われる。つまりthisがうまく渡っていない。

    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: '結果表示欄'};

        this.handleSubmit = this.handleSubmit.bind(this);
      }
      handleSubmit(event) {
        const formData = new FormData(event.target)
        event.preventDefault();

        fetch(event.target.action, {
          method: 'POST',
          body: formData
        }).then(function(response) {
          return response.json()
        }).then(function(json) {
          this.setState((state, props) => {
            return {value: JSON.stringify(json)}
          });
        })
      }
      render() {
        return (
          <div>
            <form action="/correct" method="POST" onSubmit={this.handleSubmit}>
            <RadioButtonParam label="l1" name="edit_dist_score_mode" params={[{value: "1", label: "L", checked: true}, {value: "2", label: "P"}] } />
            <TextParam label="target" name="target" />
            <p><input type="submit" value="実行" /></p>
            </form>
            <div> {this.state.value} </div>
          </div>
        )

あれこれ調べるとbindが必要とかes5のときはどうとかいろいろあって(reactの情報は新旧入り交じってて探しづらい..)、最終的に こちらの以下の記載に行き着いた。

Using a ES6 fat-arrow function means that this is bound to something else than the body of the function at hand.

functionでなくアロー関数で書けばthisが維持されるっぽい。
というわけで、下の形にしたらうまく行った。

    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: '結果表示欄'};

        this.handleSubmit = this.handleSubmit.bind(this);
      }
      handleSubmit(event) {
        const formData = new FormData(event.target)
        event.preventDefault();

        fetch(event.target.action, {
          method: 'POST',
          body: formData
        }).then(function(response) {
          return response.json()
        }).then((json) => {
          this.setState((state, props) => {
            return {value: JSON.stringify(json)}
          });
        })
      }
      render() {
        return (
          <div>
            <form action="/correct" method="POST" onSubmit={this.handleSubmit}>
            <RadioButtonParam label="l1" name="edit_dist_score_mode" params={[{value: "1", label: "L", checked: true}, {value: "2", label: "P"}] } />
            <TextParam label="target" name="target" />
            <p><input type="submit" value="実行" /></p>
            </form>
            <div> {this.state.value} </div>
          </div>
        )