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

業界未経験の私がポートフォリオを作って転職をしたい件

自分で作ったポートフォリオサイト・作品集(作成途中)

こちらに今まで作ったアプリが入っているので、ぜひご覧ください。

TOPページ

スクリーンショット 2021-01-05 21.12.34.png

TOPページは、誰もが操作しやすく簡単に理解できる見た目にしてみました。構成は4つ。
1. 自分のプロフィール
2. スキル/制作実績
3. 学習の積み上げ報告(twitter)
4. Qiita
この4つを意識することが転職活動を成功に導くと仮定して選びました。

作品集(サイトを見ればもう一つあります)」

スクリーンショット 2021-01-05 21.18.17.png
作品集はスキル/制作実績ページにあります。

画像向かって左側には自分の趣味であるウイスキーや旅、筋トレをいろんな人が主体的に投稿して楽しめたらいいなという思いで作りました(未完成)。

作ってみたきっかけ

きっかけは単刀直入にエンジニアになることでした。
東日本大震災で被災した友人から当時の話を聞いたことをきっかけに、人のために働きたいという想いが根底にあり、今一番感動させられるのはIT技術だと思ったからです。今やスマホがないと生きていけない人がほとんどな世の中で、自分自身がサービス開発に携わり、サービス利用者を満足させたいと思いました。

使用言語

フロントエンド言語であるjavascriptを選びました。
その中でもSPA(シングルページアプリケーション)を実装したかったので、reactとvueで作ってました。
前述したポートフォリオサイトはreactを使用しました。

サイト概要

https://yapiro.github.io/pf-site/

  • このサイトは主に自分を知ってもらうために作成
  • 自己紹介をはじめ、作品、学習記録をアピールすることが目的

アプリ概要

https://yapiro.github.io/portfolio/
reactで作りました。

  • ブログ簡易掲示板
  • 投稿機能
  • 写真投稿機能

使い方

上半分はブログのため、見たい記事があればクリック
下半分は3つの項目(ウイスキー・旅・筋トレ)をみんなと共有したいときにクリック

ローカル環境へのインストール方法

git clone https://yapiro.github.io/portfolio/
npm install
npm start

現在

現在もアプリは作成途中です。
作成した際はhttps://github.com/yapiro に順次掲載していきます。

twitterでもリンクを貼っています。

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

react hooksでrouterを利用するやり方とハマりポイント

ぼくは

やっている最中exactの意味が分からず20時間くらいハマりました

初期アプリの作成

create-react-app react-router
cd react-router
rm -fr src/*

routerパッケージをインストール

npm install react-router-dom

ファイルを配置

src/index.html
<div id="root"></div>
<script src="./index.js"></script>

ポイント

  • Route内のexactは必須
    • exactはデフォルトだとfalse
    • 有効にする(書く)と、前方一致で/がヒットしてしまう
    • 今回の場合のRouteは//reactとあるが、/reactにあたるRouteでexactをつけていないと、先に/がヒットしてそちらにリダイレクトするような動きになるので、たぶんとりあえずつけておいたほうが良いんじゃないかなと思う
  • BrowserRouter as Router
    • as句があるので、Routerは実質BrowserRouterが呼び出されていることになる
    • RouterとBrowserRouterが何が違うかと言うとよくわからないけど、BrowserRouterは内部的にhistoryをpropsに渡しているらしい
    • つまるところ、Routerを使って、次のようなエラーになった場合、historyがpropsにわたっていないのでエラーになっているので、わからないうちはBrowserRouterを使ってあげるのがたぶん早いんだと思う
src.index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';

function Hello() {
  return (
    <div>
      <h1>to react</h1>
      <Link to={"/react"}>
          Link
      </Link>
    </div>
  );
}

function HelloReact() {
  return (
    <div>
        <h1>to root</h1>
        <Link to={"/"}>
            Link
        </Link>
    </div>
  );
}

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/" component={Hello} exact />
        <Route path="/react" component={HelloReact} exact />
      </Switch>
    </Router>
  );
}

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

起動

npm start

/reactへ

image.png

/へ

image.png

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

React JSXの書き方

Reactを勉強していると、
JSXでいろんな書き方が出てきて混乱してしまう。
そもそもJavascriptが苦手なので、よくわからない。
そんな私が未来の自分(またよくわからなくなった時の自分)へ宛てた記事です。

import React from "react";
export default function (){
    return(
        <div>
            <h1>あいうえお</h1>
        </div>
    )
}

import React from "react";
const Kansu1 = function (){
      return(
        <div>
            <h1>あいうえお</h1>
        </div>
    )
 }
 export default Kansu1

import React from "react";
const Kansu2 = ()=>{
    return(
        <div>
            <h1>あいうえお</h1>
        </div>
    )
}
export default Kansu2

上の3つは関数の名前は違うけれど全部同じ動きをする。
これがよくわからなかったので、整理していく。

JSXの流れ

  • returnするだけの関数を作る
  • 表示したい内容をreturnの中身に入れる。
  • 関数をexport defaultする

これが基本!絶対のルール!:hand_splayed_tone2::hand_splayed_tone2::hand_splayed_tone2:
惑わされないで:bangbang:

改めて、最初の3つについて確認すると、

①は関数を直接export defaultしているだけ。

②③は定数にreturnする関数を代入。定数をexport defaultしている。

③は②をアロー関数を使って書いただけ。

アロー関数を忘れた人:point_down_tone2::point_down_tone2::point_down_tone2:

普通の関数の定義 

const 定数名 = function(){処理};

アロー関数 

function()の代わりに()=>で関数を定義できる。 

const 定数名 = ()=>{処理}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactコンポーネントの状態で初心者がしてしまう3つの間違い

本記事は、Tyler Hawkins氏による「3 Mistakes Junior Developers Make With a React Component's State」(2020年6月26日公開)の和訳を、著者の許可を得て掲載しているものです。

Reactコンポーネントの状態で初心者がしてしまう3つの間違い

そして、それをやめる方法

Image for post

Photo by Jamie Street on Unsplash.

私がウェブ開発で好きなことの1つは、常に新しいことを学ぶことができるということです。さまざまなプログラミング言語、ライブラリ、フレームワークを習得するのに一生を費やしても、すべてを理解することはできません。

私達は皆学び続けているので、それは間違いをおかしやすいということも意味します。でもそれでいいのです。目標は、より良くなること、より良くあることです。間違ってもそこから学べば、あなたは素晴らしいことをしているということです!しかし、新しいことを学ばず、同じ間違いを繰り返しているとしたら...あなたのキャリアが停滞しているような気がします。

そのような考えで、コードレビューでよく目にする間違いを3つご紹介します。Reactコンポーネントの状態の処理で、初心者がよくする間違いです。それぞれをよく観察し、修正する方法について説明します。

1. 状態を直接変更する

コンポーネントの状態を変更する時は、現在の状態を直接変更するのではなく、変更を加えた状態の新しいコピーを返すことが重要です。コンポーネントの状態を誤って変更すると、Reactの差分アルゴリズムが変更を取得できず、コンポーネントが適切に更新されません。

例を見てみましょう。次のような状態があるとします。

this.state = {
  colors: ['red', 'green', 'blue']
}

次に、この配列にyellowを追加します。こうしたくなるかもしれません。

this.state.colors.push('yellow')

またはこのように。

this.state.colors = [...this.state.colors, 'yellow']

しかし、どちらの方法も正しくありません!クラスコンポーネントの状態を更新する時は、常にsetStateメソッドを使用し、オブジェクトを変化させないように注意する必要があります。

配列に要素を追加する正しい方法は、次の通りです。

this.setState(prevState => ({ colors: [...prevState.colors, 'yellow'] }))

これが2番目の間違いにも関係してきます。次を見てみましょう。

2. 関数を使用せずに、前の状態に依存する状態を設定する

setStateメソッドを使用する方法は2つあります。1つ目は、引数としてオブジェクトを指定する方法です。2つ目は、引数として関数を指定する方法です。では、どのような場合にどちらを使用すべきなのでしょうか?

例えば、有効または無効にできるボタンがある場合、ブール値を保持するisDisabledという状態の一部があるかもしれません。ボタンを有効から無効に切り替えたい場合は、引数としてオブジェクトを指定して、こう書きたくなるかもしれません。

this.setState({ isDisabled: !this.state.isDisabled })

これの何が問題なのでしょうか?問題は、Reactの状態の更新はバッチ処理が可能であるという事実にあります。つまり、単一の更新サイクルで複数の状態更新が発生する可能性があります。更新がバッチ処理され、有効・無効の状態に複数の更新があった場合、最終的な結果は期待したものではない可能性があります。

状態を更新するより良い方法は、引数として前の状態の関数を指定することです。

this.setState(prevState => ({ isDisabled: !prevState.isDisabled }))

これで、状態の更新がバッチ処理され、有効・無効の状態に複数の更新が同時に行われた場合でも、それぞれの更新は正しい前の状態に依存するため、常に期待通りの結果が得られます。

カウンタのインクリメントのような場合にも、も同じことが言えます。

こうしないで

this.setState({ counterValue: this.state.counterValue + 1 })

こうしてください。

this.setState(prevState => ({ counterValue: prevState.counterValue + 1}))

ここで重要なのは、新しい状態が古い状態の値に依存している場合は、常に関数を引数として指定する必要があるということです。古い状態の値に依存しない値を設定する場合は、引数としてオブジェクトを指定できます。

3. setStateが非同期であることを忘れる

最後に、setStateが非同期メソッドであるということを覚えておくことが重要です。例として、次のような状態のコンポーネントがあるとしましょう。

this.state = { name: 'John' }

そして、状態を更新してコンソールに状態を記録するメソッドがあるとします。

this.setState({ name: 'Matt' })
console.log(this.state.name)

これで「Matt」が記録されると思うかもしれませんが、違います。「John」が記録されます!

これは、setStateが非同期だからです。つまり、setStateを呼び出す行に到達すると状態の更新が開始されますが、非同期コードは非ブロッキングであるため、その下のコードは続けて実行されます。

状態が更新されたに実行する必要のあるコードがある場合、Reactには、更新が完了すると実行されるコールバック関数があります。

更新後に現在の状態を記録する正しい方法は、次の通りです。

this.setState({ name: 'Matt' }, () => console.log(this.state.name))

ずっと良くなりました!これで、期待通りに「Matt」が正しく記録されます。

まとめ

これがよくある3つの間違いとその修正方法です!覚えておいてください。間違っても大丈夫。あなたは学び続けています。私もです。私達は皆学び続けています。一緒に学び続け、より良くなりましょう。

更新:同じ概念を、関数コンポーネントとフックを使用して学びたいですか?私のフォローアップ記事を読んでください!

React関数コンポーネントの状態で初心者がしてしまう3つの間違い

Zack Shapiroに謝意を表します。

翻訳協力

Original Author: Tyler Hawkins
Original Article: 3 Mistakes Junior Developers Make With a React Component's State
Thank you for letting us share your knowledge!

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。
選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

ご意見・ご感想をお待ちしております

今回の記事はいかがでしたか?
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
皆様のメッセージをお待ちしております。

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

【React.js/Next.js】styled-componentsとnext/linkの併用

styled-componentsとNext.jsのLinkを普通に併用すると上手くいかなかったので、上手くいった実装をメモしておく。

import { FC } from "react";
import Link from "next/link";
import styled from "styled-components";

const StyledLink = styled.a`
  // 省略
`;

const Hoge: FC = () => {
  return (
    <Link href="/" prefetch passHref>
      <StyledLink>リンク</StyledLink>
    </Link>
  );
};

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

UI5 Web Components for React で参戦ライブを振り返る

はじめに

HR/HM 愛好家の方々とオンライン飲みをするにあたり、自分自身がこれまで参戦してきたライブを振り返るために電子化しておこうと思い立ち、せっかくなので(?)UI5 Web Components for React を使ってシンプルな React アプリケーションを作成してみました。

SAPUI5 ネイティブではなく、UI5 Web Components を使うことのメリットなどは、以下の tami さんの記事をご参照ください。

UI5 Web Components for React の紹介

UI5 Web Components for React とは?

公式サイト上では、以下のように紹介されています。要するに、React ネイティブのように扱える UI5 Web Components ということです。

UI5 Web Components for React is a Fiori3 compliant React library built on top of the UI5 Web Components. With the help of UI5 Web Components for React, you can use UI5 Web Components as if they were native React components. In addition to that, UI5 Web Components for React is providing complex components and layouts on top of the UI5 Web Components.

UI5 Web Components とは?

では、UI5 Web Components は何かというと、公式サイト上では、以下のように紹介されています。要するに(ネイティブだと参入障壁の高い)SAPUI5 を Web Components 化することで、簡単に扱えるようにするということです。

The UI5 Web Components share fundamental UI5 qualities with others to provide enterprise-grade features, Fiori UX and themeability. The goal is to achieve an easy consumption of UI5 controls to lower the entry barrier to use UI5 controls for own applications and to avoid complexity of UI5 rendering-stack by making the consumption of the full-stack UI5 optional. In general, the UI5 Web Components are targeting for Web Developers who want to have more flexibility to use just HTML tags or arbitrary JS frameworks.

Web Components とは?

では、そもそもの Web Components は何かというと、公式サイト上では、以下のように紹介されています。要するに再利用可能でカプセル化された独自の HTML タグを提供するためのものです。

Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.

具体例での比較

シンプルな Link を例として、SAPUI5、UI5 Web Components、UI5 Web Components for React を比較してみましょう。

まずは、SAPUI5 から。SAPUI5 はオープンテクノロジがベースになっていますが、SAP 固有のクセはあります。公式リファレンス を見ればクセが理解できると思いますが、普段 React や Vue.js をお使いの方からすると、少なくとも初見では「?」だと思います。

SAPUI5 では、MVC フレームワークに従い、ビューを XML ファイルとして定義します。Link だけでないのは、比較する上でアンフェアな気もしますが、SAPUI5 らしさを伝えるためにビュー全体を載せました。ちなみに、イベント処理等は、別のコントローラ(JavaScript ファイル)にコードします。

SAPUI5 の場合 (Hoge.view.xml)
<mvc:View
    controllerName="sap.m.sample.Link.LinkGroup"
    xmlns:l="sap.ui.layout"
    xmlns:mvc="sap.ui.core.mvc"
    xmlns="sap.m">
    <l:VerticalLayout
        class="sapUiContentPadding"
        width="100%">
        <l:content>
            <Link
                text="Open SAP Homepage"
                target="_blank"
                href="https://www.sap.com" />
        </l:content>
    </l:VerticalLayout>
</mvc:View>

一方、Web Comoponents の場合は以下のようになります。

UI5 Web Components の場合
<ui5-link href="https://www.sap.com" target="_blank">Open SAP Homepage</ui5-link>

Web Comoponents for React の場合は以下のようになります。

UI5 Web Components for React の場合
<Link href="https://www.sap.com" target="_blank">Open SAP Homepage</Link>

どちらも SAPUI5 を Web Components(=再利用可能でカプセル化された独自の HTML タグ)として扱えるようになっています。確かに SAPUI5 ネイティブならではの参入障壁の高さは、軽減されていますね。

ライブ参戦記録アプリを作成する

React アプリケーションの作成

lives-in-my-life アプリケーションを作成します。色々と楽なので、UI5 Web Components for React 用のテンプレートを指定しました。

新規で作成する場合
$ npx create-react-app lives-in-my-life --template @ui5/webcomponents-react

ちなみに、既存の React アプリケーションに追加する場合は、以下のコマンドを実行します。

後から追加する場合
$ npm install @ui5/webcomponents @ui5/webcomponents-react @ui5/webcomponents-fiori --save

React アプリケーションの確認(テンプレート)

@ui5/webcomponents-react テンプレートによって生成されたアプリケーションを実行してみます。

$ npm start

生成直後のアプリケーションは以下のようなシンプルなものです。おおまかに言えば、ShellBarLink から構成されています。
screenshot-localhost_3000-2020.12.31-17_24_45.png

ちなみに、生成直後のソースコードは以下の通りです。

App.js
import {
  FlexBox,
  FlexBoxAlignItems,
  FlexBoxDirection,
  FlexBoxJustifyContent,
  Link,
  LinkDesign,
  ShellBar,
  ThemeProvider
} from '@ui5/webcomponents-react';
import React from 'react';
import './App.css';

function App() {
  return (
    <ThemeProvider>
      <ShellBar primaryTitle="UI5 Web Components for React Template" />
      <FlexBox
        style={{ width: '100%', height: '100vh' }}
        direction={FlexBoxDirection.Column}
        justifyContent={FlexBoxJustifyContent.Center}
        alignItems={FlexBoxAlignItems.Center}
      >
        <Link href="https://sap.github.io/ui5-webcomponents-react/" target="_blank" design={LinkDesign.Emphasized}>
          Getting Started with UI5 Web Component for React
        </Link>
      </FlexBox>
    </ThemeProvider>
  );
}

export default App;

ライブ参戦記録アプリの実装

テンプレートで生成された初期アプリケーションに対して、主に以下の変更を加えました。

  • ShellBar に検索ボックスを追加(参戦ライブを絞り込み)
  • Timeline を追加(参戦ライブを表示)

以下の変更は、おまけみたいなものです。

  • ShellBar にロゴを追加
  • ShellBar のタイトルを変更
  • ShellBar にプロファイル画像を追加

実行結果は以下の通りです。せっかくなのでダークテーマを指定してみました。UI テーマは sap-ui-theme URL パラメータによって動的に切り替えることが可能ですが、以下のコードでは sap_fiori_3_dark を固定セットしています。
screenshot-localhost_3000-2021.01.04-15_50_45.png
検索結果がないときは、MessageStrip でその旨のメッセージを表示するようにしました。
screenshot-localhost_3000-2021.01.04-15_51_11.png
ちなみに、変更後のソースコードは以下の通りです。JSON 直書きだったり、日付データが yyyy/MM/dd 形式だったり、i18n 対応していなかったり、コード分割を検討していなかったり、色々と考えるべきことはありますが、お手軽な初期バージョンとして晒します。

App.js
import {
  Avatar,
  FlexBox,
  FlexBoxAlignItems,
  FlexBoxDirection,
  FlexBoxJustifyContent,
  Input,
  MessageStrip,
  ShellBar,
  ThemeProvider,
  Timeline,
  TimelineItem
} from '@ui5/webcomponents-react';
import React from 'react';
import './App.css';
import '@ui5/webcomponents/dist/Assets.js';
import '@ui5/webcomponents-react/dist/Assets.js';
import '@ui5/webcomponents-fiori/dist/Assets.js'; // Only if using the @ui5/webcomponents-fiori package
import '@ui5/webcomponents-icons/dist/Assets.js'; // Only if using the @ui5/webcomponents-icons package
import { setTheme } from "@ui5/webcomponents-base/dist/config/Theme.js";
setTheme("sap_fiori_3_dark");

const LIVES = [
  { "date": "1991/12/31", "title": "METALLICA / EUROPE / TESLA / THUNDER - FINAL COUNTDOWN 1991", "venue": "東京ドーム" },
  /* (以下、省略) */
];

class LiveTimeline extends React.Component {
  render() {
    const filteredLives = LIVES.filter((element) => {
      return (element.date + element.title + element.venue).toLocaleLowerCase().indexOf(
        this.props.filterText.toLowerCase()) > -1;
    });

    if (filteredLives.length > 0) {
      return (
        <Timeline className="Lives-timeline">
          {filteredLives.map((element, index) => {
            return (
              <TimelineItem
                key={index}
                icon="calendar"
                itemName={element.title}
                subtitleText={element.date}
              >
                <div>{element.venue}</div>
              </TimelineItem>
            );
          })}
        </Timeline>
      );
    } else {
      return (
        <MessageStrip className="Lives-timeline">No data found.</MessageStrip>
      );
    }
  }
}

class LiveToolbar extends React.Component {
  handleFilterTextChange = (e) => {
    this.props.onFilterTextChange(e.target.value);
  }

  render() {
    return (
      <ShellBar
        primaryTitle="Lives in My Life"
        logo={<img alt="SAPUI5 Logo" src="https://sap.github.io/ui5-webcomponents/assets/images/ui5.png" />}
        profile={<Avatar image="https://avatars0.githubusercontent.com/u/25473342?s=400&u=b399ebf80c62121616c0435bed3f3c39b4fc9c9b&v=4" />}
        searchField={
          <Input
            value={this.props.filterText}
            placeholder="Please input ..."
            onInput={this.handleFilterTextChange}
          />
        }
      />
    );
  }
}

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

  handleFilterTextChange = (filterText) => {
    this.setState({
      filterText: filterText
    });
  }

  render() {
    return (
      <ThemeProvider>
        <FlexBox
          style={{ width: '100%', height: '100%' }}
          direction={FlexBoxDirection.Column}
          justifyContent={FlexBoxJustifyContent.Center}
          alignItems={FlexBoxAlignItems.Center}
        >
          <LiveToolbar
            filterText={this.state.filterText}
            onFilterTextChange={this.handleFilterTextChange}
          />
          <LiveTimeline
            filterText={this.state.filterText}
          />
        </FlexBox>
      </ThemeProvider>
    );
  }
}

export default App;

さいごに

エンタープライズアプリケーションの世界では、どのような UI テーマ(デザイン)を採用すべきか悩みどころですが、少なくとも SAP は Fiori という答えを用意してくれています。2013 年にリリースされた Fiori は、単なるモダン Web フレームワークではなく、SAP の統一的・横断的な UX として進化を遂げてきました。「Fiori にしておけば間違いない」というのは、(エンタープライズアプリケーションの世界では)大きな魅力です。機会があれば、積極的に採用していきたいと思います。

ところで、最近では電子チケット化が進んでいますが、90 年代は当然ながら紙文化でしたので、手元に保管されていたチケットの半券と薄れゆく記憶を頼りにデータとして復元しました。半券はないが参戦した確信があるものについては、一緒に参戦した友人から半券のエビデンスを送ってもらうなど、この復元作業が一番大変だった気がします。一日も早くコロナが収束し、参戦ライブのリスト要素が追加できる日がくることを、心の底から祈っています。

参考リンク

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

ESP32のセキュアなプロトタイピング環境を作成した(ドキュメント編)

はじめに

独学しているIoTセキュリティについて、集大成の意味合いを込めて現在理想と考えるセキュアなプロトタイピング環境を構築しました。
デバイス、サーバー、クライアントアプリの要素技術の勉強を含めて備忘録として分散して記していきます。

経緯

ESP32のセキュアなプロトタイピング環境を作成した(概要編)の内容をユーザーにトライアルしてもらうためドキュメントの整備を進めた。

今回のプロジェクトのゴールは下記の通りとなる。

プロトタイプ段階から開発クライアント、サーバー、およびデバイス全体を保護
ESP32プラットフォームに対応し、以下の機能を有効にする

  • デバイスの起動制御
  • セキュアブート、フラッシュ暗号化
  • ハードウェアルートオブトラストによるTLS通信暗号化
  • OTA機能
  • Wifi接続管理

Arduino, ESP-IDFに対応し、ファームウェアをTLS通信で安全に更新可能
AWSおよびAzureへの安全なデバイス接続をサポート

プロジェクト名をrainbowtypeとした。
image.png

ランディングページの作成

プロジェクトを知ってもらい、試してもらう目的、また体系的なドキュメント化にランディングページを作ってみた。

rainbowtype
image.png

ベースはReactで作成し、Material-UIのチュートリアルを参考にした。
独自ドメインをValue-domain で取得し、ページはgithub pagesへデプロイした。

まず英語で書いてみたが、追って日本語との切り替えを実装してみたい。

デモの作成

コンセプトの提示にデモが有効と考え、以下のシナリオでデモを作成し、ランディングページに組み込んだ。

1, 通信経路の暗号化が必須なリモートシリアルコンソールのデモ
2, プライバシー保護が必要なWEB監視カメラデモ
3, 接続に証明書認証が必要なAWS接続

1, 通信経路の暗号化が必須なリモートシリアルコンソールのデモ

リモートシリアル制御では、リモートターゲットデバイスを完全に制御するために、通信パスを保護することが重要となる。
ユーザーが入力するログインパスワードなどのさまざまな認証情報がやり取りされることを想定する。

このようなアプリケーションでは、デバイスからサーバー、およびWebサイトへのすべてのルートで相互認証が必要であろう。

このシナリオでは、ブラウザコンソールから操作できるように、ターゲットデバイスとのシリアル通信をWebSocket経由でサーバーに転送するように構成されたESP32ファームウェアを作成する。

デバイスからWebSocketを受信して​​ルーティングする機能と、ブラウザー側に表示する機能の両方を提供するボイラープレートを作成した。
これはDockerfileに設定されているため、デバイスが指すことができる静的IPを備えたサーバーを用意するだけである。
GCEなど、無料で使える環境で試しやすい。

image.png

サーバー側にXterm.jsを使い、見た目も整ったリモートシリアルコンソールを実現した。

2, プライバシー保護が必要なWEB監視カメラデモ

監視カメラのアプリケーションでは、プライバシーを保護するために通信パスを保護することが重要である。

このようなアプリケーションでも、デバイスからサーバー、およびWebサイトへのすべてのルートで相互認証が必要であろう。

このシナリオでは、ターゲットデバイスのカメラ画像をWebSocket経由でサーバーに転送するように構成されたrainbowtypeのESP32を使用して、ブラウザーに複数のカメラ画像を表示させる。

この目的のために、デバイスからWebSocketを受信して​​ルーティングする機能と、ブラウザー側に表示する機能の両方を提供するボイラープレートを作成した。
これはDockerfileで設定されているため、デバイスが指すことができる静的IP持ったサーバーを用意するだけで実現可能。
上記同様簡単な例はGCEだが、画像転送は多くの帯域幅を使用するため、請求状況に注意する必要がある。できれば安いVPSなどを使った方がよいだろう。

image.png

Reactでページを構成し、Websocketで着信があったカメラを順番にIDを振り、複数表示させている。

3, 接続に証明書認証が必要なAWS接続

多くのデバイスを使用するIoTアプリケーションの場合、セキュリティ上の理由から、AWSへのデバイス登録は自動的に行う必要があるだろう。

rainbowtypeは、AWS IoTとの接続を安全に保つために、信頼のハードウェアルートとしてATECC608Aを使用できる。
このシナリオでは、AWS IoTジャストインタイム登録(JITR)を使用して、デバイスに署名した証明書を使用してデバイスをAWSIoTに自動的に接続を行う。

rainbowtypeで作成し、デバイスに署名を行ったSigner CA証明書を、AWSへ登録する手順の紹介、デバイス側でAWSへ接続するボイラープレートを作成した。

今後

rainbowtype向けに開発ボードをelecrowで作成したので、その紹介を予定。
詳細はぜひrainbowtypeを参照いただければと思う。

少しでも開発者のセキュリティ検討の参考になれれば幸いである。

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

おっさんReact使ってみる

もう頭がついていかないですが、さまざまな文献を見ながら、少なくとも欲しいウェブサイトはコーディングしたいです。素人の備忘録程度に書いてみます。

例題

まずは簡単そうなものを。シンプルにかけました。

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">
      ReactDOM.render(<h1>Hello, world!</h1>,document.getElementById('root'));
    </script>
  </body>
</html>

もういっちょ例題

サイトを参考にして変数を入れてみました。
https://ja.reactjs.org/docs/introducing-jsx.html

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">
      const name = 'Minato'
      const element = <h1>Hello, {name}</h1>
      ReactDOM.render(element,document.getElementById('root'));
    </script>
  </body>
</html>

これも無事。

例題3

関数も導入してみましたが、うまく動きました。

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">

      function formatName(user) {
        return user.firstName + ' ' + user.lastName;
      }

      const user = {
        firstName: 'Yu',
        lastName: 'Min'
      };

      const element = <h1>Hello, {formatName(user)}</h1>
      ReactDOM.render(element,document.getElementById('root'));
    </script>
  </body>
</html>

こちらも。

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">

      function getGreeting(user) {
        if (user) {
          return <h1>Hello, {formatName(user)}!</h1>;
        }
        return <h1>Hello, Stranger.</h1>;
      }

      function formatName(user) {
        return user.firstName + ' ' + user.lastName;
      }

      const user = {
        firstName: 'Yu',
        lastName: 'Min'
      };

      const element = getGreeting()
      ReactDOM.render(element,document.getElementById('root'));
    </script>
  </body>
</html>

描画の更新

これもチュートリアル通りやりました。
https://ja.reactjs.org/docs/rendering-elements.html

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">
      function tick() {
        const element = (
          <div>
            <h2>{new Date().toLocaleTimeString()}.</h2>
          </div>
        );
        ReactDOM.render(element, document.getElementById('root'));
      }
      setInterval(tick, 1000);
    </script>
  </body>
</html>

いい感じです。

関数コンポーネントのレンダー

関数コンポーネントという部品化を学びました。
https://ja.reactjs.org/docs/components-and-props.html

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">
      function Welcome(props) {
        return <h1>Hello, {props.name}</h1>;
      }
      const element = <Welcome name="Sara" />;
      ReactDOM.render(element,document.getElementById('root'));
    </script>
  </body>
</html>

これで、綺麗にレンダリングされました。コンポーネントの使い回しは、

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">
      function Welcome(props) {
        return <h1>Hello, {props.name}</h1>;
      }

      function App() {
        return (
          <div>
            <Welcome name="A" />
            <Welcome name="B" />
            <Welcome name="C" />
          </div>
        );
      }

      ReactDOM.render(<App />, document.getElementById('root'));
    </script>
  </body>
</html>

これもいけました。とりあえず今のところ大丈夫そうです。思ったよりも使いやすいです。

続いて、コンポーネントの抽出

コンポーネントを抽出して記述をコンパクトにみやすくするのをやってみます。

https://ja.reactjs.org/docs/components-and-props.html

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">

      function Comment(props) {
        return (
          <div className="Comment">
            <div className="UserInfo">
              <img className="Avatar"
                src={props.author.avatarUrl}
                alt={props.author.name}
              />
              <div className="UserInfo-name">
                {props.author.name}
              </div>
            </div>
            <div className="Comment-text">
              {props.text}
            </div>
            <div className="Comment-date">
              {props.date}
            </div>
          </div>
        );
      }

      const comment = {
        date: "2021/1/5",
        text: "hello",
        author: {
          avatarUrl: "test",
          name:"Yu min"
        }
      }

      ReactDOM.render(<Comment author={comment.author} text={comment.text} name={comment.name}/>, document.getElementById('root'));
    </script>
  </body>
</html>

json形式も書きやすいです。。。正直かなり混乱しますが、コンポーネントを抽出して記述をコンパクトにできました。。。

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/jsx">

      function Avatar(props) {
        return (
          <img className="Avatar"
            src={props.user.avatarUrl}
            alt={props.user.name}
          />
        );
      }

      function UserInfo(props) {
        return (
          <div className="UserInfo">
            <Avatar user={props.user} />
            <div className="UserInfo-name">
              {props.user.name}
            </div>
          </div>         
        );
      }

      function Comment(props) {
        return (
          <div className="Comment">
            <UserInfo user={props.author} />
            <div className="Comment-text">
              {props.text}
            </div>
            <div className="Comment-date">
              {props.date}
            </div>
          </div>
        );
      }

      const comment = {
        date: "2021/1/5",
        text: "hello",
        author: {
          avatarUrl: "test",
          name:"Yu min"
        }
      }

      ReactDOM.render(<Comment author={comment.author} text={comment.text} name={comment.name}/>, document.getElementById('root'));
    </script>
  </body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

create-react-appで作成したReactプロジェクトに、TypeScriptを導入する

すごい今更感ありますが、React の勉強も兼ねて、環境を1から作っていきました。
create-react-app で作成したプロジェクトに、TypeScriptを導入していきます。

やったこと

  • 必要なパッケージのインストール
  • create-react-app で作成された js ファイルを置換する
  • React の型定義をコンパイラに伝える

必要なパッケージのインストール

公式サイト に必要なパッケージが記載されています。
プロジェクト作成時にテンプレート使えば良かったじゃんとあとで気づきました

プロジェクトのルートディレクトリで下記コマンドを実行
公式では普通にインストールしていますが、ビルド時に必要なものと思うので、devDependencies でインストールします。

yarn add -D typescript @types/node @types/react @types/react-dom @types/jest

create-react-app で作成された js ファイル置換する。

  • レンダリングを行う js ファイル(jsxを使用しているファイル) -> tsx ファイル
    • 今回は src/App.jssrc/App.test.tsxsrc/index.jsが該当
  • ロジックのみ書かれているjsファイル -> ts ファイル
    • src/reportWebVitals.jssrc/setupTests.js
src/App.tsx
import logo from './logo.svg';
import './App.css';
- function App() {
+ // 返り値の型書いておく
+ function App(): JSX.Element {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
-          Edit <code>src/App.js</code> and save to reload.
+          Edit <code>src/App.tsx</code> and save to reload.
        </p>

src/reportWebVitals.ts
- const reportWebVitals = onPerfEntry => {
+ import { ReportHandler } from 'web-vitals';
+
+ const reportWebVitals = (onPerfEntry?: ReportHandler) => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(onPerfEntry);
    });
  }
};

export default reportWebVitals;

React の型定義をコンパイラに伝える

d.ts を作成します

src/react-app-env.d.ts
/// <reference types="react-scripts" />

これにて TypeScript の導入は完了です!

終わりに

GitHubのリポジトリにプルリクとして、やったことまとめてます。(こちら)
現在ならコマンド一つで TypeScript 込みの React プロジェクトを作ってくれますが、自身で書き換えを行ったことで、なんとなく理解度が上がった気がします。

これから実際に何か作ってみて、React の tips みたいなものをアウトプットしていきます:muscle:

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

React を Azure Static Web Appsでホスティングする開発・本番環境を作る

Azure Static Web Apps という SPA 用の Azure リソースがあります。React や Vue.js などを実に簡単にホスティングできます。
2020/12時点でまだプレビューで、機能的にはまだこれからなところがありますが、これから間違いなく主役級リソースとなるでしょう。
本記事では React を使った ローカル開発環境の構築と、Azureへのデプロイまでの手順を記載します。

事前準備

以下の準備が必要です。

GitHub にリポジトリを準備し、ローカルリポジトリと同期

まずは GitHub にリポジトリを作りましょう。myappという名前で私は作ります。publicかprivateかはどちらでもOKです。.gitignoreファイルは作らないでおきましょう。この後の React アプリ作成時に自動的に作成されるため、重複するからです。

create repository

Powershell、コマンドプロンプト、bashなどのターミナルからを立ち上げてください。これから GitHub に作った Repository をローカルに clone しますので、ソースを配置したい場所へ移動します。私はソースは常に C:\gitroot 配下に作ることにしていますので、そこに移動します。
clone コマンドは Repository 名のフォルダを自動的に作成しますので、C:\gitroot で下記コマンドを叩きます。<org_name>の箇所は、自分のアカウント名か、組織名(GitHub内の組織)に書き換えてください。

git clone https://github.com/<org_name>/myapp.git

自動作成したフォルダに移動してから、お作法として、最初のコミットには空のコミットを入れておきます。ついでに、もし branch 名が master なら main にリネームしておきます。(リネームはしなくてもいいのですが、最近の流れはmasterというブランチ名を使わないそうです)

cd myapp
git commit --allow-empty -m "first commit"
git branch -M main

無事に変更出来たら、push します。master を main に変更していますので、わざわざ origin へ HEAD を pushする、と指定しなければなりません。

git push origin HEAD

最初の push はこれでいいのですが、毎回 origin HEAD を指定するのは面倒です。今後のために up-stream 先の master を mainに変更しておきます。

git branch --unset-upstream
git branch --set-upstream-to origin/main main

これで以降の push は次のコマンドで簡単になります。

git push

Reactアプリを作成する

自分で1つずつ組み上げてもいいですし、create-react-appを使ってもいいので、Reactアプリを作ります。
create-react-appで作らない場合や既存のReactアプリの場合は、package.json の scripts に build コマンドを作って本番用にビルドした結果をどこかのフォルダに固めるようにしておいてください。後で構成する GitHub Actionsは "yarn run build" を叩く仕様になっているからです。
今回はmyappという名前のReactアプリをcreate-react-appを使ってTypeScriptで作ります。
注意がいるのは、yarn create コマンドもフォルダを自動作成しようとします。なので、このままだと1階層多くなってしまうので、一つ上の階層(C:\gitroot)に移動してからyarn create します。

cd ..\
yarn create react-app myapp --template typescript

動かしてみましょう。

cd myapp
yarn start

自動的にブラウザが立ち上がり、画面が表示されればOKです。

CTRL+Cを押下して、yarn を停止しましょう。今後はこのターミナルで React アプリを起動はしません。

Visual Studio Codeでフォルダを開く

yarn start したターミナルが残っている場合は、そこで次のコマンドを叩けばそのフォルダを VSCode で開くことができます。

code . -r

もしくは、エクスプローラーで右クリック→「codeで開く」とするか、 VSCode を起動してから「ファイル」→「フォルダーを開く」を選択してからgit cloneして自動作成されたフォルダを選びます。

以降の作業は VSCode のターミナルを使用します。「ターミナル」→「新しいターミナル」を選択します。

vscode terminal open

こんな画面が表示されるはずです。
image.png

powershell だったり、bash だったりと表示されるターミナルは環境によって様々のはずです。私の場合は Windows で Powershell を使用していきます。
ちなみに私の Powershell ターミナルにカラフルな情報(現在位置と Git 情報)が表示されているのは、別途そういうセットアップをしているからです。ご興味がある方はこちらをどうぞ。

Azure Functions アプリをローカルで作る

一番上の階層にapiフォルダを作ります。ここにAzure Functionsアプリを構成します。(ちなみにapiという名前じゃなくても本当は大丈夫です。後ほどAzure Static Web Apps をAzure上に新規作成するときにAzure Functionsの場所を指定するので、api以外の名前を付けた場合はそれを指定できるからです)

create api folder on the top

Azure Functions の作成はAzure Functions拡張機能を使います。Visual Studio Codeの「表示」メニューからコマンドパレットを開きます。
show command pallet

Azure Functions:Create New Projectsを一文字ずつ入力していくと選択肢が絞られていきます。選択します。

選択すると、情報入力をウィザード形式で求めてきます。最初は Azure Functions を作る場所です。apiフォルダを指定したいので、Broseを選択してから api フォルダを選択します。

select api folder

どの言語で実装するのかを選択します。今回はC#で作ります。

select lang

次はAzure Functions のトリガーを選択します。選択肢のドロップダウンが表示されるまで5秒ぐらいかかりますので少し待ちましょう。
HttpTriggerを選択します。

select httptrigger

最初に作成するメソッド名を選択します。APIのURLでもあります。デフォルトで名前を提案されますが、「GetData」変えます。
メソッド名は後から変更できます。

input function name

名前空間を決めます。これも後から変更可能です。今回はデフォルトのままにします。
input namespace

最後に、APIアクセス時の認証設定です。本番時には認証を入れるべきですが、今回は「認証なし」を意味するAnonymousを選びます。
select auth

Azure Functions が作成されるとエクスプローラーはこのようになります。

folder structure after azure functions created

エラーが大量に出ていると思いますが、パッケージをダウンロードしてないからです。気にせずデバッグ実行してみましょう。(デバッグ実行のプロセスの最中にパッケージもダウンロードします)
左のデバッグ実行メニューをクリックしてから、実行ボタンをクリックします。

image.png

もしかしたら、この時に Azure Functions Core Toolsのインストールを求めるポップアップが上がってくるかもしれません。その場合はインストールしてください。

install azure functions core tools

無事に起動すると、URLがターミナルに表示されます。ブラウザで開きましょう。(CTRL+クリックで開きます)
image.png

エラーなくメッセージが表示されればOKです。

view azure functions in browser

この後すぐにソースを修正するので、デバッグ実行は停止しておきます。

image.png

JSONを返すようにAPIを実装する

ダミーのJSONを返すように実装します。

namespace Company.Function
{
    public static class GetData
    {
        [FunctionName("GetData")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {

            var results = new Data[] {
                new Data { Id = 1, Name = "Sato" },
                new Data { Id = 2, Name = "Suzuki" }
             };

            return new JsonResult(results);
        }
    }

    public class Data {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

asyncメソッドなのにawaitがないから同期的に実行しますよ、という警告が出ると思いますが無視します。今回そこはどうでもよいところです。
忘れずにデバッグ実行を開始しておきましょう。

APIを呼び出すように React を実装する

外部へのhttpアクセスにはjavascriptのfetchメソッドを使うことができますが、httpステータスエラーを補足しない、という特徴がありますので私は使いません。いつもaxiosを使いますので今回もそうします。VSCode のターミナルで次のコマンドを叩きます。

yarn add axios

App.tsxファイルを以下のように修正します。Azure Functions からデータを取ってきて、それを表示しているだけです。

App.tsx
import React from 'react';
import axios, { AxiosResponse } from 'axios';

function App() {

  const [response, setResponse] = React.useState<AxiosResponse>();

  React.useEffect(() => {
    axios.get('http://localhost:7071/api/GetData')
    .then(response => {
      // handle success
      setResponse(response);
      console.log(response);
    })
    .catch(error => {
      // handle error
      console.log(error);
    })
    .then(() => {
      // always executed
    });
    }, []);

  return (
    <React.Fragment>
      {response?.data.map((elm:any, index:number) => {
        return (
          <div key={index}>id: {elm.id}, name: {elm.name}</div>
        )
      })}
    </React.Fragment>
  );
}

export default App;

yarn start で実行してみましょう。(デバッグ実行が表示されていてコマンドが叩けませんか?その場合はターミナル右側に表示されている「2:タスク-host start」をクリックしてみてください。「1:powershell」というのがありませんか?それを選択します)

switch terminal

・・・ブラウザには何も表示されないですね。開発者ツールから、Consoleを開いてみます。
error.png

CORS policyにブロックされた、とメッセージが出ています。

ローカル開発用のCORSを設定する

デフォルトの状態では Reactは http://localhost:3000 で Azure Functions は http://localhost:7071 です。ホストは同じですがポートが異なるため、CORS(Cross-Origin Resource Sharing)ではオリジンが異なると判定されてしまい、APIアクセスの結果がエラーとなります。これを解決する方法は二つあって、一つはアクセスされる側であるAzure Functionsで明示的にReact側のオリジンを許可するように設定するか、React側で外部アクセスする時にProxy機能を使ってアクセスするホストを書き換えるかのどちらかになります。

Azure Functions でReact側のオリジンを設定する方法ですが、なぜか SignalR のdocsに設定方法が記載されています。
https://docs.microsoft.com/ja-jp/azure/azure-signalr/signalr-concept-serverless-development-config#enabling-cors

しかし、この方法は何かしら特別な理由がない限りは採用しないでしょう。APIアクセス時のURLを何かしらの方法(環境ファイルなど)を使って切り替えなければいけないですし、デプロイ後の本番環境(Azure Static Web)ではReactアプリもAPIサーバーと同じオリジンになるように構成されるため、余計なCORS設定は削除しておく必要があります。積極的に採用する理由がありません。

通常はReactアプリ側でProxy機能を利用するでしょう。create-react-appで作成した場合はpackage.jsonの一番上の階層(nameやversion等と同じ階層)に次のように実装します。

  "proxy": "http://localhost:7071"

create-react-appを使用していない場合や既存のReactアプリの場合は使用しているWebサーバーの仕様次第ですので、ご自身で設定をお願いします。(webpack-dev-serverを使っている方が多いと思います。その場合はwebpack.config.jsのdevServerセクションにproxy設定をすることができます。公式の解説

Proxy機能を使う場合、APIアクセス時のURLを書き換える必要があります。

    axios.get('http://localhost:7071/api/GetData') // 修正前
    axios.get('/api/GetData') // 修正後

proxy機能を有効にするために、Reactを実行中の場合はCTRL+C押下で一度停止して、再度yarn startで開始してください。
ブラウザで http://localhost:3000 にアクセスします。このようになれば成功です。

image.png

これでローカルの開発環境作成は完成です。Azure Functions のソースにブレークポイントを置いてからブラウザを再読み込みすると、ブレークポイントで止まります。確認してください。

image.png

Azure Static Web Appsを作成する

Azureのポータル画面を開き、リソースの作成をクリックします。

create new

検索ボックスに static web apps を入力していくと、Static Web Appが絞り込みで表示されてきますので、選択します。

search static web app

Static Web App の作成画面が表示されます。作成をクリックします。

情報を入力します。

  • リソースグループは新規で作成します。(既存のグループを使ってもいいですが)今回はstaticwebtestという名前にしました。
  • 静的 Webアプリの名前を適当に付けます。今回はmystaticwebにしました。
  • 地域は選択します。今回は East Asiaにしました。2020/12現在、日本リージョンはまだ使用できません。

image.png

「GitHubアカウントでサインイン」をクリックすると、画面がポップアップします。「Authorize Azure-App-Service-Static-Web-Apps」をクリックします。

パスワードを入力し、「Confirm password」をクリックします。

confirm github password

GitHubアカウントから、デプロイに使用するリポジトリとブランチ(分岐)を選択します。今回はmainブランチを選択します。
「ビルドの詳細」では、まずビルドのプロセットを選択します。今回はReactを選びます。すると、自動的に

  • アプリの場所
  • APIの場所
  • アプリの成果物の場所

の3つに値が入力されます。ここでいうアプリ、とはReactアプリのことを指します。次のように自動的に値が入ります。

アプリの場所 /
APIの場所 api
アプリの成果物の場所 build

confirm react app settings

左下の「確認及び作成」をクリックします。

confirm to create static web app

作成をクリックします。デプロイが開始されます。デプロイが完了すると「リソースへ移動」が表示されますのでクリックします。

static web app created

GitHubへソースを push して deploy する

いよいよ Azure Static Web App にデプロイするわけですが、それにはソースを GitHub へ Push することが相当します。
最初に GitHub のリポジトリに空のコミットを行いました。以降は何もコミットしていませんが、実はさきほどAzure Static Web App を作成したときにファイルが1つ追加されてコミットされています。そのため、今の時点ではリモートリポジトリ(GitHub のリポジトリ)の方が先に進んでしまっているので、まずはリモートリポジトリと同期することから始めます。
React アプリと Azure Functions のデバッグ実行を停止したら、次のコマンドを叩きます。(私は普段 git コマンドを使いますのでコマンドを紹介しますが、せっかくVSCodeを使っているのでGUIを使って作業しても構いません)

git pull

成功したら、すべてのソースをコミットしてから push します。

git add .
git commit -m "first deploy"
git push

git push に成功すると、自動的にGitHub Actionsはソースのビルドを行い、デプロイ用にZipファイルを作成し、アップロードし、デプロイ先がそのZipを展開します。そんなGitHub Actionsでのデプロイ状況を見る場合は GitHub の Actions を開いて、履歴を参照します。
image.png

image.png

「Build And Deploy」をクリックして開くと、デプロイ処理の詳細を見ることができます。
image.png

Nodeのインストールから始まり、yarn install でパッケージ復元をして・・・という一連の流れを見ることできます。一度は見ておいたほういいでしょう。
image.png

確認

Azure Static Web Appの概要を開くと、右側にURLが表示されています。クリックします。

image.png

Azure Static Web AppでReactアプリがホスティングされました。
image.png

途中のセットアップで気づかれたかと思いますが、APIの場所は別に api フォルダじゃなくてもいいし、Reactアプリをビルドした結果をまとめる先がbuildフォルダでなくても問題ありません。Azure Static Web App作成時に指定した場所を変更したい場合は GitHub Actions のワークフローを修正します。

image.png

いかがだったでしょうか。実は一番ややこしいのは git の操作なのではないか、と思います。慣れていないと難しいですね。
まだ日本リージョンではホスティングすることができませんが、近い将来には日本リージョンでも動かせる日がきっと来ると思います。

是非、React アプリを Azure Static Web App で動かしてみてください。とても便利ですよ。

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