【入力値反映されない】Reactでクラスを使ったときにフォームでハマった話【Cannot read property 'state' of undefined】

この記事は「[株式会社オープンストリーム "小ネタ" Advent Calendar 2020](https://qiita.com/advent-calendar/2020/opst-mini)」の 3 日目の記事です。

React でフォームを実装したときにいきなりハマった点を共有したいと思います。

## 環境

```bash
$ node --version
v14.15.0
```

```json:package.json
{
...
  "dependencies": {
    "next": "10.0.3",
    "react": "17.0.1",
    "react-dom": "17.0.1"
  },
  "devDependencies": {
    "@types/node": "^14.14.10",
    "@types/react": "^17.0.0",
    "typescript": "^4.1.2"
  }
}

```

ちなみに私はVue(Nuxt)を使っていてReactに入門しています…！

## state に紐づけたテキストボックスに入力しても反映されない

これは[Reactのドキュメント](https://ja.reactjs.org/docs/forms.html)に書いてあるので、初めに読めば防げる問題ですが…

React で管理したいテキストボックスには `handleChange` 関数と `onChange` イベントを入れないと「入力しても反映されない」現象が起きます。

```tsx:src/App.tsx
import React, { Component } from 'react';
import './App.css';

type Props = {}

type State = {
  todo: TodoItem[],
  currentText: string
}

type TodoItem = {
  title: String
}

class App extends Component<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state =  {
      todo: [
        { title: 'JavaScript覚える' },
        { title: 'jQuery覚える' },
        { title: 'ES2015覚える' },
        { title: 'React覚える' }
       ],
       currentText: ''
    }

    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(event: React.ChangeEvent<HTMLInputElement>) {
    this.setState({
      currentText: event.target.value
    })
  }

    render() {
    return (
    ...

        <input type="text" value={this.state.currentText} onChange={this.handleChange}/> <input type="button" value="追加" onClick={this.addTodo} />
      </div>
    );
  }
```

さらに、TypeScript で書くときはテキストボックスに入力したときの Event の型も指定します。
any にしなくても次の Event 型から使うことができます。

any 型で諦めない React.EventCallback - Qiita
https://qiita.com/Takepepe/items/f1ba99a7ca7e66290f24


## クラス内の関数でstateを操作するとCannot read property 'state' of undefinedと出る

ボタンをクリックしたときにフォームで入力した値を使いたいですよね？ところがボタンをクリックしたときに次のメッセージが表示されます。

> TypeError: Cannot read property 'state' of undefined

クラスの「中で」関数を書く場合、`state` など `this` から使うものは **関数をアロー関数にしないと関数が呼び出されたときに `this` がバインドされていない状態になってエラーになります。**

アロー関数で書いてみると次の通りになります。

```tsx:src/App.tsx
class App extends Component<Props, State> {

  ...
  addTodo =()=> {
    this.state.todo.push({
      title: this.state.currentText
    })

    this.setState({
      todo: this.state.todo,
      currentText: ''
    })
  }
  
  deleteTodo = (i: number)=> {
    this.state.todo.splice(i, 1)
    this.setState({
      todo: this.state.todo
    })
  }
  
  render() {
    return (
      <div>
        <h1>TODOアプリ</h1>
        <nav>
        <ul>{this.state.todo.map( (todo, i) => {
          return <li key={i}><input type="button" value="☓" onClick={() => this.deleteTodo(i)}/> {todo.title}</li>
        })}</ul>
        <input type="text" value={this.state.currentText} onChange={this.handleChange}/> <input type="button" value="追加" onClick={this.addTodo} />
      </div>
    );
  }
```

## 参考

(コードの元ネタ)
React で作る TODO アプリ前編 – React 入門 - to-R Media
https://www.to-r.net/media/react-tutorial13/

React.js で Form を扱う - Qiita
https://qiita.com/koba04/items/40cc217ab925ef651113

any 型で諦めない React.EventCallback - Qiita
https://qiita.com/Takepepe/items/f1ba99a7ca7e66290f24

「TypeError: Cannot read property ‘state’ of undefined」に出会ったら Javascript の”this”について真剣に考えよう
https://applingo.tokyo/article/1422