20210119のReactに関する記事は10件です。

React.js & Next.js, Material UI + AWS Lambdaでお問合せフォームを作ってみた

はじめに

今回の記事は以下2冊の書籍の復習を兼ねて作成した内容になります。

  • React.js & Next.js 超入門(掌田津耶乃さん 著)
  • AWS Lambda実践ガイド(大澤文孝さん 著)

何を作ったのか

今回は上記2冊で学んだ内容を総動員ということで以下のようなお問合せフォームを作成してみました。
こちらに内容を記載し、「送信」ボタンをクリックすると管理人へメールが飛ぶ仕様です。
まあこれだけですとわざわざライブラリ等を利用するほどでもないのですが、一応復習ということで敢えて利用して作りました。
フロントエンドではReact.js & Next.js, CSS, Material UIを利用し、バックエンドではLambda(Python), S3, DynamoDB, API Gatewayを利用しています。
お問合せフォーム.gif

実装内容

簡単ではありますが実装内容を振り返っていきたいと思います。

画面系

こちらはコンポーネントとして「ヘッダー」「フォーム画面」「フッター」「レイアウト」とそれぞれ分けてファイルを作成しています。
https://github.com/Oooooomin2/ContactForm/tree/main/components

個人的にはMaterial UIの破壊力といいますか、便利さとデザインの綺麗さに圧倒されてしまいました。
今までは「簡単にデザイン等を作りたい」という時はBootstrapを使っていたのですが、こちらの方がデザインが今風で綺麗ですね。
以下のコードのみでテキストフィールドやヘッダーが作れちゃいました(送信ボタンとフッターは別途CSSで作成しています。)

ContactForm.js
import React, { Component } from 'react';
import { TextField, Input } from '@material-ui/core';
import Grid from '@material-ui/core/Grid';

class ContactForm extends Component {
    render() {

        return (
            <Grid container alignItems="center" justify="center">
                <form id="ContactForm" action="">
                    <Grid className="gridItems" item md={12}>
                        <TextField
                            required
                            fullWidth
                            id="Username"
                            label="お名前"
                            variant="outlined"
                            color="secondary"
                        />
                    </Grid>
                    <Grid className="gridItems" item md={12}>
                        <TextField
                            required
                            fullWidth
                            id="MailAddress"
                            label="メールアドレス"
                            variant="outlined"
                            color="secondary"
                        />
                    </Grid>
                    <Grid className="gridItems" item md={12}>
                        <TextField
                            fullWidth
                            id="PhoneNumber"
                            label="電話番号"
                            variant="outlined"
                            color="secondary"
                        />
                    </Grid>
                    <Grid className="gridItems" item md={12}>
                        <TextField
                            required
                            fullWidth
                            rows={15}
                            multiline
                            id="Contact"
                            label="お問合せ内容"
                            variant="outlined"
                        />
                    </Grid>
                    <Grid id="SubmitButtonGrid" className="gridItems" item md={12}>
                        <Input
                            id="SubmitButton"
                            type="submit"
                            variant="outlined"
                        />
                    </Grid>
                </form>
            </Grid>
        );
    }
}
export default ContactForm; 
Header.js
import React, { Component } from 'react';
import { AppBar, Toolbar, Typography } from '@material-ui/core';

class Header extends Component {
    render() {
        return (
            <header>
                <AppBar position="static">
                    <Toolbar>
                        <Typography variant="h6">
                            お問合せフォーム
                        </Typography>
                    </Toolbar>
                </AppBar>
            </header>

        );
    }
}
export default Header;

今回のお問合せフォームでは感じませんでしたが、UIを「コンポーネント」という部品で細かく管理していくReactは大きなサービスを作るにあたってはかなり有用だなと感じました。次回以降趣味等でアプリを作る際はReactが大活躍するよう大きめのアプリを作ってみようと思います。

サーバ側

サーバ側の処理の流れは以下になります。
(アプリはS3の静的ウェブサイトホスティング機能を用いて配信しています。)

  1. 「送信」ボタンを押します。
  2. API Gatewayを通してLambda関数へフォームデータが渡ります。
  3. DynamoDBに問い合わせ内容を格納します。
  4. Amazon SESを用いて管理者へ問い合わせ通知メールを送ります。

Lambda関数は以下です。
こちらのコードは上記の「AWS Lambda実践ガイド」に記載されたコードへ少々変更を加えて作成しています。
Lambda関数を作る際はAWS側が提供してくれる設計図(1からコードを書かなくてもいいように用途ごとに用意されている大まかなサンプルコード)がありますが僕にとっては本のコードが設計図になりました。
凄く勉強になりました。ありがとうございます。

Contact.py
import json
import boto3
import urllib.parse
import time
import decimal
import base64

dynamodb = boto3.resource('dynamodb')

MAILFROM='メールアドレス'
def sendmail(to, subject, body):
    client = boto3.client('ses')

    response = client.send_email(
        Source=MAILFROM,
        ReplyToAddresses=[MAILFROM],
        Destination={
            'ToAddresses': [ to ]
        },
        Message={
            'Subject': {
                'Data': subject,
                'Charset': 'UTF-8'
            },
            'Body' :{
                'Text': {
                    'Data': body,
                    'Charset': 'UTF-8'
                }
            }
        }
    )

def next_seq(table, tablename):
    response = table.update_item(
        Key={
            'tablename': tablename
        },
        UpdateExpression="set seq = seq + :val",
        ExpressionAttributeValues={
            ':val' : 1
        },
        ReturnValues='UPDATED_NEW'
    )
    return response['Attributes']['seq']

def lambda_handler(event, context):
    try:
        seqtable=dynamodb.Table('sequence')
        nextseq = next_seq(seqtable, 'contact')

        body = base64.b64decode(event['body'])
        param = urllib.parse.parse_qs(body.decode('utf-8'))
        username=param['username'][0]
        mailaddress=param['mailaddress'][0]
        phonenumber=param['phonenumber'][0]
        contact=param['contact'][0]

        host=event['requestContext']['http']['sourceIp']

        now=time.time()

        contacttable = dynamodb.Table("ContactInfo")
        contacttable.put_item(
        Item={
            'id': nextseq,
            'username': username,
            'mailaddress': mailaddress,
            'phonenumber' :phonenumber,
            'contact': contact,
            'accepted': decimal.Decimal(str(now)),
            'host': host
            }
        )

        mailbody = """

        {0}さん({1})からお問合せメッセージが届いたよ!

        {2}
        """.format(username, mailaddress, contact)

        sendmail(MAILFROM, 'お問合せ', mailbody)

        return {
            'statusCode': 200,
            'headers' : {
                'content-type': 'text/html'
            },
            'body':'<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>お問合せありがとうございました。</body></html>'
        }

    except:
        import traceback
        traceback.print_exc()
        return {
            'statusCode': 500,
            'headers': {
                'content-type': 'text/html'
            },
            'body': '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>内部エラーが発生しました。</body></html>'
        }

※エラーが発生した場合はCloudWatchにログが出力されますので確認してみてください。僕は何度もエラー出しました。

使ってみる

いざお問合せフォームを入力して「送信」ボタンを押すとメールも届いてDynamoDBにもしっかりと情報が格納されていました。

image1.png

image3.png

さいごに

作成したアプリのコード類は以下のGithubにて公開しております。
https://github.com/Oooooomin2/ContactForm

今後の展望としましては、自分が普段から使っているASP.NET Core MVCにReactやMaterial UIを組み込んでアプリを作ってみたいです(いつもフロントはjQueryとBootstrapなので)。Lambdaを使ったサーバレスアーキテクチャもだいぶイメージがつきましたので引き続き色々なものを作ったりして知識を定着させていこうと思います。

おまけ

今回、お問合せフォームをS3に配置するにあたってNext.jsの機能であるStatic HTML ExportにてHtmlを作成して配置しました。
その際に対象のエンドポイントにアクセスしても毎回403エラーが出力されたんですよね...。

解決策としまして、S3のバケットのアクセス許可だけではなく、その中に配置したオブジェクト一つ一つをアクセス許可することで解消できました。盲点でした。次からはハマらないように気をつけます(;^_^A

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] Reactの学習をします(1-4)式の埋め込み

Reactの学習をします(1-4)

Reactの学習をしてみることにしました。
前回の記事 : [React] Reactの学習をします(1-3)式の埋め込み

教材

Reactチュートリアル1:犬画像ギャラリーを作ろう

likr さんが公開している「Reactチュートリアル1:犬画像ギャラリーを作ろう」という記事を教材に学習させて頂きます。

素晴らしい教材をありがとうございます。

学習日記

本日は「条件分岐」~「Web アプリの公開」までやりました!

無事、公開することができました!

(これで、「Reactチュートリアル1:犬画像ギャラリーを作ろう」の学習は完了です。)

条件分岐

※ 教材から箇条書き的に抜粋させて頂きます。

・Dog API からの応答を受け取ってはじめて画像の一覧を表示することができるようになります。そのため、画像を取得し終わるまでは別の内容を表示しておかなければいけません。例えば、「loading」などと表示しておくと、まだデータを読み込み中であることがわかるでしょう。

・さて、データの取得が終わっているかどうかに応じて画面の表示内容を切り替える必要が出てきました。条件分岐によってそれを実現しましょう。

App.js
if (urls == null) {
  return <Loading />;
}

(一部抜粋させて頂きました。)

サーバーからのデータ取得

※ 教材から箇条書き的に抜粋させて頂きます。

・新たに src/api.js を作成し、そこに Dog API から画像取得を行う fetchImages 関数を作成します。

ここで、サンプルプログラムに async や await というのが出てきます。async や await 何となく聞いたことがあるのですが、何だったけ? と思ったので検索しました。

検索しますと、Qiita で @soarflat さんという方が書かれた記事が出てきました。読ませて頂きました。ありがとうございます。

async/await 入門(JavaScript)

さて、チュートリアルを続けます。

・React のコンポーネントは、アプリケーションの状態が同じであれば常に同じ JSX 式を結果として返すべきです

・アプリケーションの状態から JSX を組み立てることがこの関数の主作用となります。

・Web アプリケーションでは、アプリケーションの状態を更新するための副作用も発生します。例えば、外部のサーバーにリクエストを送ることなどが副作用にあたります。

・useEffect 関数は、時間の経過や外部サーバーからのリソース取得結果を処理するなど、コンポーネントの副作用を表現するために使われます。

Main コンポーネントを useEffect を使うように、書き換えていきます。

・useEffect の第 1 引数には、副作用を起こす関数を渡します。

・useEffect の第 2 引数には、その副作用が依存する値のリストを配列で渡します。

状態の変更

※ 教材から箇条書き的に抜粋させて頂きます。

・コンポーネントの状態を扱うためには useState 関数を使います。まずは、src/App.js の先頭で useState をインポートしましょう。

・次に、Main コンポーネントを以下のように書き換えます。

サンプルプログラムの通りに書き換えますと、Dog API から画像を取得を取得できるようになります!

フォームの操作とイベントハンドリング

※ 教材から箇条書き的に抜粋させて頂きます。

・今は柴犬の画像のみを表示していますが、Dog API は様々な犬種の画像を提供しています。表示する画像の犬種を選べるようにしてみましょう。

・フォームが送信されたときの処理を追加します。form要素において、フォームが送信されるときには submit イベントが発生します。React では、イベントを起こす要素にイベントを処理する関数を渡すことで行います。

・今回は、select要素で選択された値を引数として親コンポーネントから渡された onFormSubmit 関数を呼び出すことにします。

・select要素の値を受け取って処理する関数を reloadImages として作成します。関数の中身では、fetchImages 関数を呼び出して新しく取得した画像 URL のリストでurlsを更新します。

※ ここまでやりますと、このWebアプリケーションは完成です!!

Web アプリの公開

※ 教材から箇条書き的に抜粋させて頂きます。

仕上げに、開発した すばらしい 犬画像ギャラリーを、世界中の人がアクセスできるように公開しましょう。Web アプリの公開にはいくつもの方法がありますが、今回はNetlify を利用します。

成果物

Netlifyに公開させて頂きました!素晴らしいチュートリアルをありがとうございます!!

こちらです → Cute Dog Images

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] Reactの学習をします(1-4)Web アプリの公開

Reactの学習をします(1-4)

Reactの学習をしてみることにしました。
前回の記事 : [React] Reactの学習をします(1-3)式の埋め込み

教材

Reactチュートリアル1:犬画像ギャラリーを作ろう

likr さんが公開している「Reactチュートリアル1:犬画像ギャラリーを作ろう」という記事を教材に学習させて頂きます。

素晴らしい教材をありがとうございます。

学習日記

本日は「条件分岐」~「Web アプリの公開」までやりました!

無事、公開することができました!

(これで、「Reactチュートリアル1:犬画像ギャラリーを作ろう」の学習は完了です。)

条件分岐

※ 教材から箇条書き的に抜粋させて頂きます。

・Dog API からの応答を受け取ってはじめて画像の一覧を表示することができるようになります。そのため、画像を取得し終わるまでは別の内容を表示しておかなければいけません。例えば、「loading」などと表示しておくと、まだデータを読み込み中であることがわかるでしょう。

・さて、データの取得が終わっているかどうかに応じて画面の表示内容を切り替える必要が出てきました。条件分岐によってそれを実現しましょう。

App.js
if (urls == null) {
  return <Loading />;
}

(一部抜粋させて頂きました。)

サーバーからのデータ取得

※ 教材から箇条書き的に抜粋させて頂きます。

・新たに src/api.js を作成し、そこに Dog API から画像取得を行う fetchImages 関数を作成します。

ここで、サンプルプログラムに async や await というのが出てきます。async や await 何となく聞いたことがあるのですが、何だったけ? と思ったので検索しました。

検索しますと、Qiita で @soarflat さんという方が書かれた記事が出てきました。読ませて頂きました。ありがとうございます。

async/await 入門(JavaScript)

さて、チュートリアルを続けます。

・React のコンポーネントは、アプリケーションの状態が同じであれば常に同じ JSX 式を結果として返すべきです

・アプリケーションの状態から JSX を組み立てることがこの関数の主作用となります。

・Web アプリケーションでは、アプリケーションの状態を更新するための副作用も発生します。例えば、外部のサーバーにリクエストを送ることなどが副作用にあたります。

・useEffect 関数は、時間の経過や外部サーバーからのリソース取得結果を処理するなど、コンポーネントの副作用を表現するために使われます。

Main コンポーネントを useEffect を使うように、書き換えていきます。

・useEffect の第 1 引数には、副作用を起こす関数を渡します。

・useEffect の第 2 引数には、その副作用が依存する値のリストを配列で渡します。

状態の変更

※ 教材から箇条書き的に抜粋させて頂きます。

・コンポーネントの状態を扱うためには useState 関数を使います。まずは、src/App.js の先頭で useState をインポートしましょう。

・次に、Main コンポーネントを以下のように書き換えます。

サンプルプログラムの通りに書き換えますと、Dog API から画像を取得を取得できるようになります!

フォームの操作とイベントハンドリング

※ 教材から箇条書き的に抜粋させて頂きます。

・今は柴犬の画像のみを表示していますが、Dog API は様々な犬種の画像を提供しています。表示する画像の犬種を選べるようにしてみましょう。

・フォームが送信されたときの処理を追加します。form要素において、フォームが送信されるときには submit イベントが発生します。React では、イベントを起こす要素にイベントを処理する関数を渡すことで行います。

・今回は、select要素で選択された値を引数として親コンポーネントから渡された onFormSubmit 関数を呼び出すことにします。

・select要素の値を受け取って処理する関数を reloadImages として作成します。関数の中身では、fetchImages 関数を呼び出して新しく取得した画像 URL のリストでurlsを更新します。

※ ここまでやりますと、このWebアプリケーションは完成です!!

Web アプリの公開

※ 教材から箇条書き的に抜粋させて頂きます。

仕上げに、開発した すばらしい 犬画像ギャラリーを、世界中の人がアクセスできるように公開しましょう。Web アプリの公開にはいくつもの方法がありますが、今回はNetlify を利用します。

成果物

Netlifyに公開させて頂きました!素晴らしいチュートリアルをありがとうございます!!

こちらです → Cute Dog Images

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初ハッカソンでコロナ感染状況を可視化するサイトを作って、運良く最優秀賞を頂いた話

はじめに

こんにちは。
先日、レッドインパルス株式会社主催のFirebaseオンラインハッカソンで最優秀賞をいただきました!
以前に、私が所属するRuten株式会社でオンラインハッカソンを開催したことがあるのですが、参加するのは今回が初めてでした。
1週間という短い時間と、チームメンバーが普段の業務を抱えているという状況を踏まえると、うまくやりきれたと思います。
この記事では、開発したものの紹介や、期間中に考えたことや、主催する側と参加する側の両方を経験したことでわかったことなどを書いていきたいと思います。

何を開発したのか?

私たちが開発したのは、コロナウイルスの感染状況をポップに可視化してくれるサイト「コロナ君」です。
coronakun-ogp.png
デプロイしたものは「コロナ君」で見られます。
当日の発表スライドは「コロナ君発表スライド」で見られます。
コロナ君の公式Twitterはこちらです。

コロナ君の特徴

for-qitta.jpg

  • コロナウイルスの新規感染者発生状況によって、表示されるコロナ君の個体数が変動する. -> 感染状況を視覚的に理解できる。
  • より新規感染者が多い都道府県上に、よりコロナ君が集まるようになっている。 -> 一目で感染者の分布を理解できる。
  • 都道府県と日付の絞り込みで、ピンポイントで正確な情報が得られる。 -> 視覚的な理解から正確な数値へ、と理解を深めることができる。

使用技術

  • フロントエンド
    • React.js(SPA)
  • バックエンド
    • Firebase Cloud Functions(Node.js)
  • インフラストラクチャ
    • Firebase Hosting
    • Firebase Cloud Storage
  • データリソース

構成

スクリーンショット 2021-01-19 19.58.44.png
React.jsベースのSPAがCloud Functionのエンドポイントを叩くと、Cloud Functionが日本政府のAPIを叩きに行って、データの整形をして返してくれるようにしています。
データの処理はできるだけCloud Functionで行うようにして、フロントの処理を軽くしようという意図があります。

企画や開発時に考えていたこと

  • (チームメンバーの得意な分野が幸いにもバラバラだったので、)共通の仕様だけ決めたら、後は完全に任せちゃう。
  • (チームメンバーが作業できる時間が限られていたので、)時間に対するレバレッジが効くように選択と集中をしよう。
    • 評価項目(アイデア、技術力、プレゼン)のうち、アイデアとプレゼンで稼ごう。
  • 早めに、アイデアを実現した最低限のものを開発してデプロイしてしまおう。
  • 発表スライドの見栄えから逆算して、デザインと機能の選択を行おう。

主催する側と参加する側の両方を経験したことでわかったこと

  • 評価はプロダクトというより発表時の印象に大きく左右される。
  • 時間の制約を考慮した意思決定をしているチームは強い。
  • 参加チームの中でユニークな技術を使っていると評価されやすい。
  • ハッカソンを通して自分の相対的な技術力や企画力を認識できて、内省する機会が得られる。

最後に

今回、このハッカソンに参加したことで内省することができたのが一番の収穫でした。内省したことについて詳しくは以下に書いたので、興味があればご覧ください。
「初めてのハッカソンで、運良く最優秀賞を頂けて、色々と感慨深くなった話~プログラミング初心者に届け~」
今回一緒にチームを組んだメンバーである太一と、大地くんが、それぞれフロント開発とデザインを担当してくれました。彼らが私の苦手なところを素晴らしい仕事で補ってくれたので、良い結果を出すことができました。感謝します。
ハッカソンは受賞すると景品がもらえますし、受賞しなくても内省する機会が得られるので、是非参加してみてはいかがでしょうか?
アセット 1.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub Actionsで create-react-appをFirebaseへ自動デプロイする?

[OS:windows] 結果を先に。ポエムは最後。

ブラウザでGitHubの該当レポジトリのアクションタブをクリックし、"New workflow"をクリックし.ymlファイルを作成します。

以下.ymlのコード

masterDeploy.yml
name: masterDeploy CI

on:
  push:
    branches:
      - master
jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [15.x]
    steps:
      - uses: actions/checkout@v1
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install yarn Packages
        run: yarn install
      - name: Build page
        run: yarn build
        env:
          CI: false
      - name: use release
        run: yarn firebase use release --token xxxxx
      - name: deploy to Firebase Hosting
        run: yarn firebase deploy --token xxxxx

上から説明します。

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [15.x]
    steps:
      - uses: actions/checkout@v1
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

masterbranchがpushされたら実行。
OSはubuntu-latest
Node.jsはver15.xを使用する。
(Node.jsにyarnも入っているみたいので、指定後普通に使用できます)
下記のテンプレをコピペして使った感じなので、まだ完全に理解していません
https://docs.github.com/ja/actions/guides/building-and-testing-nodejs

      - name: Install yarn Packages
        run: yarn install
      - name: Build page
        run: yarn build
        env:
          CI: false 

yarn installしてビルドします。

現在のプロジェクトは大漁warningなので
CI:falseしないとビルドエラーが発生します。
(CI:trueで通るプロジェクトにはしたいと思っているよ。いつやるかは分からんけど。)

      - name: use release
        run: yarn firebase use release --token xxxxx
      - name: deploy to Firebase Hosting
        run: yarn firebase deploy --token xxxx

yarn でfirebaseを呼び出し実行します。
実行する時、トークンが必要なので
自分のPCのcmd等でfirebase login:ciを実行してトークンを入手。
xxxxに記入します。
(GitHubActionにシークレット定数入力する設定あるみたいなので、本当はそれ使う方が良いんだと思う)

ブラウザ上のGitHubでActionの動作確認した後、vscode等でプロジェクトをpullします。
このままだとpushする時workflowの権限がないというエラーが出るのでworkflowの権限等を有効にしたtokenを取得します。方法は下記リンク参照
https://docs.github.com/ja/github/authenticating-to-github/creating-a-personal-access-token

windowsだと、タスクバーの検索から資格情報マネージャーを起git:https://github.comを現在のユーザー名とパスワードをtokenにすればOK。
これで、ローカルからmastarにプッシュすればアクションが自動で実行されるはず。


現在、物語構築支援WEBツールなるものをReact & firebase で開発中です。
開発進捗等twitter:https://twitter.com/dicebook_dev

生活の事情で母艦PCをノートでリモートして開発していたのですが、母艦の電源が突然落ちたりして前々から気になっていたGitHubActionsを試してみました。無料枠もかなりあるので、個人で使用する分には十分。開発と本番環境の切り替えのいい方法が見つかって良かったです。(切り替えられると言ったわけではない。ブランチとAction(具体的にはコピー)を組み合わせた運用で試してみたい)
怪我の功名。ちなみにPCはUSBデバイス全部とグラボを抜いたら安定稼働してる。母艦はRyzen3900Xなので、グラボ抜いたらリモートデスクトップ使えないのかと思ってました。
普通に使えるのね。何かあったときはきついけど、RTX3060を買うまで耐えていきたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】いま一度、importとexportについて復習してみる

export

最初にexportから見ていきましょう。

名前付きexport

一つのファイル(モジュール)から複数の関数をexportできます。では、実際のコードを見ていきます。

export const Hoge = () => {
  return(
    <p>hoge</p>
  )
}
export const Aha = () => {
  return(
    <p>aha</p>
  )
}

こんな感じでexportできます。上記のように1ファイルから複数の関数コンポーネントを出力できます。

名前なしexport

一つのファイル(モジュール)から一つのコンポーネントしかexportできません。では、実際のコードを見ていきます。

const Hoge = () => {
  return(
    <p>hoge</p>
  )
}
export default Hoge

上記のように一つのコンポーネントしかexportできません。

Import

名前付きexportをImport

名前付きでexportされたモジュールをimportします。

import { Hoge, Aha } from './HogeAha'

1ファイル(モジュール)から複数のコンポーネントをimportするにはこの方法ですね。ライブラリからコンポーネント等を引っ張ってくる時によく使われますね。

名前なしexportをImport

  • 名前なしでexportされたコンポーネントをimportする。
  • モジュール全体をimportする。
import Hoge from './hoge'

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript オブジェクト内の更新するキーを動的に変える

手順

[]を使えば引数の値をキーとして使える

const onChangeSelection = (e, key) => {
    dispatch(
      setConditionsActions({
        ...conditions,
        [key]: e.target.value,
      })
    );
  };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】ユーザー認証をCognito + Amplifyで構築してみた ~サインインページカスタマイズ編~

はじめに

Reactで作成したWebアプリケーションのユーザー認証部分をCognito + Amplifyフレームワークで構築してみました。「【React】ユーザー認証をCognito + Amplifyで構築してみた」の構築準備編構築完成編を経て、現状下記のようなサインインページのWebアプリケーションが出来上がっています。

ezgif.com-gif-maker.gif

ただ、このWebアプリケーションは管理者が事前にユーザーを登録し、登録されているユーザーのみが使用できるようにしたいので、今回はサインインページにあるサインアップボタンを非表示にしたいと思います。

スクリーンショット 2021-01-11 18.33.47.png

既存のソースコード

※↓クリックするとソースコードが見れます。

既存のソースコード
App.js
import React, {useEffect} from "react";
import Amplify, {Auth} from 'aws-amplify';
import awsconfig from './aws-exports';
import {withAuthenticator} from "@aws-amplify/ui-react";

Amplify.configure(awsconfig);

function App() {
    const [currentUserName, setCurrentUserName] = React.useState("");
    useEffect(() => {
        const init = async() => {
            const currentUser = await Auth.currentAuthenticatedUser();
            setCurrentUserName(currentUser.username);
        }
        init()
    }, []);

    const signOut = async() => {
        try {
            await Auth.signOut();
        } catch (error) {
            console.log('error signing out: ', error);
        }
        document.location.reload();
    }

    return (
        <div>
            <h1>{currentUserName}さんこんにちは</h1>
            <button onClick={signOut}>サインアウト</button>
        </div>
    );
}

export default withAuthenticator(App);

既存のものは、withAuthenticatorコンポーネントでラップすることでサインイン画面等を表示していました。ただ、withAuthenticatorだとサインアップボタンを非表示にする方法を見つけられず、<AmplifyAuthenticator>というUIコンポーネントを使う方法だと簡単にサインアップボタンを非表示にできそうだったので、<AmplifyAuthenticator>を使うことにしました。

やってみる

公式ドキュメントを参考に実装していきます。

サインアップボタンを削除する

まず、サインインページにあるサインアップボタンを非表示にするために、UIコンポーネント<AmplifySignIn/>内にhideSignUp={true}を挿入します。これでサインアップボタンを非表示にすることができます。

<AmplifyAuthenticator >
    <AmplifySignIn slot="sign-in" hideSignUp={true} />
</AmplifyAuthenticator>

この他にもさまざまなプロパティが用意されています。デフォルトのユーザー認証にはユーザー名が使われています。例えば、ユーザー認証にメールアドレスを使いたいとなれば、<AmplifySignIn/>内にusernameAlias="email"を指定するだけで簡単に変更することができます。

ログイン判定

既存のソースではwithAuthenticatorコンポーネントでラップしていたので、ログインしているかどうかを特に気にしていませんでしたが、<AmplifyAuthenticator>を使うことで、ログインしている人としていない人で表示するコンポーネントを分ける必要があります。
ここで分けないと、Uncaught (in promise) not authenticatedというエラーが出て、ログイン後にユーザー情報が取得できません。

return authState === AuthState.SignedIn && user ? (
    <TopPage />
) : (
    <AmplifyAuthenticator >
        <AmplifySignIn slot="sign-in" hideSignUp={true} />
    </AmplifyAuthenticator>
);

ステータスがSignedInになっているか、ユーザー情報が取得できているかを確認することで、ログイン済みかどうかを判定しています。

MFAを使っている場合、一段階目のユーザー名とパスワードでの認証後には、userにユーザー情報が入ってしまうので、authStateも確認する必要があります。逆のパターンは見つけることができず・・、authStateだけで判定している方も見かけましたが、今回は公式に沿って、ステータスとユーザー情報の有無で判定するようにしています。

完成したソースコード

上記を踏まえて完成したソースコードです。

既存のものでは、ラップしていただけなので、トップページも同じコンポーネントにまとめていましたが、今回は分けることにしました。TopPage.jsの内容はほとんど既存App.jsのものと同じです。

App.js
import React, {useEffect} from "react";
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
import {AmplifyAuthenticator, AmplifySignIn} from "@aws-amplify/ui-react";
import {AuthState, onAuthUIStateChange} from "@aws-amplify/ui-components";
import TopPage from "./TopPage";

Amplify.configure(awsconfig);

function App() {
    const [authState, setAuthState] = React.useState();
    const [user, setUser] = React.useState();

    useEffect(() => {
        onAuthUIStateChange((nextAuthState, authData) => {
            setAuthState(nextAuthState);
            setUser(authData);
        });
    }, []);

    return authState === AuthState.SignedIn && user ? (
        <TopPage />
    ) : (
        <AmplifyAuthenticator >
            <AmplifySignIn slot="sign-in" hideSignUp={true} />
        </AmplifyAuthenticator>
    );
}

export default App;
TopPage.js
import React, {useEffect} from "react";
import Amplify, {Auth} from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure(awsconfig);

function TopPage() {
    const [currentUserName, setCurrentUserName] = React.useState("");
    useEffect(() => {
        const init = async() => {
            const currentUser = await Auth.currentAuthenticatedUser();
            setCurrentUserName(currentUser.username);
        }
        init()
    }, []);

    const signOut = async() => {
        try {
            await Auth.signOut();
        } catch (error) {
            console.log('error signing out: ', error);
        }
        document.location.reload();
    }

    return (
        <div>
            <h1>{currentUserName}さんこんにちは</h1>
            <button onClick={signOut}>サインアウト</button>
        </div>
    );
}

export default TopPage;

完成画面

サインアップボタンを非表示にすることができました!

スクリーンショット 2021-01-11 18.35.03.png

サインアップボタンが非表示になっただけで、画面の動き等は冒頭の完成画面と同じです。

おわりに

サインインページのカスタマイズも完成です!
実はこの実装をしたのが半年以上前で、そもそもこの情報がなかったのか、あったのにうまく調べきることができなかったのかは分かりませんでした。2日調べてサインアップボタンが消せなかったので、当時はUIコンポーネントの文言を日本語化する要領で無理やり消しました(笑)
今回この記事を書くにあたり、再度調べてみると、すごくあっさり見つけることができ、しかも実装も簡単で、こんなに簡単にできるんかーいという気持ちです。でも、今回正解の実装方法を知ることができたので、いい機会になりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを触ってみる(その1)

動機: フロントエンド、ガッツリやってみたい!

  • 普段はサーバーサイドエンジニアとして、主にPHP/Javaで開発することが多いのですが、JavaScriptでゴリゴリにフロント動かしてみたい!という欲求が昔からありました。かつ、どうせだったら「altJS」(alternative JavaScript)× 「JSフレームワーク」を使ってみたい!という思いがあり、今回Javascriptをガッツリ触ってみることにしました。

フレームワーク何にしようか問題

  • さて、JavaScriptをガッツリやっていくのはいいとして、どういう組み合わせを選ぶか?日本のGoogleトレンドを見てみると、以下のような結果に。

<順位>
React > Vue > Angular

【参考】
React,Vue,Angularのトレンド比較(日本)

ふむふむ。結構ReactとVueが拮抗してますね。もっとよく調べてみると、世界の有名サービスは軒並みReactで作られている、かつグローバルではReactのシェアのほうが高い。(Vue.jsもコミュニティがかなり頑張っているので、結構いい勝負ではあるようです)悩んだんですが、Reactを以前から触ってみたかったので、今回の学習では「React」を学ぶことにしました。

何はともあれ触ってみる: 開発環境構築

  • 開発環境PC: Windows10 Home

Node.jsのインストール

まずPCに「Node.js」をインストール。そもそも、React使うのに何でNode.jsインストールする必要があるんだっけ?今一度整理。

  • Node.js
    • JavaScriptをRubyやPythonと同じようにPCのターミナル上で動かすことができるようにするためのソフトウェア。JavaScriptをサーバーサイド言語として使えるようになる。
    • npm: Node.jsの為に作られたパッケージ管理システム。Node Package Managerの略。パッケージのインストール・アップデート・アンインストールだけでなく、リポジトリ機能も完備。
    • フロントエンド用のパッケージを提供するのにも用いられるようになる。Javascriptのパッケージ管理はnpmを使う。

以下のリンクの記事を参考にWindowsマシンにNode.jsを入れていきます。

【参考】
Windows 10へNode.jsをインストールする

Visual Studio Codeのインストール
React開発元のデフォルト開発環境でもあるらしい「VS Code」をインストール。

プロジェクトの作成 ~ Hello World!まで

  • Create React App(CRA)でプロジェクト(スケルトン)を作成する。適当なディレクトリで以下のコマンドを実行。
npx create-react-app hello-world --template typescript

今回はaltJSに「TypeScript」を使います。

  • Create React App
    • react: React本体パッケージ
    • react-dom: DOMを抽象化してReactから操作できるようにするレンダラーのパッケージ
    • react-scripts: 50個以上のパッケージ群を隠蔽してくれる。

コマンド実行が終わると、最後にメッセージが表示されるので、言われた通りに実行する。

$cd hello-world
$npm start
  • コマンドを実行すると、ローカルにHTTPサーバーが3000番ポートで立ち上がってアプリが起動されてる!ブラウザにデフォルト画面が表示されました。

http://localhost:3000/

  • Hello World!と表示させる為、App.tsxを編集
import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Hello World!
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;
  • Reactで作られるアプリケーションは全て「コンポーネント(Component)」の組み合わせで構成される
  • Reactではコンポーネントの実装は関数またはクラスで定義する

まとめ

  • 第一回はいったんこれにて満足w 今後、参考にしている本を読み進めて、さらに理解を深めていきます。

参考
『りあクト! TypeScript で始めるつらくない React 開発 第3.1版』
https://booth.pm/ja/items/2368045

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React / TypeScript】ESLint と Prettier を設定する(Prettier 編)

概略

前回の記事で, Linter(ESLint)を設定した React アプリに, Formatter(Prettier)を適用します。

なお, 現在までの流れは以下の記事です。
1. Create React App(TypeScript)でアプリを起動して Material UI ちょこっと触るまで
2. 【React / TypeScript】 ESLint と Prettier を設定する(ESLint 編)

Formatter はインデントのサイズとか改行とか諸々を揃えてくれるコード整形のツールですね。
(余談ですが, チーム開発においての Formatter 導入は, 諸々の宗教論争が容赦無く静まる感じがつよつよです)

本記事では, Formatter は Prettier を導入します。(著名なやつですね)

Prettier の導入

npm install する

公式 に従って進みます。
まず, 設定したいプロジェクトのディレクトリへ移動します。

% cd my-app

prettier を npm install します。(余談:--save-exact すると Version 固定で package.json) に入ります

% npm install --save-dev --save-exact prettier

package.json に Prettier の記載が追加されました。

package.json
"devDependencies": {
  "prettier": "2.2.1"
}

prettier の設定ファイルを用意する

公式通り, 以下を実行して Prettier の設定ファイル .prettierrc.json(空)を用意します。

echo {}> .prettierrc.json

Prettier の除外ディレクトリやファイルを指定するように, .prettierignore も追加します。

touch .prettierignore

.prettierignore の中身は適宜設定どうぞという形ですが, いったん公式通り以下な感じにしておきます

.prettierignoreの中身
# Ignore artifacts:
build
coverage

ESLint との共存のための設定をする

上記, npm install と設定ファイルの用意で動かせるのですが, ESLint を入れている関係で, ESLint と Prettier で競合するルールがあります。
なので, 戦いが始まらないように, ESLint 側に設定を追加します。

やり方の大筋

さて, 前までは eslint-plugin-prettier を用いて ESLint で Prettier を実行する感じが多かったようなのですが, Prettier 公式がそのやり方を非推奨としています。(理由などは公式を参照
つまるところ, ESLint で静的コード解析を実行して, Prettier でコード整形をするような単純な感じにするとよいとのことです。

では, 公式で指示されている通りに設定していきます。

eslint-config-prettier を設定する

公式 で案内されている通り, eslint-config-prettier は, ESLint と Prettier で競合するルールを, ESLint 上でオフにするために使うものです。

では, eslint-config-prettier の公式 に従って設定を進めます。
まず npm install します。

npm install --save-dev eslint-config-prettier

続いて, ESLint の設定ファイル .eslintrc.json 内の extends 末尾に prettier を追加します。

公式のコメント通り, Eslint の何の Plugin を使っているかによって, 必要な Prettier の記載を追加します。
"prettier" は基本として入れます。そして, 僕の現状の場合は plugin:react, plugin:@typescript-eslint を利用しているので, "prettier/@typescript-eslint""prettier/react" も入れます。

.eslintrc.json
"extends": [
    "... ...",
    "plugin:react/recommended",
    "airbnb",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
    "... ...",
    "prettier", // 追加
    "prettier/@typescript-eslint", // 追加
    "prettier/react" // 追加
]

ESLint の各種ルール(airbnb とかの)を, prettier 系で上書きして競合ルールをオフにするため, 末尾にいれる必要がある感じです。(後に書かれたもので手前の設定を上書きます)

Prettier を動かす

さて, Prettier の動作のための設定はおわったので, 動かします。

実行した場合にどうなるかを先にみる方法

ファイル名の指定のみで prettier を実行すると, 整形後をみることができます。

% ./node_modules/.bin/prettier src/App.tsx 

例えば以下のような src/App.tsx に対して実施すると,

src/App.tsx
import React from 'react';

const App: React.FC = () => (
  <div>
    Hello World
  </div>
);

export default App;

コンソールに以下のように出力されます(prettier 適用後の様子です)

% ./node_modules/.bin/prettier src/App.tsx 
import React from "react";

const App: React.FC = () => <div>Hello World</div>;

export default App;

整形を実行する

--write を指定して prettier を実行すると, 整形して書き換えられます。簡単ですね。

% ./node_modules/.bin/prettier --write src/App.tsx 

全部に対してやるなら . 指定です。

% ./node_modules/.bin/prettier --write . 

整形のスクリプトを npm scripts に追加する

前回の記事で ESLint を動かすように npm scripts に書きましたが, Prettier 用にも追加します。
package.jsonscripts に追加します。以下は src 配下すべてを整形する感じにしていますが, .ts .tsx などに絞るなどは良い感じにご指定ください。

package.json
"scripts": {
  // ... ...
  "lint": "eslint --ext .jsx,.js,.tsx,.ts src/", // ESLint 用
  "fmt": "prettier --write src/**/*" // Prettier 用
}

これで, 以下を実行すれば OK です。

% npm run fmt

そして最初の方の以下の話に戻ると,

さて, 前までは eslint-plugin-prettier を用いて ESLint で Prettier を実行する感じが多かったようなのですが, Prettier 公式がそのやり方を非推奨としています。(理由などは公式を参照
つまるところ, ESLint で静的コード解析を実行して, Prettier でコード整形をするような単純な感じにするとよいとのことです。

ESLint と Prettier は以下な感じで, fmt して lint する感じで動かすようになります。(npm scripts に追加した方がよいかな)

% npm run fmt && npm run lint

言い残し

最初の方で空で作って放置している .prettierrc.json ですが, 公式の設定例 とか設定できる内容 とか見つつ良い感じに入れると良いと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む