konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓 ログインページをデザイン

Native React の環境では、一つのページ(URL)の中でNutanixクラスタ接続検索結果表示を行う、SPA(シングルページアプリケーション)でのカタチで作ってました

今回はなんとなく、それぞれ別にページを作っていきます


【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓
・ログインページをデザイン ←イマココ
フォーム制御
とりあえずFetch
FetchからRouterでページ遷移
SSR で Fetch する
Layoutつくる

今回の成果物

今回の最終形態はコチラ(Github)で公開しています

ページ設計

まともにWebサイトとか作ったことないので、どうやって設計するもんなんかはわかりませんが。。。
ページ設計としてはこんな感じで作ってみようかと


ログインページのベースデザイン

それっぽいログインページ風なものを作ってみます
(一旦色は無視)

画像の表示

表示する画像は、"public"配下に置いて

import Image from 'next/image'

<Image src="/uuid-xplorer_logo.png" alt="uuid xplorer logo" width={494} height={80} />

width, heightを両方指定しなければいけないのが、ちょっとメンドイ

フォーム

フォームには、daisyUI の Text Input をベースに使います
Text Input — Tailwind CSS Components

<input type="text" placeholder="Type here" className="input input-info" />

これだけでカタチになってくれるのは楽チン

ここから先は、tailwindcssも駆使しながらイメージしたデザインを作っていきます

<pages/index.tsx

import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'

//fontawesome
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowCircleRight } from '@fortawesome/free-solid-svg-icons'

const Index: NextPage = () => {
  return (
    <div className="">
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main data-theme="white" className="flex text-center items-center h-screen">
        <div className='w-1/2'>
          <div className='m-10'><Image src="/uuid-xplorer_logo.png" alt="uuid xplorer logo" width={494} height={80} /></div>
        </div>

        <div className="w-1/2 bg-primary h-screen flex justify-center items-center flex flex-col">
          <div className="form-control mt-20">
            <form >
              <div className='flex flex-col'>
                <input type="text" placeholder="Cluster IP" className="input input-info input-bordered m-1 w-64 text-lg" />
                <input type="text" placeholder="username" className="input input-info input-bordered m-1 w-64 text-lg" />
                <div className='m-1 relative'>
                  <input type="password" placeholder="Password" className="input input-info input-bordered w-64 text-lg" />
                  <button type="submit" className="absolute inset-y-2 right-2 opacity-20 hover:opacity-100"><FontAwesomeIcon icon={faArrowCircleRight} size="2x" /></button>
                </div>
              </div>
            </form>
          </div>
        </div>

      </main>
      <footer className="text-center text-sm">Copyright (C) konchangakita. All Rights Reserved.</footer>
    </div>
  )
}

export default Index



daisy UIを使ったテーマカラーの設定

daisyUI のテーマカラーを変更して、
 ・Primary を Nutanixのブルーに近いに色
 ・info をフォームの枠の色もぽいブルーに
 ・base をボタンのマウスオーバー用に
に設定してみます
daisyUI Theme Generator — Tailwind CSS Components

<tailwind.config.js>

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [
    require('daisyui'),
  ],
  daisyui: {
    themes: [
      {
        'mytheme': {
        'primary': '#21509e',

        'base-100': '#ffffff',
        'base-200': '#f9fafb',
        'base-300': '#d1d5db',
        'base-content': '#1f2937',
        'info': '#2094f3',

        },
      },
    ],
  }
}

ぽい色になった気がします


ここまで

今回は一旦簡単なデザインまで
フォーム制御、Fetch(SSR)につづく

【Nutanix UUIDエクスプローラーを作ってみよう】Next.js環境に tailwindcss/ DaisyUI/ Font Awesomeを導入


tailwindcss.com
daisyui.com
fontawesome.com


今回はNext.js環境に、デザインを意識したコンポーネント群を導入、初心者なのでとりあえずフレームワークを満載にして、フロントエンド開発環境をパワーアップさせてみます

【Nutanix UUIDエクスプローラーを作ってみよう】シリーズ
REST API 〜 Flask表示
React で JavaScript 挑戦シリーズ

【Next.j 特訓】
Next.js で導入
・Next.js環境に tailwindcss/ DaisyUI/ Font Awesomeを導入←イマココ
ログインページをデザイン
フォームの実装
とりあえずFetchする
FetchからRouterでページ遷移
SSR で Fetch する
Layout を作る


今回の成果物

今回の最終形態はコチラ(Github)で公開しています
<フォルダ構造>


今回導入するモノ

tailwindcsscssが簡単になるcssフレームワーク的なな
daisyUI:デザインが完成されたtailwindcssの追加ライブラリ的な
Font Awesome:アイコンライブラリ的な

tailwindCSSの導入

ここを参照しつつ、環境にあわせてアレンジして設定していきます
Install Tailwind CSS with Create React App - Tailwind CSS

2つのコマンド実行
Next.jsのプロジェクト配下で(場所重要

# npm install -D tailwindcss postcss autoprefixer
added 54 packages, changed 2 packages, and audited 284 packages in 14s

74 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
# npx tailwindcss init -p
Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js

tailwind.config.js, postcss.config.js が作成されます


Reactデフォルトの pagesを使うので、tailwind.config.jsに下記を追記
tailwind.config.js

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}


標準のCSSファイルに追記
styles/global.css

@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";


これで、こんな感じで書くとスタイル(テキストを赤く)が適用されます
pages/index.html

<div className='text-red-500'>Welcome to UUID Xplorer</div>


ポイントNext.js環境では className とするのを気をつけましょう

daisyUIの導入

公式を参照してインストールします
Install daisyUI as a Tailwind CSS plugin — Tailwind CSS Components

Next.jsのプロジェクト配下で(場所重要

# npm i daisyui


tailwindcss をインストールしてできたコンフィグファイルに追記
tailwind.config.js

  plugins: [
    require('daisyui'),
  ],

pages/index.htmlに追記して、ボタンが表示されれば成功です
pages/index.html

<button className="btn">Button</button>



Font Awesomeの導入

React版のインストール方法を参照してインストールします
React | Font Awesome Docs

Next.jsのプロジェクト配下で実行(場所重要

npm install --save @fortawesome/fontawesome-svg-core
npm install --save @fortawesome/free-solid-svg-icons
npm install --save @fortawesome/react-fontawesome

Next.js用の適用方法を確認しつつ、
Use React with... | Font Awesome Docs

Next.jsですべてのページ共通で読み込まれる _app.tsx に追記
pages/_app.tsx

import '../styles/globals.css'
import type { AppProps } from 'next/app'

// fontawesome
import { config } from '@fortawesome/fontawesome-svg-core'
import '@fortawesome/fontawesome-svg-core/styles.css'
config.autoAddCss = false

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

export default MyApp


アイコンを使いたいページにアイコンごとに読み込みを行う
pages/index.tsx

import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

//fontawesome
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowCircleRight } from '@fortawesome/free-solid-svg-icons'

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <div className='text-red-500'>Welcome to UUID Xplorer</div>
        <FontAwesomeIcon icon={faArrowCircleRight} size="2x" />
      </main>
    </div>
  )
}

export default Home



docker-composeの整備

色々とインストールをしているうちに、package.json が成長していますね

{
  "name": "uuid-xplorer",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.1.1",
    "@fortawesome/free-solid-svg-icons": "^6.1.1",
    "@fortawesome/react-fontawesome": "^0.1.18",
    "daisyui": "^2.13.6",
    "next": "12.1.4",
    "react": "18.0.0",
    "react-dom": "18.0.0"
  },
  "devDependencies": {
    "@types/node": "17.0.23",
    "@types/react": "17.0.43",
    "@types/react-dom": "17.0.14",
    "autoprefixer": "^10.4.4",
    "eslint": "8.12.0",
    "eslint-config-next": "12.1.4",
    "postcss": "^8.4.12",
    "tailwindcss": "^3.0.23",
    "typescript": "4.6.3"
  }
}


ここまで準備ができたら、フロントエンドコンテナとして、docker-composeを整備します
バックエンドコンテナも併せて整備
<docker-compose>

これでGithubの状況と同じものがつくれます
blog-uuid-Xplorer/blog/0418 at main · konchangakita/blog-uuid-Xplorer · GitHub

準備完了

ここまで開発環境の準備は完了です
次からは、Next.jsを使って実際に Webインターフェースを構築していきます

【Nutanix UUIDエクスプローラーを作ってみよう】Next.js で 導入


React を CDN でぼちぼちJavaScriptしてきましたが、ここからフロントエンド部分を独立させ大きく開発環境を進化させます
フロントエンドのコンテナを別に作り、ReactベースのフロントエンドフレームワークNext.js を導入していきます

【Nutanix UUIDエクスプローラーを作ってみよう】シリーズ
REST API 〜 Flask表示
React で JavaScript 挑戦シリーズ

【Next.j 特訓】
・Next.js で導入←イマココ
Next.js環境に tailwindcss/ DaisyUI/ Font Awesomeを導入
ログインページをデザイン
フォームの実装
とりあえずFetchする
FetchからRouterでページ遷移
SSR で Fetch する
Layout を作る


今回の成果物

今回の最終形態はコチラ(Github)で公開しています
<フォルダ構造>


Next.js 用のコンテナの準備

とりあえず nodeベースでコンテナを作ります
これだけでOK
<dockerfile>

FROM node:latest
WORKDIR /usr/src/next-app/


Next.jsの導入

Next.js 日本語翻訳プロジェクトを参考にしながら、インストールしてみます

nodeバージョン確認
# node -v
v17.2.0
Next.js プロジェクト作成

プロジェクト名を指定しuuid-xplorerすることで、プロジェクト名のディレクトリが作成されて、必要なファイルたちが設置されます
今回はTypeScriptに挑戦したいので、--typescriptオプションをつけてインストールします

# npx create-next-app uuid-xplorer --typescript
Need to install the following packages:
  create-next-app
Ok to proceed? (y) 
自動的に作られたGitリポ削除

プロジェクト作成時に、自動的に Git initされているので、
コンテナごとまるっとGithub管理を統一したいので、自動的に作成されたプロジェクト配下の .git を消しちゃいます

# cd uuid-xplorer
# rm -rf .git

ポイントnode_modules 配下に大量にファイルが作られるので、.gitignoreで github 管理対象からはずしておきます
nodeでインストールすると、node_modules に関連ファイルが設置されると共に package.json にインストールした情報が追記されるので、コンテナの作成時に npm install を実行することで package.json内を参照してインストールし、同じ環境を作ることができます


Next.js インストールしたての package.json

{
  "name": "uuid-xplorer",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "12.1.4",
    "react": "18.0.0",
    "react-dom": "18.0.0"
  },
  "devDependencies": {
    "@types/node": "17.0.23",
    "@types/react": "17.0.43",
    "@types/react-dom": "17.0.14",
    "eslint": "8.12.0",
    "eslint-config-next": "12.1.4",
    "typescript": "4.6.3"
  }
}


docker-composeはこんな風に書いておけば、次回からは楽チン

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: frontend
    ports:
      - "7778:3000"
    volumes:
    - ./frontend/next-app:/usr/src/next-app:z
    command: bash -c "cd /usr/src/next-app/uuid-xplorer
      && npm install
      && tail -f /dev/null"
    networks:
      - uuid


Fast Refreshの有効

Next.jsではコード編集すると、stateを維持したまま変更が即時反映されます
これはめっちゃ便利な機能で、コイツの有無で開発スピードは断然違います
webpackDevMiddleware: config~部分を追記です
next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  webpackDevMiddleware: config => {
    config.watchOptions = {
      poll: 800,
      aggregateTimeout: 300,
    }
    return config
  },
}

module.exports = nextConfig


Next.jsで起動

uuid-explorerフォルダで以下を実行します

# npm run dev

これでサンプルサイトにアクセスできます
デフォルトでは、http://localhost:3000/
(今回の docker-composeの環境では http://localhost:7778/ でアクセスできるようにしています)


このページの内容は、uuid-explorer/pages/配下の
 _app.tsx
 index.tsx
になります

index.tsx をシンプルに編集してみます

import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        Welcome to UUID Xplorer
      </main>
    </div>
  )
}

export default Home

Fast Refreshが有効になっていれば、ファイルをセーブ後すぐに反映されるはずです


Next.js 準備完了

これでサクッと Next.js を実行できる環境ができました
あとは随時必要なモノが出てきたら、追加でインストールしていきます
次回、Webデザイン用に tailwindCSS, DaisyUI, fontawesomeをインストールしてみます

【Nutanix UUIDエクスプローラーを作ってみよう】React で JavaScript へ挑戦 - filter編


前回まで、CDNを使い fetchして取得したデータで state を更新して、表示というところまでを実装してきました
データ表示部分にリアルタイムでフィルタする動作を付け加えてみます
ようやく JavaScript 触ってるっぽい感じに


【Nutanix UUIDエクスプローラーを作ってみよう】シリーズ
Flask表示までのまとめ
React で JavaScript へ挑戦 - CDN導入編
React で JavaScript へ挑戦 - state編
React で JavaScript へ挑戦 - fetch編
・React で JavaScript へ挑戦 - filter編←イマココ

環境の準備

今回の最終形態はコチラ(Github)で公開しています
<フォルダ構造>


配列(リスト)を扱う

リストは map関数を使います、返り値も配列になります
配列のすべての要素に対して呼び出し、その結果からなる新しい配列を作って返します

vms.map((value, key) => {
  return (
    <div key={key}>
      {val}繰り返し処理で表示する内容
    </div>
  );
})

CDNで辞書型でキーを取り出したりはちょっとメンドそう)


試しにListクラス内で、サンプルの配列を作って map 使ってみるとこんな感じ(サンプルリストは UUID となんら関係ありません)

class List extends React.Component {
  render () {
    const labomen = ['kyoma', 'mayushi', 'daru', 'joshu', 'moeka', 'rukako', 'feilis', 'suzuha']
    const fglabo = labomen.map((member, key) => {
      return (
        <li key={key}>
          {member}
        </li>
      );
    })

    return (
      <div className="list">
        <ol>
          {fglabo}
        </ol>
      </div>
    );
  }
}


繰り返し処理される一番外の要素には key をつけてやらないと怒られます

フィルターしてみる

入力された文字列と一致する要素だけを表示するような、フィルターを実装してみたいと思います
filter関数を使うのが一般的なのかもしれませんが、CDN環境では使えなかったので、別の方法を使って入力されたキーワードにリアルタイムでフィルターしてみます

フィルターする方法として、「indexOf」を使ってみます

indexOf(filterText)

indexOf()は受け取ったテキストと一致する文字があれば、「インデックス番号」を、なければ「-1」を返します
これを使って、「-1」以外の要素を表示としてやります

文字が入力されるごとにレンダリングしてリアルタイムで表示を更新していきます(サンプルリストは UUID となんら関係ありません)

class List extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filterText: ''
    };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange = (event) => {
    this.setState( {[event.target.name]: event.target.value} )
  }

  render () {
    const labomen = ['kyoma', 'mayushi', 'daru', 'joshu', 'moeka', 'rukako', 'feilis', 'suzuha']
    const fglabo = labomen.map((member, key) => {
      if ( member.indexOf(this.state.filterText) !== -1 ) {
        return (
          <li key={key}>
            {member}
          </li>
        );
      }
      return;
    })

    return (
      <div className="list">
        <div><input type="text" placeholder="filter..." name="filterText" onChange={this.handleChange} /></div>
        <ol>
          {fglabo}
        </ol>
      </div>
    );
  }
}

今回は一致するものだけを表示というように作ってみましたが
例えば、一致した要素だけ色を変えたり表示を変えることもできるので、色々と応用できます

fetch してデータを map で取り出して filter する

これを実装することで、前回までの fetchして得られたデータの一覧表示とフィルタ機能のそれらしいものができました
/flaskr/static/js/test.js


さいごに

React を使うと 1ページで、色々できそうなことがみえてきました
ただ、Webサーバ側になんのインストールも必要ない超簡単実装できるCDNでは、できることの限界も見えてきたような気がします
次からは、ちゃんと環境構築して Next.js に挑戦します

【Nutanix UUIDエクスプローラーを作ってみよう】React で JavaScript へ挑戦 - fetch編

fetchって言葉は今回実際使ってみるまで、知らなかったのですが(Web初心者丸だし。。。)、Webページ上からFormなどから httpリクエストしてデータをひっぱってくるやつです

【Nutanix UUIDエクスプローラーを作ってみよう】シリーズ
Flask表示までのまとめ
React で JavaScript へ挑戦 - CDN導入編
React で JavaScript へ挑戦 - state編
・React で JavaScript へ挑戦 - fetch編 ←イマココ
React で JavaScript へ挑戦 - filter編

環境の準備

今回までの最終形態はコチラ(Github)で公開しています
<フォルダ構造>


JavaScript に fetch を導入

こんな感じで fetch 関数を呼び出してやることで httpリクエストしてやることができます

const response = await fetch("/api/connect", requestOptions);
// requestOptionsは他で設定する


Form で入力した値を使って httpリクエストする場合には、Form の onSubmit イベントから fetchをおこなう関数を呼び出してやります

<form onSubmit={this.handleConnectPrism} >

Form で受け取った Prism IP、ユーザ名、パスワードの state を使って、httpリクエストする関数はこんな感じ
/flaskr/static/js/test.js

  handleConnectPrism = async(event) => {
    event.preventDefault();
    const requestOptions = {
      method: "POST",
      headers: {'Content-Type' : 'application/json'},
      body: JSON.stringify({
        //Formから取得したデータ
        prism_ip: this.state.prismIp,
        prism_user: this.state.prismUser,
        prism_pass: this.state.prismPass,
      })
    }
    const response = await fetch("/api/connect", requestOptions);
    if (response.ok) {
      let res = await response.json();
      this.setState({
        // リクエスト結果を反映
      });
    }
    else {
      alert("HTTP-Error: " + response.status);
    }

ポイントは、非同期処理を行う async, await を使います
これを使わないと、httpリクエストの結果を待たず処理が進んでしまい、結果表示がおかしくなっちゃいます

state のリフトアップ

では、このfetchの関数をどこに設置するのか?です
React でクラスコンポーネントを使うときは、データの流れを親クラスから子クラスへというの意識する必要があります

今回の場合
1.3-1.FormConnect で、クラスタからデータ取得、Elasticsearchへデータ格納
2.3-2.FormDisplay で、Elasticsearch から表示用データ取得
3.4.List で、取得したデータを表示

この場合、この2つのクラスの親となる 2.Content に、stateをもたせて渡していくことになりますので、fetch関数もココに設置します
Contentクラスに配置させた場合の全体イメージは、こんなこんな感じ

</static/js/test.js>


httpリクエスト先の API作成

まだ現時点では、存在していない httpリクエスト先の 「/api/connect」、「/api/latestdataset」を作っていきます

基本的には Flaskで、API 受け口を作ってやるだけです
あとは以前から作っていたデータ取得の関数をよびだすだけ

from flask import make_response, jsonify

@app.route('/api/connect', methods=['POST'])
def connect():
    data = {}
    print(request.json)
    data['info'], data['cluster_name'] = connect_cluster(request.json)
    return make_response(jsonify(data))

@app.route('/api/latestdataset', methods=['POST'])
def latestdataset():
    cluster_name = request.json['cluster_name']
    data = get_dataset(cluster_name)
    return make_response(jsonify(data))



こうやって、Flaskを APIゲートウェイ的にどんどん作っていけます
ポイントは、Flaskからの返り値を json形式にしてやることで、javascriptで扱いやすくなります

return make_response(jsonify(data))


基本的には、追加していく API用のデコレータと関数は短めにして、共通関数を充実させていく感じですね
<app.py>

これで Elasticsearchにデータが入っている状態で、クラスタ名を入力し、表示ボタンを押してやれば、取得したデータが表示されます


React データ表示部分は

とりあえず表示していますが、React での Object 表示部分にはまだ触れていないので、また次回に

【Nutanix UUIDエクスプローラーを作ってみよう】React で JavaScript へ挑戦 - state編


ようやくここから JavaScript っぽいことをはじめていきます(JavaScriptよくわからないまま)

【Nutanix UUIDエクスプローラーを作ってみよう】シリーズ
Flask表示までのまとめ
React で JavaScript へ挑戦 - CDN導入編
・React で JavaScript へ挑戦 - state編 ←イマココ
React で JavaScript へ挑戦 - fetch編
React で JavaScript へ挑戦 - filter編

環境の準備

今回までの最終形態はコチラ(Github)で公開しています
<フォルダ構造>

関数コンポーネントとクラスコンポーネント

React ではユーザ定義したコンポーネントというのを組み合わせて、UIをつくっていきます
コンポーネントは、関数コンポーネントクラスコンポーネントというのがあります

くわしくは公式で コンポーネントと props – React

コンポーネントのレンダーにはこういうのん使います

<Element user='konchangakita' />

※ユーザ定義のコンポーネントは大文字で始めること


コンポーネント間でデータの受け渡しには Props というオブジェクトで渡すことができます
この例では、「name="konchangakita"」で渡して、「{props.name}」で呼び出しています
クラスコンポーネントProps の実装

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

  render () {
    return (
      <div className="container">
        <h1>Hello, {this.props.user}</h1>
      </div>
    );
  }
}

ReactDOM.render(
  <Element user='konchangakita' />,
  document.getElementById('root')
);


ステート - React の状態管理

次に React の重要な要素であるステートを実装します
ステートは、現在の状態を表す変数のようなものです
React では初期値をクラスごとに「constructor」の中で「this.state」で宣言し、更新には状態を更新する用の関数「this.setState」を使います
Pythonでいうところの変数を更新していくのとはちょっと勝手が違い、はじめはちょっと戸惑います
公式はコチラ state とライフサイクル – React

ステート初期値の宣言

クラスの先頭で、「this.state」まずステートの初期値の宣言だけを行ってみます

class Element extends React.Component {
  constructor(props) {
    super(props);
    this.state = { words: "El psy congroo"}
  }

  render () {
    return (
      <div className="container">
        <h1>{this.state.words}</h1>
      </div>
    );
  }
}

ReactDOM.render(
  <Element />,
  document.getElementById('root')
);


Form の input でステートを使う

Form で input された文字列を取り扱うには、入力イベントをキャッチして、ステートを更新する関数を宣言してやる必要があります
input タグの valueで現在のステートonChangeイベントでステート更新の関数を呼び出します
公式はこちら フォーム – React

ステート更新

ステートの更新は 「this.setState」 を使います
input要素に入力があるごとに呼び出される onChangeイベントの関数の中で「this.setState」を使って更新していきます
単純に変数に代入するだけではダメなのです
伝統的に onXxxxxイベントで呼び出される関数は handleXxxxと書くらしい

入力しモノをコンソールに出すだけ

class Element extends React.Component {
  constructor(props) {
    super(props);
    this.state = { words: "" };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    console.log(event.target.name, event.target.value);
    this.setState({ words: event.target.value });
  }

  render () {
    return (
      <div className="container">
        <form>
          <input type="text" name="words" value={this.state.words} onChange={this.handleChange}  />
        </form>
      </div>
    );
  }
}

ReactDOM.render(
  <Element />,
  document.getElementById('root')
);


console.log を置いておくと、デバッグに便利です
入力のたびにデベロッパーツールでコンソールに表示されます

ここまでの実装

公式のまとめに従い React の流儀 – React1つのコンポーネント1つの仕事だけさせるように、コンポーネントを分解していくことで、あとで機能を追加したり、メンテナンス性があがるらしい

というわけで、JavaScript 実装前の 前回まで作成していた Webページを コンポーネント単位に分解を考えます
データの流れを考えながら、/static/test.js をこんなイメージでコンポーネント単位で分解してみました

(3-1, 3-2で取得したデータを4で表示するために、2でデータを所持する。詳しくはまた次で)

/static/js/test.js

ちゃんと入力できているかのデバッグ確認はいくつかあると思いますが
console.logと合わせて React Developer Tools が分かりやすいと思います
React Developer Tools - Chrome ウェブストア

ブラウザのデベロッパーツールに機能が追加され、Reactコンポーネントの ステートがリアルタイムで表示されます



Reactを使う上では必須ツールと言えるでしょう

ステートを使いこなす

Reactのステート管理は長くなりがちでメンドウです
関数コンポーネントでは、フックを使っていくの結構楽になりますが、またあとの話になります
次回は、ステートで得た値を用いて fetch して→ データ取得 → ステート更新→ 表示を行います
ようやく Webアプリっぽくなってきます

【Nutanix UUIDエクスプローラーを作ってみよう】React で JavaScript へ挑戦 - CDN導入編

Flask の render template 機能で、Webページ表示というところまで作ってみました
次は、Webページの中身を JavaScript を使って表示というのにチャレンジです
ちな JavaScript は全くの初心者です

JavaScript の2大フレームワークとして、React と Vue というのある模様です
それぞれがどんな特徴か。。。というのはググればなんぼでもでてきますので、そちらにおまかせします
今回のUUIDエクスプローラープロジェクトでは、React で進めていきます
なんでかというと、信頼するエンジニアの方に「React の方がよいんじゃない?」と言っていただのがきっかけ、特になんの下調べもせずに飛び込んでみました


ja.reactjs.org

【Nutanix UUIDエクスプローラーを作ってみよう】シリーズ - React挑戦
Flask表示までのまとめ
・React で JavaScript へ挑戦 - CDN導入編 ←イマココ
React で JavaScript へ挑戦 - state編
React で JavaScript へ挑戦 - fetch編
React で JavaScript へ挑戦 - fetch編
React で JavaScript へ挑戦 - filter編


Reactどころか JavaScriptと初心者なので、チュートリアルを参考にしつつ、Getting Started をみながら実装を進めていきます
(日本語ページ助かる〜)

公式チュートリアルReact チュートリアル
チュートリアル:React の導入 – React

React Getting Started
Getting Started – React


環境の準備

今回の最終形態はコチラ(Github)で公開しています
<フォルダ構造>

今回のメインで扱うのは、js配下の test.js になります
(今回 Elasticは使いません)

React を CDN で導入する

とりあえずCDNで、React を使えるようにしてみます
Getting Started のこのあたりを参考にして、リンクをflaskで呼び出している index.html へ追加していきます
script タグを追加する
React で JSX を使う
JSXを使うことで、JavaScript独特な書き方をhtmlタグでわかりやすく書けるぽい

CDNリンク

こんな感じの CDN のリンクを追記します
/templates/index.html

    <!-- Reactをロード -->
    <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
    <!-- JSXを使えるように -->
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <!-- ReactのJavaScriptファイルの場所を指定 -->
    <script src="/static/js/test.js" type="text/babel"></script>  
    <!-- Reactをここまで -->


jsファイル作成

呼び出される側の JavaScript ファイルをシンプルにお試しで書いてみます
/static/js/test.js

const user = 'konchangakita';
const element = <h2>Hello, {user}</h2>;

ReactDOM.render(
  element,
  document.getElementById('root')
);


divタグで id指定

document.getElementById('root') で指定いる id を index.html で呼び出すだけです

<div id="root"></div>


index.htmlへの反映

CDNのリンクと合わせるこんな感じになります
./templates/index.html

      <h2>この下にReactする</h2>
      <div id="root"></div>

      <!-- Reactをロード -->
      <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
      <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
      <!-- JSXを使えるように -->
      <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
      <!-- ReactのJavaScriptファイルの場所を指定 -->
      <script src="/static/js/test.js" type="text/babel"></script>  
      <!-- Reactをここまで -->


Webページの下の方で表示されるようになりました


jsファイルに html書いてみるだけ

では、Flask の render template機能で表示した部分と同じものを作ってみる
基本的にはそのまんまでいけます
/templates/index.html

/static/test.js

見かけ上は、上下見た目は同じなハリボテができました


とりあえずの CDN導入編

まだ JavaScript っぽいことは何もしてないハリボテができただけです、次から本番です
実際にデータ受け渡しなどなど、コンポーネント、state、fetch へ進みます