Reactとfirebaseでアプリケーションを作る①

# ReactとFirebaseでアプリケーションを作る①
①では、環境作成と下地作りまでです。

# やりたいこと

* **Reactのチュートリアルで基礎を学ぶ**
* **チュートリアルを参考に、一人神経衰弱ゲームを作る**
* Firebase Authentication を使ってログイン認証する
* Firebase を使ってデータを管理する

# チュートリアルを修正して、一人神経衰弱ゲームを作る

チュートリアルは、「[チュートリアル：React の導入](https://ja.reactjs.org/tutorial/tutorial.html)」を利用する。  
今回、理解するポイントはチュートリアルの通り。

> チュートリアルの準備：以下のチュートリアルを進めるにあたっての開始地点です。
> 概要：コンポーネントや props、state といった基礎概念について学びます。
> ゲームを完成させる：React での開発における非常によくある技法について学びます。

## チュートリアルの準備

### 環境を作る

以前作成したDockerのReact環境「[DockerにReact環境を構築する](https://qiita.com/abirutakayuki/items/6d009eb248bdd74fabb0)」をクローンする。

```zsh
git clone https://github.com/abiitaka/docker-react.git react-firebase
```

`docker-compose.yml` と `init.sh` のプロジェクト名を変更する。  

* 変更前：`reactstudy01`　→　変更後：`react-firebase`

以降は、チュートリアルの**不要なファイルを削除する**ところから進めていく。


## Reactのチュートリアルで基礎を学ぶ

### 三目並べを作る

チュートリアルに従って進めていくと三目並べは完成する。  

作ったソースは以下にあります。
[三目並べ作成](https://github.com/abiitaka/react-firebase/releases/tag/tutorial_game)

## チュートリアルのポイント
Reactのチュートリアルでは、リフトアップとイミュータビリティについて補足説明をしている。
以下の２点がポイントです。

### リフトアップ

>
> **複数の子要素からデータを集めたい、または 2 つの子コンポーネントに互いにやりとりさせたいと思った場合は、代わりに親コンポーネント内で共有の state を宣言する必要があります。親コンポーネントは props を使うことで子に情報を返すことができます。こうすることで、子コンポーネントが兄弟同士、あるいは親との間で常に同期されるようになります。**
>
> このように state を親コンポーネントにリフトアップ (lift up) することは React コンポーネントのリファクタリングでよくあることですので、この機会に挑戦してみましょう。

また、「[state のリフトアップ](https://ja.reactjs.org/docs/lifting-state-up.html)」にも、まとめられています。

### イミュータビリティ

> 
> **ミュータブル (mutable) なオブジェクトは中身が直接書き換えられるため、変更があったかどうかの検出が困難です。**ミュータブルなオブジェクト変更の検出のためには、以前のコピーと比較してオブジェクトツリーの全体を走査する必要があります。
> 
> **イミュータブルなオブジェクトでの変更の検出はとても簡単です。**参照しているイミュータブルなオブジェクトが前と別のものであれば、変更があったということです。

mutable、immutableの変更検出は、scpとして「[shouldComponentUpdate の実際の動作](https://ja.reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action)」にまとめられています。

## チュートリアルを参考にひとり神経衰弱ゲームを作る

### ルール
* 4x3のマス目を作る
* マスに1〜6までの数字のペアを配置する
* 1つ目をクリックすると数字が表示される
* ２つ目をクリックすると数字が表示される
* 表示した2つの数字がペアかどうか判断する
* ペアであれば表示したまま、ペアでなければ表示しない

### ソース

```react

class Board extends React.Component {
    constructor(props) {
        super(props);

        const squareCount = 12;
        const initGameBoard = this.generateBoard(squareCount);

        this.state = {
            gameBoard: initGameBoard,
            selectBoard: Array(squareCount).fill(null),
            history: [{ selectBoard: Array(squareCount).fill(null) }],
            previous: null,
            isPair: null,
        }
    }

    handleClick(current) {
        // 盤表示
        const selectBoard = this.state.selectBoard.slice();
        selectBoard[current] = this.state.gameBoard[current];

        // ペア判定
        let isPair = null, previous = null;
        if (this.state.previous == null) {
            previous = current;
        } else {
            isPair = this.isPair(selectBoard[this.state.previous], selectBoard[current]);
        }

        // 履歴
        const history = this.state.history.slice();

        this.setState({
            selectBoard: selectBoard,
            history: history.concat([{ selectBoard: selectBoard }]),
            previous: previous,
            isPair: isPair,
        });
    }

    // 盤のペア組み合わせ生成
    generateBoard(squareCount) {
        let result = [];
        // ペアの数字を生成
        for (let i = 1; i <= squareCount / 2; i++) {
            result.push(i, i);
        }
        // シャッフル(Fisher-Yates shufflアルゴリズム)
        for (let i = squareCount - 1; i > 0; i--) {
            let random = Math.floor(Math.random() * (i + 1));
            let tmp = result[i];
            result[i] = result[random];
            result[random] = tmp;
        }
        return result;
    }

    // ペアをチェックする
    isPair(previous, current) {
        if (previous == null && current == null) {
            return false;
        } else if (previous === current) {
            return true;
        } else {
            return false;
        }
    }

    renderSquare(i) {
        return (
            <Square
                value={this.state.selectBoard[i]}
                onClick={() => this.handleClick(i)}
            />
        );
    }

    render() {
        // ペアにならない場合は盤を戻す 
        if (this.state.isPair === false) {
            setTimeout(() => {
                const history = this.state.history.slice(0, this.state.history.length - 2);
                const selectBoard = history[history.length - 1].selectBoard.slice();
                this.setState({
                    selectBoard: selectBoard,
                    history: history,
                    isPair: null,
                });
            }, 300);
        }
        return (
            <div>
                <p>{this.state.selectBoard.indexOf(null) === -1 ? 'ゲーム終了' : ''}</p>
                <div className="board-row">
                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}
                    {this.renderSquare(3)}
                </div>
                <div className="board-row">
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}
                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                </div>
                <div className="board-row">
                    {this.renderSquare(8)}
                    {this.renderSquare(9)}
                    {this.renderSquare(10)}
                    {this.renderSquare(11)}
                </div>
            </div>
        );
    }
}
```

作ったソースは以下にあります。
[一人神経衰弱ゲーム作成](https://github.com/abiitaka/react-firebase/releases/tag/%E4%B8%80%E4%BA%BA%E7%A5%9E%E7%B5%8C%E8%A1%B0%E5%BC%B1%E3%82%B2%E3%83%BC%E3%83%A0%E4%BD%9C%E6%88%90)

Firebase Authentication と Firebase は、「[ReactとFirebaseでアプリケーションを作る②（準備中）]()」でまとめます。 
 
# Firebase作成

## Firebase プロジェクトを作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628225/bfaeb2f6-a5ec-a370-5cfb-199796473d8a.png)