【React】React備忘録とAWS Cognito+Amplifyを用いたログイン機能

## Reactとは
Reactとは、主にSPA (Single Page Application : Webブラウザ側でページの移動を行わず、スクリプトがサーバとの通信や画面遷移を行う方式) のUI構築で使用されているオープンソースのJavaScriptライブラリである。Facebook(現:Meta)のソフトウェアエンジニアであるJordan Walkeが開発し、その後オープンソース化した。

https://reactjs.org/

Reactの記法として、JavaScriptとHTMLを組み合わせたような記法 JSX(JavaScript XML)で記述する。バニラJavascriptで実装するより、簡潔で可読性が増している。

他の有名なJavascriptフレームワークjQueryと比較すると、jQueryはHTMLを編集する必要があるのに対し、Reactはbuild後HTMLを書き換えてくれるので、HTMLを編集する必要がない。

## Reactの始め方

1. node.jsをインストールする
   https://nodejs.org/ja/download/
1. 基本パッケージのインストール
   ```
   npm install -g create-react-app
   ```
1. プロジェクトを作成
   ```
   create-react-app hello_world
   ```
1. Development Build(開発用ビルド)で動的な公開を行い、localhost:3000で確認してみる
    ```
    cd hello_react
    npm run start
    ```
    ![React1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/64c481c7-4348-895a-77ec-e44a491b1df8.png)
1. react jsx記法で書かれたファイルであるsrc/App.jsxを開いてみる
    ```react:App.jsx
    //Reactでは画像ファイルをインポートして使うみたい
    import logo from './logo.svg';
    //cssファイルをインポート
    import './App.css';
    
    function App() {
      //return()のかっこ内でブラウザに表示するところを作る
      return (
        <div className="App">
            //ヘッダーを記述
          <header className="App-header">
            //画像をApp-logoのデザインに従い表示
            <img src={logo} className="App-logo" alt="logo" />
            //文字を表示
            <p>
              Edit <code>src/App.js</code> and save to reload.
            </p>
            //リンク文字を記載
            <a
              className="App-link"
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              Learn React
            </a>
          </header>
        </div>
      );
    }
    //exportで外部読み込み可能にする
    export default App;
    ```
1. App.jsxのAPP関数コンポーネントを読み込んで、ID='root'でhtmlに紐づけているsrc/index.jsを開いてみる
    ```javascript:index.js
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();
    ```
1. ```<div id="root"></div>```でReactで作ったUIを呼んでいるpublic/index.htmlを開いてみる
    
    ```html:index.html
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="theme-color" content="#000000" />
        <meta
          name="description"
          content="Web site created using create-react-app"
        />
        <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
        <!--
          manifest.json provides metadata used when your web app is installed on a
          user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
        -->
        <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
        <!--
          Notice the use of %PUBLIC_URL% in the tags above.
          It will be replaced with the URL of the `public` folder during the build.
          Only files inside the `public` folder can be referenced from the HTML.
        
          Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
          work correctly both with client-side routing and a non-root public URL.
          Learn how to configure a non-root public URL by running `npm run build`.
        -->
        <title>React App</title>
      </head>
      <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
        <!--
          This HTML file is a template.
          If you open it directly in the browser, you will see an empty page.
        
          You can add webfonts, meta tags, or analytics to this file.
          The build step will place the bundled scripts into the <body> tag.
          
          To begin the development, run `npm start` or `yarn start`.
          To create a production bundle, use `npm run build` or `yarn build`.
        -->
      </body>
    </html>
    ```
1. src/App.cssを開いてみる
    ```css:App.css
    .App {
      text-align: center;
    }
    
    .App-logo {
      height: 40vmin;
      pointer-events: none;
    }
    
    @media (prefers-reduced-motion: no-preference) {
      .App-logo {
        animation: App-logo-spin infinite 20s linear;
      }
    }
    
    .App-header {
      background-color: #282c34;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      font-size: calc(10px + 2vmin);
      color: white;
    }
    
    .App-link {
      color: #61dafb;
    }
    
    @keyframes App-logo-spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    ```
1. 本番用にビルドする。buildディレクトリが生成され、公開用ファイルにまとめられる。
    ```
    npm run build
    ```
1. ローカルサーバーを立ち上げ、ブラウザで確認してみる
    ```
    npm install -g serve
    serve -s build
    ```
    Live Serverでも試してみる。※親フォルダ"/"がbuildになるようにしよう
    ```
    VS codeのフォルダで、index.htmlを右クリック
    Open with Live serverを押す
    ```

これでReactの公開用ファイルが作成できた。あとは、App.jsxやindex.jsに作りたいWeb UIを記述する。

## AWS Amplifyライブラリを用いたログインUI追加

1. amplifyのライブラリをインストール
    ```
    npm install aws-amplify
    npm install @aws-amplify/ui-react
    ```
1. src/App.jsxに以下を追加
    userPoolId,userPoolWebClientId,identityPoolId,domainは以下の記事を参照して、AWS Cognitoで取得してください。
    https://qiita.com/grapefruit_0514/items/cf9e468c86a1aab4f03a
    ```diff_javascript:App.jsx
    + import React, { useEffect, useState } from 'react'
    + import {Amplify, API, Auth, Hub } from 'aws-amplify';
    + import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react';
    + import '@aws-amplify/ui-react/styles.css';
    + Amplify.configure({
    +   Auth: {
    +     region: 'us-east-1',
    +     userPoolId: 'us-east-1_XXXXXXXXXX',
    +     userPoolWebClientId: 'XXXXXXXXXXXXXXXXXXXX',
    +     identityPoolId: 'us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX',
    +     oauth: {
    +       domain: 'XXXX.auth.us-east-1.amazoncognito.com',
    +       scope: ['openid'],
    +       redirectSignIn: 'https://localhost:3000/',
    +       redirectSignOut: 'https://localhost:3000/',
    +       responseType: 'code'
    +     }
    +   }
    + })
    ```
    ```diff_javascript:App.jsx
    - function App() {
    + function App({ signOut, user }) {
    ```
    ```diff_javascript:App.jsx
    - export default App;
    + export default withAuthenticator(App, {
    +       signUpAttributes: ['email'],
    + })
    ```
1. src/index.jsに以下を追加
    ```diff_javascript:index.js
    + import { Amplify } from 'aws-amplify';
    + Amplify.configure({
    +   Auth: {
    +     region: 'us-east-1',
    +     userPoolId: 'us-east-1_XXXXXXXXXX',
    +     userPoolWebClientId: 'XXXXXXXXXXXXXXXXXXXX',
    +     identityPoolId: 'us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX',
    +     oauth: {
    +       domain: 'XXXX.auth.us-east-1.amazoncognito.com',
    +       scope: ['openid'],
    +       redirectSignIn: 'https://localhost:3000/',
    +       redirectSignOut: 'https://localhost:3000/',
    +       responseType: 'code'
    +     }
    +   }
    + })
    ```
1. npm run startでlocalでログインを確認する
    ・ サインイン画面
    ![React2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/30c396d0-50b4-8eb6-ee87-3289f45413b4.png "Sign in image")
    ・ サインアップ画面
    ![React3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/f3c25f38-c34b-bbcd-b72a-03fa159497d5.png "Sign up image")
   サインアップして、アカウント登録し、サインインすると、同じURL(Localhost:3000)でreactの初期画面が表示される。

## クラスコンポーネントでの場合
クラスコンポーネントの場合、propsで引数を一括で受け取るので、明示的にsignOut, userを与える必要がない。

```diff_javascript:App.jsx
+   class App extends Component {
+     constructor(props) {
+       super(props)
+     }
+     render() {
       return (
               <div className="App">
               //ヘッダーを記述
             <header className="App-header">
               //画像をApp-logoのデザインに従い表示
               <img src={logo} className="App-logo" alt="logo" />
               //文字を表示
               <p>
                 Edit <code>src/App.js</code> and save to reload.
               </p>
               //リンク文字を記載
               <a
                 className="App-link"
                 href="https://reactjs.org"
                 target="_blank"
                 rel="noopener noreferrer"
               >
                 Learn React
               </a>
             </header>
           </div>
       )
+     }
+   }
```

## Reactの特徴

1. classの代わりにclasssName、forの代わりにhtmlForを使用する
1. 変数など、文字列以外の値を代入する際は{ }でくくる
1. コメントアウトはreturn外は//、return内は```{/*文字*/}```で囲む
1. オブジェクトはコンポーネントと呼ばれ、基本的にはクラスではなく関数コンポーネントを使う
1. publicにはhtmlなど静的で加工されることなく公開されるファイルを置く
1. srcにてビルドが必要なファイルを置く
1. コンポーネントには、ライフサイクルというMounting、Updating、UnMountingの状態がある
1. コンポーネントなどはキャメルケース(単語の繋ぎ部分だけ大文字にする記法)で定義する
1. `const Colored = (props) => {`などで引数propsで渡し、`props.option`で中の変数や関数を呼び出す
1. Reduxにより、多コンポーネント間のデータ共有が可能
1. 関数コンポーネントを書く際にfunctionを明示的に使わなくてもよく、アロー関数を使うのが一般的
1. Virtual DOMを採用
>ChatGPT : 
>Virtual DOMはReactにおいて使われる仕組みです。Virtual DOMとは、ブラウザのDOM（Document Object Model）と同じ形式を持った仮想的なDOMのことです。Virtual DOMを使うことで、Reactは効率的にUIの変更を検知し、必要な部分だけを更新することができます。これにより、ブラウザのDOMを直接操作するよりも高速なレンダリングが可能になります。また、Virtual DOMはReactにおいて単純なAPIを提供しているため、開発者が直接ブラウザのDOMを操作することなく、効率的なUI開発ができます。

## メモ
1. Reactで大幅に変更したのち、本番環境へアップロードしてもページが古いままの場合がある。この場合、クライアン側のキャッシュを読み込んでいる可能性があるため、キャッシュを削除すれば更新される。

## まとめ
今回は、ReactとAWS Amplifyライブラリを用いて、AWS cognitoのログイン認証画面と認可機能を実装した。既存のwebサイトやreactなどで新しく作成したwebサイトに、最後に非常に簡単にログイン認証・認可機能を実装できる。次はログイン後の中身を、下記サイトのような既存の無料テンプレートを参考に作成していく。

https://coreui.io/

https://github.com/coreui/coreui-free-react-admin-template

https://ui.docs.amplify.aws/

https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration