ASP.net coreの探索　～3 ReactでSPAを作ってみた～

<a link="https://qiita.com/CheerfulGeneral/items/be34fcccfe397c27d064">1 RazorによるMVCアプリケーション</a>
<a link="https://qiita.com/CheerfulGeneral/items/80c7e45103dc7aa4ca7e">2 CRUDページとRazorを見る</a>
3 ReactでSPAを作ってみた　←今回

<h1>今回のテーマ</h1>
今回はReact.jsを用いてSPA(シングルページアプリケーション)を
作ってみます。
ASP.net coreでSPAを作成する場合、普通はBlazorを使うのだと思われますが
プロジェクト作成すると「React.js」があったので、
折角だから使ってみようと思いました。

今回はこんなものを作ってみます。
![おみくじアプリ構想.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/bcb097f7-2bc9-79cf-eb37-5f0dd5718c0f.png)
非常に簡単なWebアプリを作ってみたい！って人がいたら
いつもおみくじアプリを薦めてます。
JavaScriptによるおみくじ表示と
引いた人リストによるバックエンドとのやりとりで
Webアプリの練習になるかなーと思ってます。

ちなみに普段SPA作る際は、Vue.jsを使用しているので
今回このためにReactを学びました。
そのため、よくわかってない付け焼き刃なReactを書いているので、
色々間違ってたり、あり得ないソースコードになってると思います...
(プロジェクト作成にはAngularもあるのに、なぜかVueは無かった...)

<h1>Reactプロジェクトの作成</H1>
いつものようにVisual Studioを用いてプロジェクトを作成しましょう。
![プロジェクト作成1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/89933d18-8dbe-5179-aeb4-14e11f04cb1a.png)
![プロジェクト作成.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/75180d95-67ba-e068-e396-2221a5ad0e56.png)
![プロジェクト作成_React.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/a0d35d23-16c6-1acb-438d-7635a9d8a732.png)
ASP.net core webアプリケーションを作成し
React.jsを選択します。
React内で大掛かりなデータやりとりがないとされるため
今回はReact.jsで行います。(Reduxは使いません。)

![debugボタン.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/d5c439fb-827b-9265-1a67-6ae320628791.png)
![デバッグ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/a5f54cfe-9367-3a53-e4b4-fd5ceebe781d.png)

プロジェクトが出来たところで、サンプルページを開いてみましょう。
初回実行少々時間がかかります。
(npmが動いでたようなので、環境をインストールしてるのかな？)

・ASP.net coreとC#
・React
・Bootstrap
を使用してます！ってメッセージが出ます。

画面上のナビゲーションバー(ヘッダー)に
いくつか項目がありますね。

![デバッグ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/03c1a77f-fd02-7f43-2de6-c44585a89b73.png)
[Counter]を押したら、ボタンカウンターが出ました。
5回押したら5カウントされました。

![デバッグ3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/185c8758-83c1-19af-439e-3661fdd7223c.png)
[Fetch Data]を押したら、天気予報？が出ました。
5日間とも同じ時刻なのに、気温が偉く変わってますね－
-9℃の2日後に39℃になるって、どんな地域なんでしょうか？笑

<h1>JSファイルを編集しておみくじを作る</h1>
Reactのファイルを編集して、おみくじを作っていきます。

プロジェクトを作成した際のファイル構成は下記の通り
![ファイル構造.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/898dbcda-0e18-f18a-5e67-6d110399ea01.png)　![React構成.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/83dbbf36-286a-7e64-1507-9378171a8602.png)
srcフォルダの中にjsファイルが入っています。
メイン画面の「App.js」と、componentフォルダにコンポーネントファイルが入ってますね。
(Omikuji.jsは後から作ったファイルです。)

メイン画面の「App.js」の中身を見てみましょう。

```javascript:App.js
import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';

import './custom.css'

export default class App extends Component {
  static displayName = App.name;

  render () {
    return (
      <Layout>
        <Route exact path='/' component={Home} />
        <Route path='/counter' component={Counter} />
        <Route path='/fetch-data' component={FetchData} />
      </Layout>
    );
  }
}

```
コンポーネントをインポートして、ルーティングで表示をわけていますね。
今回はルートディレクトリの{Home}コンポーネントをおみくじにしてみましょう。
![おみくじ1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/41017433-f2ef-7906-e4de-382d4fb33f66.png)

```javascript:Home.js
import React, { Component } from 'react';
import './NavMenu.css';

let fortune = ["Daikichi", "Chukichi", "Kichi", "Shokichi", "Suekichi", "Kyo", "Daikyo"];      //運勢リスト

export class Home extends Component {
    constructor(props) {
        super(props);       //propsを継承
        this.state = {      // this.state
            message: "Please draw Omikuji !!",   //初期メッセージ
            displayName: "",     //引いた人の名前
            index:0,    //運勢インデックス
    }
        this.drawOmikuji = this.drawOmikuji.bind(this);  //メソッドをクラスにバインド
        this.changeName = this.changeName.bind(this);   //メソッドをクラスにバインド
    }

    drawOmikuji(e) {    //おみくじを引くメソッド
        this.setState(state => ({       //stateの変更
            index: Math.floor(Math.random() * fortune.length),  //ランダムで運勢インデックスを作成
            message: this.state.displayName + "'s fortune is " + fortune[this.state.index]//メッセージを変更
        }));
}

    changeName(e) { //名前入力したら名前を取得
        this.setState({ displayName: e.target.value }); //名前入力されたらstateのdisplayNameに名前を入れる
    }

  render () {
    return (
      <div>
            <h1>Hello, Omikuji!</h1>
                <div className="omikujiPadding row">
                    <input type="text" className="form-control col-6" placeholder="Your name" onChange={this.changeName} />
                    <input type="submit" className="btn btn-primary col-3" value="Draw Omikuji" onClick={this.drawOmikuji} />
                </div>
            <h2 className="messageStyle">{this.state.message}</h2>
        </div>
    );
  }
}
```
画面のようにおみくじを実装しました。
運勢をリストにして、おみくじを引くときにランダムで
運勢リストから運勢を取り出します。

Reactの仕様？なのか、配列はclassの外で作らないと使えないですね。
はじめはclassの中に「static fortune=[]」と入れましたが、
Uncaught TypeError: Cannot read property 'length' of undefined
が表示されて使えませんでした。

![おみくじ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/651b3464-4dbe-f40b-fe2a-0e0494e2b18c.png)
名前をGeorgeにして、「Draw Omikuji」でおみくじを引くと
メッセージが「Georgeの運勢は　吉」と出ました。
ちなみに日本語だとエンコードの関係で文字化けたため、英語にしてあります。

<h1>引いた人リスト</h1>
実際のおみくじは、大凶が出るとおみくじを枝に結んで返しますね。
そのように、引いたら返すつもりで、引いた報告をする機能をつけてみます。
上イメージ図のように、引いた人の記録がテーブルに並ぶ感じにします。

ここで、データを記録したり表示することが発生するため、バックエンドとの通信が必要になります。
ReactとASP.NET coreはどのようにしているのか？を探してみたところ
FetchData.js(天気予報コンポーネント)にヒントがありました。

```javascript:FetchData.js
  async populateWeatherData() {
    const response = await fetch('weatherforecast');
    const data = await response.json();
    this.setState({ forecasts: data, loading: false });
```
ルート「weatherforecast」にfetchして
そこからjsonで天気データを受け取っているようです。
ファイルを色々調べてみると、「Controller」の中に「WeatherForecastController.cs」という
天気データをReactに送るコントローラが見つかりました。
つまり、リクエストに対してjsonを返す Web APIになっているのだと思われます。

ということで、引いた人一覧クラスとそのコントローラーを作って
おみくじ返納Web APIを実装していきたいと思います。

```csharp:DrawedList.cs
using System;


namespace ReactOmikuji
{
    public class DrawedList
    {   public int ID { get; set; } //スキャフォールディング作成時の慣例

        public string Name { get; set; }
        
        public string Fortune { get; set; }
        
        public DateTime Date { get; set; }
    }
}

```
![APIコントローラ作り方.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/1071b07a-05e8-025e-b3ec-757f305e7962.png)
![コントローラ作り方2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/a6ed8569-00ed-575c-8591-c9010b2d6916.png)


引いた人一覧クラス「DrawedList」を作成します。
次に、「Controller」フォルダを右クリックし
「追加」→「コントローラ」→「API \ EFを使用したアクションがあるAPIコントローラ」
でコントローラを作成します。
Razorのスキャフォールディングと同じ要領で、コントローラを作成します。
コントローラにアクセスしたらAPIが動くように、Routeの中を書き換えます。

```csharp:DrawedListController.cs
    [Route("drawedlist")]
    [ApiController]
    public class DrawedListsController : ControllerBase{
        private static readonly string[] NameSample = new[] //リストに載せる名前サンプル
        {
            "A", "B", "C"
        };

        private static readonly string[] FortuneSample = new[]  //リストに載せる運勢サンプル
{
            "Daikichi", "Kichi", "Daikyo"
        };

        private readonly ReactOmikujiContext _context;  //コントローラー使用するためのオブジェクト

        public DrawedListsController(ReactOmikujiContext context)   //コントローラ使用するためオブジェクトを処理
        {
            _context = context;
        }

        // GET: DrawedList
        [HttpGet]
        public IEnumerable<DrawedList> Get()    //DrawedListにアクセスした際の処理
        {
            return Enumerable.Range(0,3).Select(index => new DrawedList     //名前・運勢・引いた日付を
            {
                Date = DateTime.Now.AddDays(index),
                Fortune = FortuneSample[index],
                Name = NameSample[index]
            })
            .ToArray(); //配列で返す
        }
}

```
「drawedlist」にアクセスすれば、Web APIが使えるようになりました。
今回は、「drawedlist」に行くと引いた人リストがjsonで返るようになってます。
SQL Serverは使用せず、いつも同じデータが出るようになってます。
(途中からSQL Serverがおかしくなり、使えなくなってしまった...)
![json.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/526d4b83-e620-39ac-59ab-4ec9cf60d95a.png)
jsonが出力されているのが確認できました。
これを受け取り、表にするコンポーネントを作ってみましょう。

```javascript:DrawedList.js
import React, { Component } from 'react';

export class DrawedList extends Component {
    constructor(props) {
        super(props);
        this.state = { drawedlist: [], loading: true };
    }

    componentDidMount() {   //コンポーネント起動時にする処理
        this.populateDrawedList();  //jsonデータをstateに入れる処理
    }

    static renderListTable(drawedlist) {    //引いた人リストをテーブルに表示するhtml
        return (            
            <table className='table table-striped' aria-labelledby="tabelLabel">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Fortune</th>
                        <th>Date</th>
                    </tr>
                </thead>
                <tbody>
                    {drawedlist.map(drawedlist =>
                        <tr key={drawedlist.id}>
                            <td>{drawedlist.name}</td>
                            <td>{drawedlist.fortune}</td>
                            <td>{drawedlist.date}</td>
                        </tr>
                    )}
                </tbody>
                </table>
        );
    }

    render() {  //レンダー
        let contents = this.state.loading   //表示させる中身オブジェクト
            ? <p><em>Loading...</em></p>    //読み込みに時間がかかると Loadingと表示
            : DrawedList.renderListTable(this.state.drawedlist);    //読み込めたら表を表示

        return (
            <div>
                <p>This component demonstrates fetching data from the server.</p>
                {contents}
            </div>
        );
    }
    
    async populateDrawedList() {    //バックエンドと通信して、引いた人リストを取得
        const response = await fetch('drawedlist');    //drawedlistにアクセス
        const data = await response.json();     //jsonデータを取得
        this.setState({ drawedlist: data, loading: false });    //jsonデータをstateに入れる
    }
    
}

```
結構長くなってしまいましたが、テンプレートにある天気予報取得の
「FetchData.js」をそのまんま真似しただけです。

起動時にFetchでDBに接続し、データを取得
↓
データをstateに入れる
↓
stateをmapで全部表示される

とやって表示させているだけですね。

![drawedlistつき.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595683/0e2a2d67-527a-d58f-6fc6-6d94a73588b0.png)

最後にHome.jsに```<DrawedList />```を入れたら
引いた人リストが表示されました。

ただし、現在は表示だけで
おみくじを引いたら新しく追加される機能はまだついていません。
SQL ServerやMySQLとの通信が出来たら、また挑戦してみようと思います。

<h1>まとめ</h1>
長くなってしまいましたが、おみくじを一応作ってみた。
React.jsとASP.NET Coreでアプリを作ってみて感じたことは
・おみくじであればFirebaseでよい(ASP.NET Coreの必要がない)
・ASP.NET Coreのアドバンテージは、独自のWeb APIを作る場合
・Reactは(Vueと比べて)Javascript感がある
といったところでしょうか。

初めて触ったReactですが、Javascriptを触っている感じがして
個人的にはVueよりも楽しめた気がします。
それから、ASP.NET CoreでSQL Serverと通信する場合は
Entity Frameworkというのを使うみたいです。
このEntity Frameworkを使いこなせば、MySQLが使えるのかなーと思います。
いずれチャレンジしてみたいです。

次回はBlazorを使ってSPAを作ってみたいと思います。

<h1>参考文献</h1>
https://docs.microsoft.com/ja-jp/aspnet/core/client-side/spa/react?view=aspnetcore-5.0&tabs=visual-studio
Microsoftの公式ドキュメントです。

https://ja.reactjs.org/
https://www.shuwasystem.co.jp/book/9784798056920.html
React.jsの公式ドキュメントと
書籍「React.js & Next.js超入門」です。
この2つを見ながらReactを実装しました。

https://github.com/Sho-Zhao/ReactOmikuji.git
Githubに載せています。