4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

1 RazorによるMVCアプリケーション
2 CRUDページとRazorを見る
3 ReactでSPAを作ってみた ←今回

今回のテーマ

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

今回はこんなものを作ってみます。
おみくじアプリ構想.png
非常に簡単なWebアプリを作ってみたい!って人がいたら
いつもおみくじアプリを薦めてます。
JavaScriptによるおみくじ表示と
引いた人リストによるバックエンドとのやりとりで
Webアプリの練習になるかなーと思ってます。

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

Reactプロジェクトの作成

いつものように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
デバッグ.PNG

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

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

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

デバッグ2.PNG
[Counter]を押したら、ボタンカウンターが出ました。
5回押したら5カウントされました。

デバッグ3.PNG
[Fetch Data]を押したら、天気予報?が出ました。
5日間とも同じ時刻なのに、気温が偉く変わってますね-
-9℃の2日後に39℃になるって、どんな地域なんでしょうか?笑

JSファイルを編集しておみくじを作る

Reactのファイルを編集して、おみくじを作っていきます。

プロジェクトを作成した際のファイル構成は下記の通り
ファイル構造.PNG React構成.PNG
srcフォルダの中にjsファイルが入っています。
メイン画面の「App.js」と、componentフォルダにコンポーネントファイルが入ってますね。
(Omikuji.jsは後から作ったファイルです。)

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

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

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
名前をGeorgeにして、「Draw Omikuji」でおみくじを引くと
メッセージが「Georgeの運勢は 吉」と出ました。
ちなみに日本語だとエンコードの関係で文字化けたため、英語にしてあります。

引いた人リスト

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

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

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を実装していきたいと思います。

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
コントローラ作り方2.PNG

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

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
jsonが出力されているのが確認できました。
これを受け取り、表にするコンポーネントを作ってみましょう。

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

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

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

まとめ

長くなってしまいましたが、おみくじを一応作ってみた。 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を作ってみたいと思います。

参考文献

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に載せています。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?