Reactチュートリアルやってみたその2

# Reactチュートリアルやってみたその2

前回は公式チュートリアルでしたが、今回は[React Tutorial: An Overview and Walkthrough](https://www.taniarascia.com/getting-started-with-react/)というサイトのチュートリアルで各ファイルに機能を分け、中枢に当たるファイルでそれらをインポートとするようなReactアプリの作り方とnpmによるGitHub Pageに作ったReactアプリをデプロイするやり方を見ていきます。
実は、公式チュートリアルは最後に追加課題があったのですが私は知らずに飛ばしてしまったのでこちらを今回はやっています。
ここまで、やるとおそらく簡単なReactアプリを作成し、それをデプロイするまで(ただし、今回までの範囲ではサーバーを用意していないのでローカル環境のみ)のスキルが身につくはずです。

## 今回やること

冒頭でも書いた通りですが、前回でReactの仕組みやアプリの作り方の基本的な考え方や作り方を公式チュートリアルで学んだので、今回は少し応用として

- コンポーネントに1つのファイルから各ファイルに分割する
- npmでビルドする
- GitHub Pagesにデプロイする

という以下の3点に絞って進めていきます。

## コンポーネントの分割

今回は以下のようなディレクトリ構造で作っていきます

ルート
<img width="605" alt="2020-07-23_12h45_15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/10cabc7f-0524-5f72-1691-55f2be84e6d6.png">

srcフォルダの内部
<img width="607" alt="2020-07-23_12h45_25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/fe26900d-5628-c530-2ea3-5d23d6ab6537.png">


具体的には`index.js`に`App.js`をインポートし、さらに`App.js`に各コンポーネントをインポートするような形でアプリを作っていきます。
フレームワークを使ったことがある人ならわかると思いますが、前回のように1つのファイルにすべての処理を記述するようなやり方は可読性やメンテナンス性の悪さから推奨されないので、例えばDjangoであるなら1つのアプリケーションを作るにしても機能ごとにフォルダを作り、さらにその中でもコンポーネントに分割したりととかく最小単位を突き詰めていくような作り方をします。
最小単位を突き詰めれば、必然的に1つのファイルに書かれるコードは短くなり、結果どのファイルにどの処理が書いてあるかということをわかりやすくするということにつながるわけですね。

閑話休題、では今回の各ファイルを見ていきましょう。

<details><summary>index.js</summary><div>

```jsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App'
import './index.css'


// 最後に、React DOM render() メソッドを使って、作成した App クラスを HTML のルート div にレンダリングします。
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

```

</div></details>

<details><summary>App.js</summary><div>


```jsx

import React, { Component } from 'react'
import Table from './Table'
import Form from './Form'

class App extends Component {
  state = {
    characters: [

    ],
  }

  removeCharacter = (index) => {
    const { characters } = this.state

    this.setState({
      characters: characters.filter((character, i ) => {
        return i !== index
      }),
    })
  }

  handleSubmit = (character) => {
    this.setState({characters: [...this.state.characters, character]})
  }

  render() {
    const { characters } = this.state
    return (
      <div className="container">
        <Table characterData={characters} removeCharacter={this.removeCharacter} />
        <Form handleSubmit={this.handleSubmit} />
      </div>
    )
  }
}

export default App

```

</div></details>

<details><summary>Form.js</summary><div>

```jsx

import React, {Component} from 'react'

class Form extends Component {
    initialState = {
        name:'',
        job:'',
    }

    state = this.initialState

    handleChange = (event) => {
        const {name, value} = event.target

        this.setState({
            [name]: value,
        })
    }

    submitForm = () => {
        this.props.handleSubmit(this.state)
        this.setState(this.initialState)
    }

    render() {
        const { name, job } = this.state;

        return (
            <form>
                <label htmlFor="name">Name</label>
                <input
                    type="text"
                    name="name"
                    id="name"
                    value={name}
                    onChange={this.handleChange} />
                    <label htmlFor="job">Job</label>
                <input
                    type="text"
                    name="job"
                    id="job"
                    value={job}
                    onChange={this.handleChange} />
                <input type="button" value="Submit" onClick={this.submitForm} />
            </form>
        );
    }
}

export default Form;

```

</div></details>

<details><summary>Table.js</summary><div>

```jsx

import React from 'react'

// シンプルコンポーネント

const TableHeader = () => {
    return (
        <thead>
            <tr>
                <th>Name</th>
                <th>Job</th>
                <th>Remove</th>
            </tr>
        </thead>
    )
}

const TableBody = (props) => {
    const rows = props.characterData.map((row, index) => {
        return (
            <tr key={index}>
                <td>{row.name}</td>
                <td>{row.job}</td>
                <td>
                    <button onClick={() => props.removeCharacter(index)}>Delete</button>
                </td>
            </tr>
        )
    })
    return <tbody>{rows}</tbody>
}

const Table = (props) => {
    const {characterData, removeCharacter} = props

    return (
        <table>
            <TableHeader />
            <TableBody characterData={characterData} removeCharacter={removeCharacter} />
        </table>
    )
}

// クラスコンポーネント
// class Table extends Component {
//     render() {
//         return (
//             <table>
//                 <thead>
//                     <tr>
//                         <th>Name</th>
//                         <th>Job</th>
//                     </tr>
//                 </thead>
//                 <tbody>
//                     <tr>
//                         <td>Charlie</td>
//                         <td>Janitor</td>
//                     </tr>
//                     <tr>
//                         <td>Mac</td>
//                         <td>Bouncer</td>
//                     </tr>
//                     <tr>
//                         <td>Dee</td>
//                         <td>Aspiring actress</td>
//                     </tr>
//                     <tr>
//                         <td>Dennis</td>
//                         <td>Bartender</td>
//                     </tr>
//                 </tbody>
//             </table>
//         )
//     }
// }


export default Table

```

</div></details>

<details><summary>Api.js</summary><div>


</div></details>

`Api.js`については最後に説明していきます。
なお、CSSについては進行上コピペで対応してくださいとのことです。
各ファイルの各コードについてはリポジトリの方に私がわかりにくかったところについて少し注釈を入れてあります。

各ファイルの役割を見ていきましょう。
今回は単純にフォームに入力されたデータをテーブルで表示して、削除もできるようにするということができるように作業を進めていきます。
`index.js`は最終的な`render()`を行うためのファイルです。
今回は`App.js`にアプリの機能を集約させるのでそちらのAppクラスを`render()`という役割を持っています。
その`App.js`は機能の中枢であるので各コンポーネントからデータを受け取ったり、必要に応じて各コンポーネントへデータを渡したりします。
`Table.js`は`App.js`からデータを受け取り、受け取ったデータをpropsに格納し、それを用いてテーブルを描画する役割を持っています。
今回、`Table.js`はstateを持たないので関数コンポーネントのみで構成されています。
`Form.js`は新たにデータを追加するための役割を持ちます、より具体的にはフォーム内のフィールドが変更されるたびにローカルのフォームの状態を更新し、送信時にはそのデータがすべてAppの状態に渡され、テーブルが更新されるような処理を担っています。
つまり、フォームのフィールドに応じてstate(プロパティ)が変更されますが、実際のプロパティとして確定するのは送信時のプロパティということになります。
よって、処理の流れとしては以下の通りとなります。

1. `Forms.js`(データの入力)
2. `App.js`(Form.jsからデータを受け取り、Table.jsパスする)
3. `Table.js`(テーブルを描画する)

正確には`App.js`には`Table.js`、`Forms.js`それぞれからForm・Tableコンポーネントをインポートしているので`App.js`ですべて行われていることになりますが、わかりやすくいうと上記のような流れになります。
今回のミソは`Table.js`、`Forms.js`のような子コンポーネントでも複数のコンポーネントがありますが、最終的にその中でも1つのコンポーネントへデータをパスし、そのコンポーネントを親コンポーネントにインポートしているというところになりますね。
例えば`Table.js`なんかは`TableHeader`及び`TableBody`コンポーネントでマークアップしている部分がありますが、それらはすべてreturnとして返り値にされ`Table`コンポーネントへ渡されています。
さらに`Table`コンポーネントでそれらは再度返り値にされることでインポート先の`App.js`へ渡され、そこで描画の処理が行われるという流れになります。
もちろん最終的な描画の処理は`index.js`で行われるわけですが、一見複雑かつ煩雑に見えるこの流れも丁寧に追っていけば理にかなっていることがわかりますね。

インポートに関しては`import App from './App'`や`import './index.css'`といったように書いていきます。
同時にインポートさせたいコンポーネントは`export default ~`とコードの末尾に書いて置かなければいけません。
例えば`Table.js`のTableコンポーネントをインポートしたい場合は、`Table.js`側で`export default Table`と定義をしておかないといけないということですね。
`export default ~`は1ファイルで1コンポーネントのみにしか使えません。

最後に`Api.js`ですがこれはReactにおいてAPIを使ったアプリケーションを作るための簡単なチュートリアルと思ってください。
今回はWikipediaのAPIを利用して、そのAPIデータをDOMにレンダリングしています。
`Api.js`と`App.js`の切り替えは`index.js`においてインポートをどちらにするかで決まります。

## ビルドとデプロイ

JavaScriptやCSSは実際のページなどをDeveloper Toolなどでみると

```js

/*! For license information pease see 2.f0b459d6.chunk.js.LICENSE.txt */
(this.webpackJsonptutorial2=this.webpackJsonptutorial2||[]).push([[2],[function(e,t,n){"use strict";e.exports=n(10)},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";function r(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function l(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}n.d(t,"a",(function(){return l}))},function(e,t,n){"use strict";function r(e){return(r=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.

....

```

といったような状態で表示されることがよくあります。
これは本番環境においてはソースコード(これまで書いてきたコード)を生のままおかないということと、ReactのようなTypeScriptを使う場合はこのような形式に変換しないとブラウザ上では動作しないからです。
ソースコードをこのような形式に変換することをビルドというそうです。
React……というより、TypeScriptを使う場合はnpmの利用が不可欠になってくるので必然的にビルドはnpmが行うということになります。
といってもそんなに難しいことではなく`package.json`があるディレクトリで

```

npm run build

```

を実行すればいいだけです。
ただ、今回はGitHub Pagesへのデプロイも行いたいので実行は後回しにします。

## GitHub Pagesへのデプロイ

いよいよ最後の工程になります。
package.jsonを以下のように編集します

<details><summary>package.json</summary><div>



```json

{
  "name": "tutorial2",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://GitHubのユーザー名.github.io/リポジトリ名",
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "gh-pages": "^3.1.0"
  }
}


```

</div></details>

編集が終わったらnpmに`gh-pages`をインポートします。
このライブラリを使うことでいい感じにReactがビルドされてGitHub Pagesへとデプロイされます。

```

npm install --save-dev gh-pages

```

あとは先程のコマンドを用いてビルドしてデプロイするだけです。

```

npm run build

npm run deploy

```

注意事項としては今回はサーバーを用意していないので実際にデプロイした結果を画面で確認したい場合は`yarn start`などでローカルサーバーを起動しないといけないことですね。


## 感想

Reactチュートリアルを2つ完走した感想ですが、**やっぱりJavascriptは難しい**

……というのは冗談半分としてJavascriptのフレームワークという以上にできることが多いなという印象でした。
無知ゆえに、どうしてもJavaScriptには動的な描画というところにだけ意識が行ってしまうのですが、特に今回のフォームの件はDBへの保存がないだけで、やっていることはデータの追加・削除とまるでサーバーサイドでやるようなことだったのでフロントでここまでやるのかという驚きがすごいです。
だってこれおそらくAjaxでDBへ渡してしまえばサーバーサイド側でやることは受け取ったJSONデータを変換してDBへ保存したり、検索をかけて合致したものを削除するような処理を書けばいいってことになるわけで……うーん、すごい……