【React × TypeScript】ES6での基本的なコンポーネント作成

※reactとtypescriptともに初学者のためもっといい記法などあるかもしれません、何か知見があればご共有いただけると嬉しいです:bow:

# 執筆の経緯

[Reactビギナーズガイド](https://www.amazon.co.jp/React%E3%83%93%E3%82%AE%E3%83%8A%E3%83%BC%E3%82%BA%E3%82%AC%E3%82%A4%E3%83%89-%E2%80%95%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-Stoyan-Stefanov/dp/4873117887)で勉強していたのですが、Vueなどは既に学習済みでして、少しステップアップして単に参考書を模写するのではなく、最新バージョンにしてtypescriptも導入するなんてやっていたらつまずくポイントも多かったので基本的なコンポーネント作成に絞って解説します。

ちなみにバージョンは

```json
    "react": "^17.0.2"
    "typescript": "^4.3.5"
```

# コンポーネントの全体像

tsxでの記載になります。
表をレンダリングして、theadをクリックするとレコードを並び替えるコンポーネントです。

```tsx
import React from 'react'

const initialData: Array<[string, string, string, string, string]> = [
    [
        "The Lord of the Rings",
        "J. R. R. Tolkien",
        "English",
        "1954-1955",
        "150 million"
    ],
    [
        "Le Petit Prince (The Little Prince)",
        "Antoine de Saint-Exuperty",
        "French",
        "1943",
        "140 million"
    ],
    [
        "And Then There Were None",
        "Agatha Christie",
        "English",
        "1939",
        "107 million"
    ],
    [
        "Dream of the Red Chamber",
        "Cao Xueqin",
        "Chinese",
        "1754-1791",
        "100 million"
    ],
    [
        "The Hobbit",
        "J. R. R. Tolkien",
        "English",
        "1937",
        "100 million"
    ],
    [
        "She: AHistory of Adventure",
        "H. Rider Haggard",
        "English",
        "1887",
        "100 million"
    ]
]

type propTypes = {
    headers: [string, string, string, string, string]
}

type stateTypes = {
    data: Array<[string, string, string, string, string]>
}

export default class Excel<P = propTypes, S = stateTypes> extends React.Component<propTypes, stateTypes> {
    constructor(props: Readonly<propTypes>) {
        super(props)

        this.state = {
            data: initialData
        }

        this._sort = this._sort.bind(this)
    }

    _sort(e: any){
        let column = e.target.cellIndex
        let data = this.state.data.slice()

        data = data.sort(function(a, b) {
            return a[column] > b[column] ? 1 : -1
        })

        this.setState({
            data: data
        })
    }

    render() {
        return (
            <table>
                <thead onClick={this._sort}>
                    <tr>
                        {
                            this.props.headers.map((header: string, idx: number) => {
                                return (
                                    <th key={idx}>{header}</th>
                                )
                            })
                        }
                    </tr>
                </thead>
                <tbody>
                    {
                        this.state.data.map((row: Array<string>, idx: number) => {
                            return (
                                <tr key={idx}>
                                    {
                                        row.map((item: string, idx: number) => {
                                            return (
                                                <td key={idx}>{item}</td>
                                            )
                                        })
                                    }
                                </tr>
                            )
                        })
                    }
                </tbody>
            </table>
        )
    }
}
```

呼び出し元はこう、
propsに値を入れ込むためにはどうするかわかりませんでしたが、呼び出し元で定義するんですね。

```tsx
import React from 'react';
import '../css/App.css';

import Excel from "./component/Excel";

const headers: [string, string, string, string, string] = [
    "タイトル", "著者", "言語", "出版年", "売上部数"
];

export default class App extends React.Component {
  render() {
    return (
        <div className="App">
            <Excel headers={headers} />
        </div>
    );
  }
}

```

# React.Componentの継承

React.createClass()
というメソッドを使ってコンポーネント作成すると参考書には記載されていましたが、
最新のバージョンではこのメソッドはなくなったようです。

代わりに、class定義を行いReact.Componentを継承します。
上ではTypeScriptに怒られるので細かく定義していましたが、基本形は以下になります。

```tsx
export default class Excel extends React.Component {
  render(){
    return (
    
    )
  }
}
```

# stateの初期化

どこでやるんかぁと気になりましたが、constructor内でのみthis.stateの定義が許されているらしいです。

```tsx
    constructor(props: Readonly<propTypes>) {
        super(props)

        this.state = {
            data: initialData
        }

        this._sort = this._sort.bind(this)
    }
```

# thisのバインド

クラス内でthisを利用するにはconstructor()でbindが必要。
これをしないとrender以外でthisはundefinedになります。
bindした変数はちゃんと同変数名に代入しないといけません。

```tsx
    constructor(props: Readonly<propTypes>) {
        super(props)

        this.state = {
            data: initialData
        }

        this._sort = this._sort.bind(this)
    }
```

# クラスへの型指定

typescriptでは型定義をしてあげないとクラス内で、
変数利用するときなどにエラーを吐かれてしまいます。
クラス内に直書きしてしまうと、汚くなるので、type宣言して当て込んであげるのがいいかと思います。

```tsx
type propTypes = {
    headers: [string, string, string, string, string]
}

type stateTypes = {
    data: Array<[string, string, string, string, string]>
}

export default class Excel<P = propTypes, S = stateTypes> extends React.Component<propTypes, stateTypes> {}
```

# 感想

いやぁ難しいですね:frowning2:
Vueができるとはいえ、Reactとなるとカッチリしててちょこちょこエラーにはまります。。。
コンポーネントの取り回しとかなんとなく雰囲気は似てるのでやってけそうですが疲れます。

ここまでお付き合いいただきありがとうございました！