20201129のReactに関する記事は22件です。

【React】 AWSにデプロイする

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

AWS の S3 にデプロイする

S3 の静的サイトホスティング機能を使ってデプロイします。

準備

サンプルコードを clone してきます。
中身は redux-toolkit の公式サイトで紹介されているサンプルのTODOアプリです。

$ git clone https://github.com/reduxjs/rtk-convert-todos-example.git

アプリのディレクトリに移動し yarn build を実行します。

$ cd rtk-convert-todos-example
rtk-convert-todos-example $ yarn build

アプリの中に新しく build というディレクトリができたと思います。
確認してみましょう。

rtk-convert-todos-example $ ls
README.md   jsconfig.json   package.json    src
build       node_modules    public      yarn.lock

build ディレクトリができていますね。
build ディレクトリの中身も見てみましょう。

rtk-convert-todos-example $ ls build
asset-manifest.json
favicon.ico
index.html
logo192.png
logo512.png
manifest.json
precache-manifest.4cc66b9b5865646273439dd929f332c4.js
robots.txt
service-worker.js
static

色々なファイルができていますね。

これらをS3にアップロードします。

デプロイしていく

まずS3バケットを作りましょう。
「バケットを作成」と言うボタンをクリックしてください。

スクリーンショット 2020-11-29 23.31.40.png

バケット名を入力してください。
他の設定などは全てデフォルトで大丈夫です。

スクリーンショット 2020-11-29 23.26.22.png

バケットの作成ができたらS3のバケット一覧画面から先ほど作ったバケットを選択するとバケットの編集画面に遷移します。

スクリーンショット 2020-11-29 23.36.19.png

この中でプロバティというタブをクリックすると静的ウェブサイトホスティングという項目が出てきます。
この項目の設定を変更したいので編集ボタンをクリックします。

スクリーンショット 2020-11-29 23.39.46.png

  • 静的webサイトホスティングを有効に
  • インデックスドキュメントは index.htmlと入力してください。
  • エラードキュメントは error.htmlと入力してください。
  • 他はデフォルトで大丈夫です。

設定ができたらバケットの編集画面からオブジェクトタブをクリックしてファイルをアップロードします。
build ディレクトリの中身をそのままドラッグアンドドロップで大丈夫です。

このままだとアクセスできないので設定をを変更します。
バケットの編集画面からアクセス許可のタブをクリックしてください。

バケットポリシーを設定します。。
arn:aws:s3:::awsexamplebucket1の部分を自分の ARN に変えてそのままコピペして貼り付けるだけで大丈夫です。

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"PublicRead",
      "Effect":"Allow",
      "Principal": "*",
      "Action":["s3:GetObject","s3:GetObjectVersion"],
      "Resource":["arn:aws:s3:::awsexamplebucket1/*"]
    }
  ]
}

ブロックパブリックアクセスは上の二つだけチェックして他はチェックを外してください。
スクリーンショット 2020-11-29 23.46.19.png

プロパティの静的ウェブサイトホスティングの項目に記載のあるURLにアクセスし、以下のようなTODOアプリが表示されたら成功です。

スクリーンショット 2020-11-29 23.53.00.png

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

ReduxとContextの再レンダリングの違いをサンプルアプリケーションと共に確認する

VISITS Technology株式会社( https://visits.world/ )の2020年アドベントカレンダー1発目を努めさせていただきますpipopotamsauです。

https://qiita.com/advent-calendar/2020/visits

業務ではフロントエンドエンジニアとしてReactを用いたアプリケーションを開発していますので、React関連について書きたいと思います。

1. ReduxとContextについて

Reactアプリケーションでデータ、特にグローバルなデータをストアしたい場合Reduxを用いるか、はたまたContextを使うかという議論があるかと思います(最近はHTTP Requestをキャッシュするライブラリなどが出てきて色々選択肢が増えましたが1、本記事の本筋とは関係ないため言及しません)。

どちらを使うべきであるかは開発の前提条件にもよりますので一概には言えませんが、論点の中には「パフォーマンス」に関するものがあります。

2. パフォーマンスについての議論

おそらく「Redux vs Context」でググると、「頻繁にグローバルストアを更新するんならReduxの方がパフォーマンスいいよ」的なものが出てくると思います。これは何故でしょうか?

これは両者の状態が更新されたときに発生する再レンダリングのスコープが違うからです。

Redux: 変更された値をサブスクライブしているコンポーネントのみ再レンダリングが走る
Context: 変更されたContextのProviderの子コンポーネント以下全てに再レンダリングが走る

このような違いがあるため「頻繁にグローバルストアを更新するんならReduxの方がパフォーマンスいいよ」という話が出てきます。

3. ReduxとContextの再レンダリングの違いをサンプルアプリケーションで確認してみる

さて、ここからが本番です。
上記でどのような再レンダリングの違いがあるかを言葉で書きましたが、今度は実際にサンプルアプリケーションで確認していきましょう。

※ 本記事のサンプルコードのレポジトリはこちらです

前提として、ReduxとContextそれぞれのサンプルアプリケーションで以下のようなグローバルステートを保持するとします。

{
  user: { id: 1, name: 'test name' },
  posts: [{ id: 1, title: 'test post' }]
}

上記のステートを更新したときに、どのような再レンダリングの違いがあるかをみていきます。

Reduxの再レンダリング

前述したグローバルステートを持った、以下のようなReduxを用いたサンプルアプリケーションを作成しました。

redux-sample-application.png

コードとしてはこのようになります。

import { Provider, useSelector, useDispatch } from 'react-redux';
import { store } from '../reduxStore';
import classes from '../styles/page.module.css';

function UserInfo () {
  console.log('UserInfo is updated!');
  const user = useSelector(state => state.user);
  const dispatch = useDispatch();

  return (
    <div className={classes.userContainer}>
      <p>{user.name}</p>
      <button
        onClick={() => {
          dispatch({ type: 'UPDATE_NAME', payload: 'updated name' });
        }}
      >
        update user
      </button>
    </div>
  )
}

function PostList () {
  console.log('PostList is updated!');
  const posts = useSelector(state => state.posts);
  const dispatch = useDispatch();

  return (
    <div className={classes.postsContainer}>
      <ul>
        {
          posts.map(post => <li key={post.id}>{post.title}</li>)
        }
      </ul>
      <button
        onClick={() =>  {
          dispatch(
            { type: 'ADD_POST', payload: { id: posts.length + 1, title: 'added post' }}
          )
        }}
      >
        add post
      </button>
    </div>
  )
}

export default function ReduxExample () {
  return (
    <Provider store={store}>
      <h1>Redux</h1>
      <UserInfo />
      <PostList />
    </Provider>
  )
}

親コンポーネントとしてReduxExampleコンポーネントがあり、ReduxのstoreをProvideしています。
また、子コンポーネントとしてuserを参照しているUserInfoコンポーネントとpostsを参照しているPostListコンポーネントがあります。

このような構成でuserpostsの値を更新/追加するとどうなるでしょうか?

redux-demo.gif

userを更新した際はUserInfoコンポーネントのみ再レンダリングされ、postsを更新した時はPostListコンポーネントのみ再レンダリングされます。

Contextの再レンダリング

今度はContextについてみていきます、以下のようなContextを用いたサンプルアプリケーションを作成しました(Reduxのものと見た目は一緒です)。

context-sample-application.png

コードとしてはこのようになります。

import { useContext, useReducer } from 'react';
import { initialState, reducer, Context } from '../context';
import classes from '../styles/page.module.css';

function UserInfo () {
  console.log('UserInfo is updated!');
  const { user, dispatch } = useContext(Context);

  return (
    <div className={classes.userContainer}>
      <p>{user.name}</p>
      <button
        onClick={() => dispatch({ type: 'UPDATE_NAME', payload: 'updated name' })}
      >
        update user
      </button>
    </div>
  )
}

function PostList () {
  console.log('PostList is updated!');
  const { posts, dispatch } = useContext(Context);

  return (
    <div className={classes.postsContainer}>
      <ul>
        {
          posts.map(post => <li key={post.id}>{post.title}</li>)
        }
      </ul>
      <button
        onClick={() =>  {
          dispatch(
            { type: 'ADD_POST', payload: { id: posts.length + 1, title: 'added post' }}
          )
        }}
      >
        add post
      </button>
    </div>
  )
}

export default function ContextExample () {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <Context.Provider value={{ user: state.user, posts: state.posts, dispatch }}>
      <h1>Context</h1>
      <UserInfo />
      <PostList />
    </Context.Provider>
  )
}

親コンポーネントとしてContextExampleコンポーネントがあり、ContextのステートをProvideしています。
また、子コンポーネントとしてuserを参照しているUserInfoコンポーネントとpostsを参照しているPostListコンポーネントがあります。

Reduxのサンプルアプリケーションと同様に、こちらもuserpostsの値を更新/追加してみましょう。

context-demo.gif

Reduxとは違い、userpostsのうち片方を更新すると、UserInfoコンポーネントとPostListコンポーネントの両方が再レンダリングされていることがわかります。

userを更新したのにPostListコンポーネントが再レンダリングされてしまう、もしくはその逆のパターンの抑制策としてはReact.memoを用いmemo化を行う方法があります。本記事では詳しく説明しませんが、もし興味がある方は以下を確認してみてください。

https://ja.reactjs.org/docs/react-api.html#reactmemo

4. 終わりに

以上のように今回はReduxとContextの再レンダリングの違いについて、文字だけでなく実際にサンプルアプリケーションを用いて確認していきました。

本記事のサンプルコードは以下になります。コード全体がみたいという方はこちらを参照してください。
https://github.com/pipopotamasu/redux-context-comparison

明日は弊社のEM兼バックエンドエンジニアであるhamがお送りします。

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

app制作 vol.1初期設定

はじめに

今回、会社の同期と一緒にアプリの開発をすることになったので、その作業記録として記事を書くことにした。
自分の担当はフロント
Reactを使って作成していく予定

auto deployの設定

作成したアプリは、メンバーが立ててくれたAWSサーバで公開する予定、コードの管理はGitを使う。
ローカルマシンで作成したコードをGitにpushし、本番サーバにdeployするという流れ。
Gitにpushした変更を本番サーバに自動でdeployするための設定についてここで書く。
GitHub上での設定項目は、メンバがやってくれたのでこちら(後日追加)を参考に。
Jenkinsを用いているので、本番サーバのjenkinsにログインして、設定をいじっていく。

コードがpush/mergeされると、GitHubから本番サーバのJenkinsに通知がとび、設定されている処理が実行される。
まずは、Gitから最新のコードをpullする処理を記述する。
設定ページの’ビルド’の項目を編集する

ビルド手順の追加 から シェルの実行を選択し、
image.png

gitのリポジトリがあるユーザへのログイン、git pullの実行を行うコマンドを記述する

image.png

-t -t オプションはdeploy実行時に、Pseudo-terminal will not be allocated because stdin is not a terminal.エラーが発生する場合があるため、その対策としてつけている

雛形の作成

まずはdeployの確認として、 reactAppの雛形を作成し、それをpushしてみる
npx create-react-app ${プロジェクト名}を実行し、プロジェクトフォルダと雛形を作成する
ローカルで関連ライブラリのインストール、実行の確認をしたら、gitにpushする

deployの確認

pushをした後、本番サーバのJenkinsにログインし、ビルドが実行できているか確認する。
このとき自分が遭遇したエラーは、前述のビルド時のSSHログイン時に標準出力がないというエラーと、SSH先がknown_hostsに登録されておらず発生した、Host key verification failed.エラーの2つ。
後者のエラーは本番サーバにSSHクライアントでログインした後、jenkinsにユーザスイッチし、コンソール上で一度SSHを実行して、接続先を登録すれば解決する。

後書き

今回は、auto deployの設定を中心にまとめた、実際は、ビルドのたびにライブラリのインストールの確認など、追加しなければならない処理はあるが、追々追加していく予定。

参考文献

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

【Next.js】getServerSidePropsでクエリパラメータを取得する方法

前回、Next.jsを使ったクライアントサイドでのクエリパラメータを取得する方法を解説しました。

Next.jsでクエリパラメータを遷移先に渡すにはどうすればいいのか? - Qiita

前回使用したuseRouterフックはクライアントサイドで動きますが、サーバーサイドではエラーになります。そのためサーバーサイドでクエリパラメータを取得するには別の方法が必要です。

なので、今回はサーバーサイドでクエリパラメータを取得する方法を解説します。

サーバーサイドレンダリングでクエリパラメータを取得する方法

まずはサーバーサイドでレンダリングするためgetServerSidePropsの定義が必要です。その際の注意点としてはexportayncキーワードをつけることです。これにより、リクエスト毎にこのページを事前にレンダリングします。

そして、このgetServerSidePropsの中でuseRouterを使おうとするエラーが発生します。

WS000002.JPG

これはサーバーサイドレンダリングではuseRouterを使えないためです。クライアントサイドで使う前提になっています。

ここで使えるのがgetServerSidePropsの引数です。通常contextという名前で設定しますが、どんな引数名でも使えるはずです。

このcontextの中には

  • req:HTTPリクエストオブジェクト
  • res:HTTPレスポンスオブジェクト
  • query:クエリパラメータ

が入っています。この中のqueryの中にクエリパラメータの値が格納されているので、この値を使えばサーバーサイドレンダリングでも使うことができます。

たとえば、次のページを作る際には2つのページが必要になります。

Nextjsのパラメータ引き渡し(サーバーサイドレンダリング).gif

  1. クエリパラメータを送るページ
  2. クエリパラメータを受け取るページ

クエリパラメータを受け取るページでは、受け取った値でWebAPIを呼び出してGoogleブックの検索結果を表示しています。

クエリパラメータを送るページ

index.jsx
import { useRouter } from 'next/router';
import {useState} from 'react';

export default function Index() {
  const router = useRouter();                //ルーターの取得
  const [keyword, setKeyword] = useState();  //検索キーワード

  // ボタンをクリックしたときの処理
  const clickButton = () => {
    //未入力の時
    if (!keyword) {
      return;
    }

    router.push({
        pathname:"/result",       //URL
        query: {keyword :keyword} //検索クエリ
      });
  }

  return (
    <div style={{textAlign: "center", marginTop: "50px"}}>
      {/* 入力項目 */}
      <input 
        type="text" 
        value={keyword}
        onChange={(e) => setKeyword(e.target.value)} /*変更時keywordに値をセット*/
      />

      {/* ボタン */}
      <button 
        onClick={clickButton}
        disabled={!keyword}>    {/*入力項目が未入力の場合、非活性*/}
        検索
      </button>
    </div>
  )
}

クエリパラメータを送信するページではuseRouterフックを使って画面遷移とクエリパラメータの設定を行っています。router.pushメソッドの引数にURLとクエリパラメータをセットしています。

  • pathname:URL
  • query:クエリパラメータ

注意点としてはクエリパラメータは連想配列で設定します。複数セットする可能性があるためです。

クエリパラメータを受け取るページ

//サーバーサイドレンダリング
export async function getServerSideProps(context) {
  //検索キーワード
  const keyword = context.query.keyword;

  //Googleブックで検索
  const response = await fetch (encodeURI(`https://www.googleapis.com/books/v1/volumes?q=${keyword}`));

  return {
    props: {
      bookList: await response.json(),  //Googleブックの戻り値
      keyword: keyword,                 //検索キーワード
    }
  }
}

export default function Result(props) {

  //Googleブックの情報を取得
  const bookList = props.bookList.items.map(item => item.volumeInfo).map(((item) => {
    return {
      title : item.title,                             //タイトル
      url   : item.infoLink,                          //URL
    }
  }));

  return (
    <div style={{marginLeft: "50px", marginTop: "50px"}}>
      <h1>検索キーワード:{props.keyword}</h1>

      {/* 検索データの表示 */}
      <ul>
        {bookList.map((item) =>
          <li>
            <a href={item.url}>{item.title}</a>
          </li>
        )}
      </ul>
    </div>
  )
}

一方、遷移先ページではgetServerSidePropsメソッド内でクエリパラメータを取得してgoogleブックで検索した結果をリストで表示しています。

クエリパラメータはcontext.query.keywordで取得しています。keywordの部分は遷移元で設定した名前です。

後は、getServerSidePropsの戻り値のpropsにクライアントサイドに渡したい値をセットして表示するだけです。クライアント側でbookList変数で取得した検索結果を加工して、map関数を使ってリスト表示しています。

まとめ

  • router.pushpathnameに遷移先URL、queryにクエリパラメータをセットする
  • getServerSidePropsを使うことでサーバーサイドレンダリングを利用できる
  • 引数のcontext.query内に遷移元から送られたクエリパラメータが取得できる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React(next.js), TypeScript, python, Azure Functions, GASでアプリを作ってみた

前提

・開発経験1年半程度のエンジニアです
・普段はPHP(laravel)のバックエンド開発がメインです
・javaScript(jQuery)も実務で使います(たまに)
・vue(next)も少し学習したことはあります
・なのでフロントエンドに関しては未経験に毛が生えた程度の知識です
・フロントエンドフレームワークの勉強しようと思い、React, TypeScriptで簡単なアプリを作りました

この記事では作成したアプリと使用技術、その技術の勉強方法を紹介しますが、各技術の詳細、ソースコードの記述は割愛しておりますのでご了承ください。

成果物

https://news-app-ruby.vercel.app
*css等が整えられていないですが、「done is better than perfect」の精神で公開してます笑
おいおい直していこうかと思います
image.png

機能

・主要ニュースサイトの記事タイトル、記事URLをまとめたアプリ
・NewYorkTimes、The GuardianはAPIを提供しているのでAPIでニュースデータを取得
・上記以外のニュースサイトはAPIを提供していないので、各サイトのTOPページからスクレイピングでTOP記事データ(記事タイトル、記事URL)を取得
・NewYorkTimes、The Guardian、BBC NEWSの原文が英語のニュースはタイトルをマウスオーバーすると日本語翻訳される(GoogleAppsScript使用)
・どのニュースページも構造は同じなので、Reactのコンポーネントを使いまわしています(開発がすごい楽でした)
・UI/UXはまるでなっていませんが、割と便利なアプリで自分で日常的な情報収集用途として使っています

使用技術とその目的

1.React(Next.js) + TypeScript
APIデータの取得、SSG(static site generation)でページ描画

2.Python
ニュースデータを各サイトからscrapingで取得。JSONデータに変換してReact側に渡す

3.Azure Functions
上記pythonファイルのAPIサーバーとして使用

4.GoogleAppsScript(GAS)
英語→日本語に翻訳する際使用。JSONデータに変換してReact側に渡す

5.Vercel
上記アプリのデプロイ先

勉強方法と所感

1. TypeScript
【世界で7万人が受講】Understanding TypeScript - 2020年最新版というUdemyの教材をメインに学習しました。かなりボリュームがありますが、包括的にTypeScriptを学べるのでおすすめです。
・javascriptに関しても、今までは実務で必要にかられたときにググりながら局所的に使用していたのみなので、今回包括的に学べてすごくためになりました。またES2015についても学べたのでこれもためになりました
・javascript知らない人でも問題なく理解できる教材かと思います。

2.React
・これも下記のUdemy教材をメインで学習しました。
1:【はむ式】フロントエンドエンジニアのための React ・ Redux アプリケーション開発入門
2:最短・最速で作る Youtube クローンアプリ React・React Hooks編
・2つとも完走はしておりません。1の前半でReactの概要を学習しつつ、2のアプリ制作の過程で知識の定着を図りました。
・1のReduxの箇所は難しかったので今回は飛ばしました(笑)。(おいおい学び直します。多分。。。)
・代わりにHooksを、公式サイトで少し学習しました。Hooksの方がとっつきやすかったです
・reduxが「Reactは難しい」という考えの一つの遠因のような気が個人的にします

3.Next.js, Vercel
公式サイトのチュートリアルがわかりやすかったです。半日もあれば1
周できると思います。(next.jsを使って簡単なブログアプリを作る)
・vercelへのデプロイ方法も上記のチュートリアルで学べます
・複雑な設定なしでgithubにpushするだけで自動的にvercelにデプロイできるので、革命的に便利に感じました
・個人利用の場合は費用等はかからないそうです
・TypeScriptの導入方法も公式のドキュメント通りにやれば簡単にできました

4.Python
・実はPythonもほぼ初めて触りましたが、BeautifulSoupの公式ドキュメントみながらスクレイピング実装しただけなので、腰を据えて学習はしてないです
・PHPより簡潔にかけるなと少し感動しました。人気言語であるのは頷けます
・ただPythonは公式ドキュメントが読みづらいと思いました

5.Azure Functions
・スクレイピングをサーバレスで動かしたかったので利用しました
・AWS lamdaでもよかったかもしれませんが、Azureは無料枠があったのと、VSCODEとの連携が楽と聞いていたので今回はAzure Functionsを利用しました
Azure Functions の Python 開発者向けガイドという公式のチュートリアルで学習しました
・VSCODEにextencion入れればVSCODE内で開発、テスト、デプロイが簡単にできるので、便利でした。このやり方も公式ドキュメントがわかりやすかったです

最後に

以上、簡単なアプリ開発を通して、React,TypeScript,next.js,Azureの触りの部分を学習しました。
React,Next.jsは公式ドキュメントが充実していたので初心者でも学習しやすかったです。

ちなみに上記技術スタックの学習に約2〜3週間(仕事終わりの1、2時間、休日2,3時間)費やし、アプリ自体は概ね3日間程で作成しました。

Reactは触っていて単純に楽しかったです。
下記の記事にもあるように、HTML側で、JS/TSを制御するより、JSXでJS/TSでHTML側を制御する方が自分にはとっつきやすかったです。
参照:JSXが実はベターな解だったのではないか?
現に今の職場では巨大なHTML文の中に所狭しとjqueryの制御が埋め込まれているのでかなりコードの可読性が悪くなっております。

スタートアップ等を中心にバックエンド、フロントエンドの垣根が低くなってきているようなので(ベンチャー、スタートアップの面接で実際に何度か聞いたことがあります)、折に触れてフロントエンドの学習もしていきたいと思います。

ただ、次の開発のアイディアが浮かびません。。。

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

初心者がReact(next.js), TypeScript, python, Azure Functions, GASでアプリを作ってみた

前提

・開発経験1年半程度のエンジニアです
・普段はPHP(laravel)のバックエンド開発がメインです
・javaScript(jQuery)も実務で使います(たまに)
・vue(next)も少し学習したことはあります
・なのでフロントエンドに関しては未経験に毛が生えた程度の知識です
・フロントエンドフレームワークの勉強しようと思い、React, TypeScriptで簡単なアプリを作りました

この記事では作成したアプリと使用技術、その技術の勉強方法を紹介しますが、各技術の詳細、ソースコードの記述は割愛しておりますのでご了承ください。

成果物

https://news-app-ruby.vercel.app
*css等が整えられていないですが、「done is better than perfect」の精神で公開してます笑
おいおい直していこうかと思います
image.png

機能

・主要ニュースサイトの記事タイトル、記事URLをまとめたアプリ
・NewYorkTimes、The GuardianはAPIを提供しているのでAPIでニュースデータを取得
・上記以外のニュースサイトはAPIを提供していないので、各サイトのTOPページからスクレイピングでTOP記事データ(記事タイトル、記事URL)を取得
・NewYorkTimes、The Guardian、BBC NEWSの原文が英語のニュースはタイトルをマウスオーバーすると日本語翻訳される(GoogleAppsScript使用)
・どのニュースページも構造は同じなので、Reactのコンポーネントを使いまわしています(開発がすごい楽でした)
・UI/UXはまるでなっていませんが、割と便利なアプリで自分で日常的な情報収集用途として使っています

使用技術とその目的

1.React(Next.js) + TypeScript
APIデータの取得、SSG(static site generation)でページ描画

2.Python
ニュースデータを各サイトからscrapingで取得。JSONデータに変換してReact側に渡す

3.Azure Functions
上記pythonファイルのAPIサーバーとして使用

4.GoogleAppsScript(GAS)
英語→日本語に翻訳する際使用。JSONデータに変換してReact側に渡す

5.Vercel
上記アプリのデプロイ先

勉強方法と所感

1. TypeScript
【世界で7万人が受講】Understanding TypeScript - 2020年最新版というUdemyの教材をメインに学習しました。かなりボリュームがありますが、包括的にTypeScriptを学べるのでおすすめです。
・javascriptに関しても、今までは実務で必要にかられたときにググりながら局所的に使用していたのみなので、今回包括的に学べてすごくためになりました。またES2015についても学べたのでこれもためになりました
・javascript知らない人でも問題なく理解できる教材かと思います。

2.React
・これも下記のUdemy教材をメインで学習しました。
1:【はむ式】フロントエンドエンジニアのための React ・ Redux アプリケーション開発入門
2:最短・最速で作る Youtube クローンアプリ React・React Hooks編
・2つとも完走はしておりません。1の前半でReactの概要を学習しつつ、2のアプリ制作の過程で知識の定着を図りました。
・1のReduxの箇所は難しかったので今回は飛ばしました(笑)。(おいおい学び直します。多分。。。)
・代わりにHooksを、公式サイトで少し学習しました。Hooksの方がとっつきやすかったです
・reduxが「Reactは難しい」という考えの一つの遠因のような気が個人的にします

3.Next.js, Vercel
公式サイトのチュートリアルがわかりやすかったです。半日もあれば1
周できると思います。(next.jsを使って簡単なブログアプリを作る)
・vercelへのデプロイ方法も上記のチュートリアルで学べます
・複雑な設定なしでgithubにpushするだけで自動的にvercelにデプロイできるので、革命的に便利に感じました
・個人利用の場合は費用等はかからないそうです
・TypeScriptの導入方法も公式のドキュメント通りにやれば簡単にできました

4.Python
・実はPythonもほぼ初めて触りましたが、BeautifulSoupの公式ドキュメントみながらスクレイピング実装しただけなので、腰を据えて学習はしてないです
・PHPより簡潔にかけるなと少し感動しました。人気言語であるのは頷けます
・ただPythonは公式ドキュメントが読みづらいと思いました

5.Azure Functions
・スクレイピングをサーバレスで動かしたかったので利用しました
・AWS lamdaでもよかったかもしれませんが、Azureは無料枠があったのと、VSCODEとの連携が楽と聞いていたので今回はAzure Functionsを利用しました
Azure Functions の Python 開発者向けガイドという公式のチュートリアルで学習しました
・VSCODEにextencion入れればVSCODE内で開発、テスト、デプロイが簡単にできるので、便利でした。このやり方も公式ドキュメントがわかりやすかったです

最後に

以上、簡単なアプリ開発を通して、React,TypeScript,next.js,Azureの触りの部分を学習しました。
React,Next.jsは公式ドキュメントが充実していたので初心者でも学習しやすかったです。

ちなみに上記技術スタックの学習に約2〜3週間(仕事終わりの1、2時間、休日2,3時間)費やし、アプリ自体は概ね3日間程で作成しました。

Reactは触っていて単純に楽しかったです。
下記の記事にもあるように、HTML側で、JS/TSを制御するより、JSXでJS/TSでHTML側を制御する方が自分にはとっつきやすかったです。
参照:JSXが実はベターな解だったのではないか?
現に今の職場では巨大なHTML文の中に所狭しとjqueryの制御が埋め込まれているのでかなりコードの可読性が悪くなっております。

スタートアップ等を中心にバックエンド、フロントエンドの垣根が低くなってきているようなので(ベンチャー、スタートアップの面接で実際に何度か聞いたことがあります)、折に触れてフロントエンドの学習もしていきたいと思います。

ただ、次の開発のアイディアが浮かびません。。。

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

ReactにSassは非推奨?

CRAのドキュメントを漁っていたら気になる文面を見つけました。

https://create-react-app.dev/docs/adding-a-sass-stylesheet/

Generally, we recommend that you don’t reuse the same CSS classes across different components. For example, instead of using a .Button CSS class in and components, we recommend creating a component with its own .Button styles, that both and can render.

Following this rule often makes CSS preprocessors less useful, as features like mixins and nesting are replaced by component composition. You can, however, integrate a CSS preprocessor if you find it valuable.

このルールに従うと、ミックスインやネストなどの機能がコンポーネントの構成に置き換えられるため、CSSプリプロセッサの有用性が低下することがよくあります。 ただし、CSSプリプロセッサが役立つ場合は、統合することができます。

通常、異なるコンポーネント間で同じCSSクラスを再利用しないことをお勧めします。 たとえば、およびコンポーネントで.Button CSSクラスを使用する代わりに、との両方がレンダリングできる独自の.Buttonスタイルを使用してコンポーネントを作成することをお勧めします。

そうだったのか...

非CSS Module、非CSS in JSの場合のデファクトスタンダードがSassだと盲信していた自分には目からウロコでした。

という訳でボタンコンポーネントを作る場合でどうなるのか試しに実装してみました。

Sass

CodeSandbox: https://codesandbox.io/s/react-using-sass-dcfix

App.scss
$colors: (
  primary: #027afe,
  primaryDark: #0052aa,
  danger: #ff3c2f,
  dangerDark: #ad1106,
);

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
    'Open Sans', 'Helvetica Neue', sans-serif;
}

button {
  border: 0;
  padding: 0;
  background-color: transparent;
  cursor: pointer;
  font-size: inherit;
}

.Container {
  max-width: 1000px;
  margin-right: auto;
  margin-left: auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}


.Button {
  padding: 8px 16px;
  border-radius: 4px;
  background-color: rgb(223, 223, 223);
  transition: background 0.2s ease-out;
  &:hover {
    background-color: rgb(235, 235, 235);
  }
}

.Button--primary {
  background-color: map-get($colors, primary);
  color: white;
  &:hover {
    background-color: map-get($colors, primaryDark);
  }
}

.Button--danger {
  background-color: map-get($colors, danger);
  color: white;
  &:hover {
    background-color: map-get($colors, dangerDark);
  }
}
App.tsx
import * as React from "react";
import "./App.scss";

function Button(props: any) {
  return (
    <button type="button" className="Button" {...props}>
      {props.children}
    </button>
  );
}

function PrimaryButton(props: any) {
  return (
    <button type="button" className="Button Button--primary" {...props}>
      {props.children}
    </button>
  );
}

function DangerButton(props: any) {
  return (
    <button type="button" className="Button Button--danger" {...props}>
      {props.children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <div className="Container">
        <h1>Buttons</h1>

        <p>
          <Button>Button</Button>
        </p>

        <p>
          <PrimaryButton>Primary Button</PrimaryButton>
        </p>

        <p>
          <DangerButton>Danger Button</DangerButton>
        </p>
      </div>
    </div>
  );
}

基底コンポーネントの状態がまるっと別コンポーントになっているのは確かに一見スッキリしないですね。

CSS

CodeSandbox: https://codesandbox.io/s/react-using-css-h07qq

App.css
:root {
  --color-primary: #027afe;
  --color-primary-dark: #0052aa;
  --color-danger: #ff3c2f;
  --color-danger-dark: #ad1106;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

button {
  border: 0;
  padding: 0;
  background-color: transparent;
  cursor: pointer;
  font-size: inherit;
}

.Container {
  max-width: 1000px;
  margin-right: auto;
  margin-left: auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

.Button {
  padding: 8px 16px;
  border-radius: 4px;
  background-color: rgb(223, 223, 223);
  transition: background 0.2s ease-out;
}
.Button:hover {
  background-color: rgb(235, 235, 235);
}

.Button--primary {
  background-color: var(--color-primary);
  color: white;
}
.Button--primary:hover {
  background-color: var(--color-primary-dark);
}

.Button--danger {
  background-color: var(--color-danger);
  color: white;
}
.Button--danger:hover {
  background-color: var(--color-danger-dark);
}
App.tsx
import * as React from "react";
import "./App.css";

function Button(props: any) {
  return (
    <button type="button" className={`Button Button--${props.variant}`}>
      {props.children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <div className="Container">
        <h1>Buttons</h1>

        <p>
          <Button>Button</Button>
        </p>

        <p>
          <Button variant="primary">Primary Button</Button>
        </p>

        <p>
          <Button variant="danger">Danger Button</Button>
        </p>
      </div>
    </div>
  );
}

状態をpropsとして渡すことでコンポーネントと状態の主従関係がすぐ分かり、

Sassという余分なレイヤーも消えたので心が気持ち軽いです。

CSS(2)

TailwindのようなUtil的アプローチにも最近魅力を感じています。その場合こんな感じになるでしょう。

CodeSandbox: https://codesandbox.io/s/react-using-css-02-0jxq1

App.css
:root {
  --color-primary: #027afe;
  --color-primary-dark: #0052aa;
  --color-danger: #ff3c2f;
  --color-danger-dark: #ad1106;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

button {
  border: 0;
  padding: 0;
  background-color: transparent;
  cursor: pointer;
  font-size: inherit;
}

.Container {
  max-width: 1000px;
  margin-right: auto;
  margin-left: auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

.Button {
  padding: 8px 16px;
  border-radius: 4px;
  background-color: rgb(223, 223, 223);
  transition: background 0.2s ease-out;
}
.Button:hover {
  background-color: rgb(235, 235, 235);
}

App.tsx
import * as React from "react";
import classnames from "classnames";
import "./App.css";
import "./utils.css";

function Button(props: any) {
  return (
    <button
      type="button"
      className={classnames(
        "Button",
        props.variant && `bg-${props.variant}`,
        props.variant && "text-white",
        props.variant && `hover:bg-${props.variant}-dark`
      )}
    >
      {props.children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <div className="Container">
        <h1>Buttons</h1>

        <p>
          <Button>Button</Button>
        </p>

        <p>
          <Button variant="primary">Primary Button</Button>
        </p>

        <p>
          <Button variant="danger">Danger Button</Button>
        </p>
      </div>
    </div>
  );
}

Sass(おまけ)

元々のキモは別コンポーネントに同じクラスを使わないことだったはずです。
それならSassでも簡単に再現できます。

CodeSandbox: https://codesandbox.io/s/react-using-sass-02-k1ohh

App.scss
$colors: (
  primary: #027afe,
  primaryDark: #0052aa,
  danger: #ff3c2f,
  dangerDark: #ad1106,
);

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
    'Open Sans', 'Helvetica Neue', sans-serif;
}

button {
  border: 0;
  padding: 0;
  background-color: transparent;
  cursor: pointer;
  font-size: inherit;
}

.Container {
  max-width: 1000px;
  margin-right: auto;
  margin-left: auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

.Button {
  padding: 8px 16px;
  border-radius: 4px;
  background-color: rgb(223, 223, 223);
  transition: background 0.2s ease-out;
  &:hover {
    background-color: rgb(235, 235, 235);
  }
}

.Button--primary {
  background-color: map-get($colors, primary);
  color: white;
  &:hover {
    background-color: map-get($colors, primaryDark);
  }
}

.Button--danger {
  background-color: map-get($colors, danger);
  color: white;
  &:hover {
    background-color: map-get($colors, dangerDark);
  }
}
App.tsx
import * as React from "react";
import "./App.scss";

function Button(props: any) {
  return (
    <button type="button" className={`Button Button--${props.variant}`}>
      {props.children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <div className="Container">
        <h1>Buttons</h1>

        <p>
          <Button>Button</Button>
        </p>

        <p>
          <Button variant="primary">Primary Button</Button>
        </p>

        <p>
          <Button variant="danger">Danger Button</Button>
        </p>
      </div>
    </div>
  );
}

結論

「異なるコンポーネント間で同じCSSクラスを再利用しないことをお勧めします」は守った方が良いかも。
型指定はサボって申し訳ないです...

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

【React Hooks】非同期処理をいい感じに扱うuseAsyncを自分でつくる。

始めに

3 amazing REACT HOOKS to keep your code organized neatly』この記事を読んでいてうち一つは結構有益だなぁぁって思ったので紹介です!本編中のコードはここからの引用をちょっと修正したものになります。
react-useをご存知の方も改めてこうやって作れるなってコードベースが参考になったらなと思っています。

  • あるある非同期処理
  • useAsyncを使った場合
  • useAsyncの作りかた
  • react-use

といった形で紹介していきます。

As is:例えばあるあるの非同期処理

function MyComponent() {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)

  useEffect(() => {
    (async () => {
        setLoading(true)
        try {
            const result = await doSomeAction();
            setResult(result);
        } catch (e) {
            setError(e.toString());
        } finally {
            setLoading(false);
        }
    })();
  }, [])

  if (loading) {
    return <>loading...</>
  }

  if (error) {
  return <>{error}</>
  }

  return <>{result}</>
}

といった形の処理です。
doSomeAction()という何らかの処理によって、非同期で動作します。そして、それは何らかの結果やエラーとして帰ってきます。
そこでこのようにuseStateを絡めながらコードを書いていくことがしばしばあるかなと思います。
とはいえ、これを何度か書いていくのは煩雑感があります・・・。そこで、react-apollouseQuery みたいな形でかけたら幸せじゃないですか?

To be:useAsyncのある世界

function MyTidyComponent() {
    const {loading, result, error} = useAsync(doSomeAction)

    if (loading) {
      return <>loading...</>
    }

    if (error) {
      return <>something broke</>
    }

    return <>{result}</>
}

随分とシンプルになりました。
ではこれを作っていきましょう!というのが今日の本題。

useAsyncのつくりかた

function useAsync(asyncFunction, immediate = true) {
  const [loading, setLoading] = useState(false)
  const [result, setResult] = useState(null)
  const [error, setError] = useState(null)

  const mounted = useRef(true)

  useEffect(() => {
    return () => {
      mounted.current = false
    }
  }, []);

  const execute = useCallback(async (...args) => {
    setLoading(true)
    setResult(null)
    setError(null)
    try {
      const r = await asyncFunction(...args)
      if (mounted.current) {
        setResult(r);
      }
      return r
    } catch (e) {
      if (mounted.current) {
        setError(e);
      }
    } finally {
      if (mounted.current) {
        setLoading(false);
      }
    }
  }, [asyncFunction])

  useEffect(() => {
    if (immediate) {
      execute()
    }
  }, [execute, immediate]);

  return { execute, loading, result, error };
}

ほとんどはAs is のコードをそのまま持ってきたのかのような形です。
immediateはおしゃれポイントみたいですね。コンポーネントがマウントされてもimmediateをfalseにしておけば実行されず、trueに変更した時に実行されます。

  useEffect(() => {
    if (immediate) {
      execute()
    }
  }, [execute, immediate]);

useRefの記述や条件分岐はメモリリーク対策でこれが一度実行されてアンマウントされた場合に以下のコードが動かないようにしています。

  useEffect(() => {
    return () => {
      mounted.current = false
    }
  }, []);

クリーンアップ関数でcurrentをfalseにしていますね。
といった形なんですがreac-useではこれよりももうちょっとだけ内部的に良いuseAsyncが提供されています。
じゃあなんで書き方を紹介したの?ってなるんですが、これは内部実装についてしるのが面白かったのと、簡単にかけるのでimmediatelyのようなカスタマイズを施せるので知っておいて損はないなと思ったからです。

react-use

react-use

import {useAsync} from 'react-use';

const Demo = ({url}) => {
  const state = useAsync(async () => {
    const response = await fetch(url);
    const result = await response.text();
    return result
  }, [url]);

  return (
    <div>
      {state.loading
        ? <div>Loading...</div>
        : state.error
          ? <div>Error: {state.error.message}</div>
          : <div>Value: {state.value}</div>
      }
    </div>
  );
};

おしゃれポイントはなくなっていますが、ほとんどおんなじです。どこら辺が少し高性能なのかでいくと、第二引数がdepsになっておりdepsが変更された時、非同期関数が作り直されます。また、mounted.currentのような判定に加えて呼び出し回数による制御もかかっています。
他にはuseIntervalやuseTimeoutなどの使い勝手もよいものがたくさん提供されているのでつかっていくのが良さそうですね。

まとめ

  • 基本的にはreact-useを使った方が楽
  • だけどカスタマイズを重ねていきたい時はこんな感じでuseAsyncみたいなものをつくることができる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-konvaライブラリが使って便利だったので使用法などをメモ

概要

HTML5の世界では<canvas>というタグが存在し、2Dグラフィックスを描くことができます。
ただ、素の<canvas>は操作処理が手続き型なので、宣言的に記述できるライブラリが欲しいと思っていました。

そこで目をつけたのがKonva Framework。こちらは2Dグラフィックスを描くためのJavaScriptライブラリなのですが、それをReact風に宣言的に使えるreact-konvaというライブラリもあります。なので今回は、react-konvaのざっくりとした使用感について解説します。

導入

# npm の場合
npm install react-konva konva --save
# yarnの場合
yarn add react-konva konva

概念

このライブラリで宣言的に記述できるのは、<Stage>タグ、<Layer>タグ、<Group>タグ、<Rect><Text><Image>などの各種描画用タグです。階層構造としては次の通り。

              Stage
                |
         +------+------+
         |             |
       Layer         Layer
         |             |
   +-----+-----+     Shape
   |           |
 Group       Group
   |           |
   +       +---+---+
   |       |       |
Shape   Group    Shape
           |
           +
           |
         Shape

(出典:https://konvajs.org/docs/overview.html)

  • <Stage>タグ……描画の土台となるタグ。おおよそ<canvas>タグに相当。<canvas>の表示サイズや表示スケールを決めたり、これより内側の描画をtoDataUrl()メソッドで画像化したりするのに使える
  • <Layer>タグ……名前の通り、描画レイヤーとして機能するタグ。z-index、つまり表示順をこの単位で管理できる
  • <Group>タグ……こちらは必須ではないが、描画用タグをグルーピングするために使う。グループ単位で描画を拡大・縮小・回転・移動させることができる
  • 描画用タグ……四角形を描画する<Rect>、テキストを描画する<Text>、画像を描画する<Image>などのタグ。塗りつぶしの方法や透明度など、細かな設定も可能

より詳しい概要については、Konva自体の概要説明ページを読むと良いでしょう。

基本的な使い方

まずは、500x500ピクセルの<canvas>内に、左上座標が(100, 100)で、大きさが300x200な赤色の長方形を描いてみます。
見やすさの問題から、その長方形よりも先に、外枠として辺だけの長方形も黒線で描画しておきます。
ちなみに、いずれの描画用タグからも塗りつぶされていない箇所は透明色になっています。

import { Layer, Rect, Stage } from "react-konva";
// (中略)
return <Stage width={500} height={500}>
  <Layer>
    <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} />
    <Rect fill='red' x={100} y={100} width={300} height={200} />
  </Layer>
</Stage>;

image.png

真円を描く場合は<Circle>、楕円を描く場合は<Ellipse>、(折れ曲がりも含めた)直線を引く場合は<Line>を使用します。
他にも様々な描画用タグはありますが、使い方はKonvaのドキュメントを読めば察せられるかと。
後、opacity属性は透明度を決めるもので、0が完全透明・1が無透明です。

<Stage width={500} height={500}>
  <Layer>
    <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} />
    <Rect fill='red' x={100} y={100} width={300} height={200} />
    <Circle fill='blue' x={100} y={100} radius={50} opacity={0.5} />
    <Ellipse fill='green' x={250} y={300} radiusX={100} radiusY={180} />
    <Line points={[400, 50, 300, 150, 150, 170, 300, 50]} stroke='purple' strokeWidth={15} />
  </Layer>
</Stage>

image.png

面白いのが<Text>タグ。純正<canvas>におけるテキスト描画用関数(fillTextメソッドやstrokeTextメソッド)では自動で折り返してくれないのに対し、<Text>タグでは(最大横幅を設定すると)自動で折り返してくれます。また、テキスト内に改行を入れると、それに従って改行を入れてくれます。

<Stage width={500} height={500}>
  <Layer>
    <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} />
    <Text text={text} x={50} y={50} width={400} fontSize={40} fontFamily={'Calibri'} fill='black' align='left' />
  </Layer>
</Stage>

image.png

画像読み込みも可能です。サンプル画像は貼りませんが、指定したサイズにリサイズした上で貼ってくれます。
また、image属性に渡せるものは、Canvas APIにおける画像描画で使用できるそれと同様です。つまり、TypeScriptで言えば、

  • HTMLImageElement型……<img>タグ自体のDOM
  • SVGImageElement型……<svg>タグ自体のDOM
  • HTMLVideoElement型……<video>タグ自体のDOM
  • HTMLCanvasElement型……<canvas>タグ自体のDOM
  • ImageBitmap型……事前に描画のための処理を済ませた画像データ。Serializableなので保存しやすい
  • OffscreenCanvas型……名前の通り、画面に描画することを考慮してない<canvas>。データキャッシュ用に利用できる

が利用可能です。

<Stage width={500} height={500}>
  <Layer>
    <Image image={imageData} x={50} y={50} width={300} height={300} />
  </Layer>
</Stage>

座標系について

<Stage>タグに設定するwidth属性やheight属性はピクセル単位ですが、実は、<Layer>タグ以下に配置した各種描画タグで使うx属性やy属性などで設定するピクセル単位と微妙に異なります。以下では、両者をそれぞれ、次のように言い分けます。

  • 見かけ上のピクセル……<Stage>タグに設定するwidth属性やheight属性に設定するもの
  • 描画上のピクセル……各種描画タグで使うに設定するxywidthheight属性などに設定するもの

見かけ上のピクセルは<canvas>自体の表示サイズに関わります。一方、描画上のピクセルは、<Stage>タグに別途設定したscale属性の値に従い、実際に描画される際のサイズが変わります。例えば、次のように書いたとしましょう。

const scaleX = 0.5;
const scaleY = 0.4;
return <Stage scale={{x: scaleX, y: scaleY}} width={500} height={500}>
  <Layer>
    <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} />
    <Rect fill='red' x={100} y={100} width={300} height={200} />
  </Layer>
</Stage>;

ここで、<Rect>がタグどのように描画されるかを考えます。<Stage>タグにscale属性を設定した影響で、x属性やwidth属性はscaleX倍された大きさとして描画されます(y属性などはscaleY倍)。
しかし、<canvas>自体は<Stage>タグのwidth属性・height属性の値に従うので、結局、「500x500ピクセルの画像の中に、左上座標(50, 40)から大きさ150x80の赤い長方形が描画」されます。

image.png

<Stage>自体の大きさもscaleに合わせたいなーって時は、<Stage>自体のwidthheight属性もスケーリングする必要があります。ただし、その場合でも、描画上のピクセルに対応する、各種描画タグに、いちいちscaleXだのscaleYだのを使う必要はありません。

const scaleX = 0.5;
const scaleY = 0.4;
return <Stage scale={{x: scaleX, y: scaleY}} width={500 * scaleX} height={500 * scaleY}>
  <Layer>
    <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} />
    <Rect fill='red' x={100} y={100} width={300} height={200} />
  </Layer>
</Stage>;

image.png

画像保存について

<canvas>で描画したものを画像データ化したいことはよくあると思います。<Stage>においてもそういった場合の処理は提供されていますが、React的にはuseRefを使うのがベターでしょう。具体的にはこんな感じ。

import React, { useRef } from "react";
import { Stage as StageType } from 'konva/types/Stage';

const stageRef = useRef<StageType>(null);

const process = () => {
  const temp = stageRef.current;
  // stageRefの中身(temp )がnullな可能性を考慮してチェック
  if (temp !== null) {
    // dataUrlに、画像データがdata URL(MIME Type + base64文字列)形式で書き込まれる。
    // toDataURLの引数を変更すれば、PNG以外の画像形式への変換も可能
    const dataUrl = temp.toDataURL();
    // これ以降、dataUrlを使った操作(画像保存、画像加工等)を行う
  }
};

return <Stage ref={stageRef} width={500} height={500}>
  <Layer>
    <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} />
    <Rect fill='red' x={100} y={100} width={300} height={200} />
  </Layer>
</Stage>;

興味深いのは、toDataURLメソッド内の引数にpixelRatioというのがあることです。例えばtemp.toDataURL({pixelRatio: 2})とすれば、<Stage>width100height200だった場合、200x400ピクセルの画像として書き出されます。
つまり、例えば、「<canvas>をWebページ上では小さく表示するが、ダウンロードする際は大きい状態にする」ことが簡単にできます。

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

JavaScriptでアバターに自動的にユーザーの名前が入れよう

Webアプリを作っていると、headerなどに表示されているアバターに画像を表示したいと言ったことが出てくると思います。

しかし、ユーザーの新規登録時などアバターの画像を設定していない段階で、何かしら代わりの画像を設定しておきたいことがあります。

そこで、今回はJavaScriptのsliceメソッドを使ってユーザー名を切り出し、イニシャルをアバターとして表示する方法について解説します。

完成形の確認

完成形は以下の画像の通りです。

スクリーンショット 2020-11-29 14.49.55.png

この画像ではユーザー名を'Guest User'としています。
イニシャルのGが表示されています。

例えば、ユーザー名がTaro YamadaならTJohnならJのように最初の1文字が表示されます。

実装

今回、ReactとFirebaseを用いて実装しますが、大切な部分はJavaScriptのsliceを使うところだけです。

上記の画像は以下のコードで実装しています。

HeaderRight.jsx
export default function HeaderRight() {
  const classes = useStyles();
  const { currentUser } = useAuth();
  const [userNameInitial, setUserNameInitial] = useState();

  useEffect(() => {
    const id = currentUser.uid;
    db.collection("users")
      .doc(id)
      .get()
      .then((snapshot) => {
        const data = snapshot.data();
        const username = data.username;
        const initial = username.slice(0, 1);
        setUserNameInitial(initial);
      });
  }, []);

  return (
    <div className={classes.headerRight}>
      <Link to="/postform" className={classes.postFormLink}>
        <Button className={classes.postFormButton}>新規投稿</Button>
      </Link>
      <Link to="/bookmarkList">
        <IconButton className={classes.bookMark}>
          <LibraryBooksIcon />
        </IconButton>
      </Link>
      <Link to="/dashboard" className={classes.avatarLink}>
        <Avatar aria-label="recipe" className={classes.avatar}>
          {userNameInitial}
        </Avatar>
      </Link>
    </div>
  );
}

ここで注目して欲しいのは、useEffectの中に書いてあるconst initial = username.slice(0, 1);です。

ここでusernameというのはデータベースから参照してしるユーザー名です。

そしてusernameに対して、slice(0, 1)とすることで、最初の一文字を切り抜けます。sliceの0は切り抜きの開始位置、1は何文字切り取るかを示しています。

例えば、文字列の先頭から2文字切り取りたいなら、slice(0, 2)とすればOKです。

あとは、切り抜いた文字をアバターとして表示するだけです。
※今回はMaterial uiのAvatarを使っています。

今回はuserNameInitialという変数に格納していますが、それを表示すれば完了です。

参考

String.prototype.slice()

先頭、末尾の文字を取得する方法

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

JavaScriptでアバターに自動的にユーザーの名前が入るようにしよう!!

Webアプリを作っていると、headerなどに表示されているアバターに画像を表示したいと言ったことが出てくると思います。

しかし、ユーザーの新規登録時などアバターの画像を設定していない段階で、何かしら代わりの画像を設定しておきたいことがあります。

そこで、今回はJavaScriptのsliceメソッドを使ってユーザー名を切り出し、イニシャルをアバターとして表示する方法について解説します。

完成形の確認

完成形は以下の画像の通りです。

スクリーンショット 2020-11-29 14.49.55.png

この画像ではユーザー名を'Guest User'としています。
イニシャルのGが表示されています。

例えば、ユーザー名がTaro YamadaならTJohnならJのように最初の1文字が表示されます。

実装

今回、ReactとFirebaseを用いて実装しますが、大切な部分はJavaScriptのsliceを使うところだけです。

上記の画像は以下のコードで実装しています。

HeaderRight.jsx
export default function HeaderRight() {
  const classes = useStyles();
  const { currentUser } = useAuth();
  const [userNameInitial, setUserNameInitial] = useState();

  useEffect(() => {
    const id = currentUser.uid;
    db.collection("users")
      .doc(id)
      .get()
      .then((snapshot) => {
        const data = snapshot.data();
        const username = data.username;
        const initial = username.slice(0, 1);
        setUserNameInitial(initial);
      });
  }, []);

  return (
    <div className={classes.headerRight}>
      <Link to="/postform" className={classes.postFormLink}>
        <Button className={classes.postFormButton}>新規投稿</Button>
      </Link>
      <Link to="/bookmarkList">
        <IconButton className={classes.bookMark}>
          <LibraryBooksIcon />
        </IconButton>
      </Link>
      <Link to="/dashboard" className={classes.avatarLink}>
        <Avatar aria-label="recipe" className={classes.avatar}>
          {userNameInitial}
        </Avatar>
      </Link>
    </div>
  );
}

ここで注目して欲しいのは、useEffectの中に書いてあるconst initial = username.slice(0, 1);です。

ここでusernameというのはデータベースから参照してしるユーザー名です。

そしてusernameに対して、slice(0, 1)とすることで、最初の一文字を切り抜けます。sliceの0は切り抜きの開始位置、1は何文字切り取るかを示しています。

例えば、文字列の先頭から2文字切り取りたいなら、slice(0, 2)とすればOKです。

あとは、切り抜いた文字をアバターとして表示するだけです。
※今回はMaterial uiのAvatarを使っています。

今回はuserNameInitialという変数に格納していますが、それを表示すれば完了です。

参考

String.prototype.slice()

先頭、末尾の文字を取得する方法

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

【React】 routing を設定してみる

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です

routing を 設定してみる

routing とはこのURLにアクセスするとこの画面を表示しますっていうやつです。

まずは必要なライブラリをインストール

$ yarn add react-router-dom 

今回の目標としては Home, A, B の 3つのコンポーネントを準備し、

  • localhost:3000 にアクセスすると Home component を表示
  • localhost:3000/A にアクセスすると A component を表示
  • localhost:3000/B にアクセスすると B component を表示

を目指します。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route  } from "react-router-dom"

const Home = () => {
  return(<p>ホーム画面です</p>) 
}

const A = () => {
  return(<p>A画面です</p>) 
}

const B = () => {
  return(<p>B画面です</p>) 
}

const  App= () => {
  return (
    <BrowserRouter>
      <Route exact path="/" component={Home} />
      <Route path="/A" component={A} />
      <Route path="/B" component={B} />
    </BrowserRouter>
  )
};

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


localhost:3000/A にアクセス
スクリーンショット 2020-11-29 11.52.40.png

表示成功

localhost:3000, localhost:3000/B も正常に表示されていました。

まとめ

  • routing には react-router-dom を使うと良い
    • yarn add react-router-dom を実行するだけでインストール可
    • ファイルの先頭に import { BrowserRouter, Route } from "react-router-dom" を書く

今回は react-router-dom の中から BrowserRouter, Route だけを使いましたが、他にも Switch や Link, NaviLink など便利なものがいろいろあります。適宜使う時はどれも import するだけでOK

  • 補足 「exactについて」

<Route exact path="/" component={Home} /> の exact って何?って思った方もいると思います。

routing は URLの前方一致で最初に一致した component が表示されてしまいます。
ですが、exactを使うと 完全一致で一致した場合のみ表示されます。

今回の例で言うと/Aにアクセスした時に/がまずHome componentへの routing に一致してしまうので/AにアクセスするとHome画面が表示されます。
それを防ぐ意味でexactを追記しています。

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

Lottie+React NativeでFunctional Componentを使いたいんだけど?

結論

useRefを使いましょう

やりたかったこと

React Nativeでいいねボタンを押した時に、以下のようなオシャレなアニメーションをつけたくなりました。

animation_500_ki2f47b9.gif

Lottieを使うことにする

自作は辛いので調べているとこちらの記事の内容がドンピシャだったので、Lottieを使うことに決めました。

lottie-react-nativeの実装例を探す

React Nativeでlottieを使うにはlottie-react-nativeが便利です。

以下のリポジトリや記事を参照しながら実装例を探していきました。

あれ?どれもClass Componentばかりだぞ...?

ここまで調べてきたところ、
Class Componentのインスタンスにanimationの参照をもたせている実装例ばかりです。

意地でもFunctional Componentで書きたい

既存のコンポーネントをガッツリFunctional Componentで書いてしまっていたので、
ここからClass Componentに書き換えるのは面倒です。何とか実現できる方法を探しました。

Stackoverflowで良い質問を見つける

[useRef hooks doesn't work with lottie-react-native(https://stackoverflow.com/questions/58283848/useref-hooks-doesnt-work-with-lottie-react-native)

どうやらReact Hooksの機能の一つであるuseRefを使えばDOMの参照を取得できるので、
クラスを書かずに済みそうです。

Reactの公式ドキュメントでも紹介されていますね。
https://ja.reactjs.org/docs/hooks-reference.html#useref

useRefで実装した例

Expoの公式ドキュメントの内容をuseRefに書き換えたものをSnackに書きました。
コードはこちらをご覧ください。

lottie-react-native-useref-example

動作イメージはこんな感じです。

be059d357699d96915c584630c368d7e.gif

まとめ

ということでFunctional Componentで書いてしまっていたコンポーネントに、
Lottieを使ってアニメーションをつけたくなったらuseRefを使いましょう。

React Hooks便利

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

【React】state を使ってみる ~ React Hooks/useEffect ~

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

state を使って1秒毎にカウントアップするタイマーを作る

index.js
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const Timer = () => {
  const [time, setTime] = useState(0);

  // クリーンアップ関数を登録(return)する
  useEffect(() => {
    const timerId = setInterval(() => setTime(time + 1), 1000);
    return () => clearInterval(timerId);
  });

  return(
    <p>
      {time}
    </p>
  )
};

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

まとめ

import React, { useEffect } from 'react';を書くとuseEffectが使えるようになる。

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

【React】state を使ってみる

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

state を使って1秒毎にカウントアップするタイマーを作る

index.js
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const Timer = () => {
  const [time, setTime] = useState(0);

  // クリーンアップ関数を登録(return)する
  useEffect(() => {
    const timerId = setInterval(() => setTime(time + 1), 1000);
    return () => clearInterval(timerId);
  });

  return(
    <p>
      {time}
    </p>
  )
};

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

まとめ

import React, { useEffect } from 'react';を書くとuseEffectが使えるようになる。

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

【React】state を使ってみる useEffect

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

state を使って1秒毎にカウントアップするタイマーを作る

index.js
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const Timer = () => {
  const [time, setTime] = useState(0);

  // クリーンアップ関数を登録(return)する
  useEffect(() => {
    const timerId = setInterval(() => setTime(time + 1), 1000);
    return () => clearInterval(timerId);
  });

  return(
    <p>
      {time}
    </p>
  )
};

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

まとめ

import React, { useEffect } from 'react';を書くとuseEffectが使えるようになる。

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

【React】stateを使ってみる ~ ReactHooks/useState ~

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

クリックすると数字が増えていくというやつです。

index.js
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

const Counter = () => {
  const [count, setCount] = useState(0);

  return(
    <div>
       <p>count: {count}</p>
       {/* onClick={} の中に関数を書くとその関数が実行されます。*/}
       <button onClick={() => setCount(count + 1) }>click!</button>   
    </div>
  );
}

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

スクリーンショット 2020-11-29 0.54.17.png

表示成功(静止画を表示してますがクリックすると数が増えました)

まとめ

  • ファイルの先頭に import React, { useState } from 'react'; を書くと useStateが使えるようになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】stateを使ってみる1

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

クリックすると数字が増えていくというやつです。

index.js
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

const Counter = () => {
  const [count, setCount] = useState(0);

  return(
    <div>
       <p>count: {count}</p>
       {/* onClick={} の中に関数を書くとその関数が実行されます。*/}
       <button onClick={() => setCount(count + 1) }>click!</button>   
    </div>
  );
}

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

スクリーンショット 2020-11-29 0.54.17.png

表示成功(静止画を表示してますがクリックすると数が増えました)

まとめ

  • ファイルの先頭に import React, { useState } from 'react'; を書くと useStateが使えるようになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】stateを使ってみる useState

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

クリックすると数字が増えていくというやつです。

index.js
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

const Counter = () => {
  const [count, setCount] = useState(0);

  return(
    <div>
       <p>count: {count}</p>
       {/* onClick={} の中に関数を書くとその関数が実行されます。*/}
       <button onClick={() => setCount(count + 1) }>click!</button>   
    </div>
  );
}

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

スクリーンショット 2020-11-29 0.54.17.png

表示成功(静止画を表示してますがクリックすると数が増えました)

まとめ

  • ファイルの先頭に import React, { useState } from 'react'; を書くと useStateが使えるようになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】配列を繰り返し処理で回して props に渡してみる。

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

配列を繰り返し処理で回して props に渡してみる。

defaultPropsを使ってpropsに値が入らなかった時のデフォルトも設定してみまし

index.js
import React from 'react';
import ReactDOM from 'react-dom';

// User に入れる用のデータ
const profiles = [
  { name: '太郎', age: 10 },
  { age: 30 },
  { name: '花子', age: 25 },
  { name: '太郎' },
]

const User = (props) => {
  return (
    <p>私の名前は{props.name}で、年齢は{props.age}です。</p>
  )
};

// 値がなかった時のデフォルトを設定
User.defaultProps = {
  name: '名無し',
  age: 20
};

const Users = () => {
  return (
    <div>
      // map を使って profiles から User を繰り返し処理で出力
      {profiles.map((profile) => {
        return <User name={profile.name} age={profile.age}/>
      })}
    </div>
  )
};

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

表示成功!

スクリーンショット 2020-11-29 0.28.44.png

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

【React】配列を繰り返し処理で回して props に渡してみる

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

配列を繰り返し処理で回して props に渡してみる

defaultPropsを使ってpropsに値が入らなかった時のデフォルトも設定してみました。

index.js
import React from 'react';
import ReactDOM from 'react-dom';

// User に入れる用のデータ
const profiles = [
  { name: '太郎', age: 10 },
  { age: 30 },
  { name: '花子', age: 25 },
  { name: '太郎' },
]

const User = (props) => {
  return (
    <p>私の名前は{props.name}で、年齢は{props.age}です。</p>
  )
};

// 値がなかった時のデフォルトを設定
User.defaultProps = {
  name: '名無し',
  age: 20
};

const Users = () => {
  return (
    <div>
      // map を使って profiles から User を繰り返し処理で出力
      {profiles.map((profile) => {
        return <User name={profile.name} age={profile.age}/>
      })}
    </div>
  )
};

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

スクリーンショット 2020-11-29 0.28.44.png

表示成功

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

【React】 props を使ってみる

はじめに

React を習得するまでの軌跡をメモっていく備忘録的な記事です。

props を使ってみる

index.js
import React from 'react';
import ReactDOM from 'react-dom';

const User = (props) => {
  return (
    <p>私の名前は{props.name}です。</p>
  )
};

ReactDOM.render(
    <User name={'太郎'} />,
  document.getElementById('root')
);

スクリーンショット 2020-11-29 0.00.28.png

表示成功

まとめ

  • コンポーネントの引数に props と入れる。

  • コンポーネントの中では props.hoge という形で props の値が取り出せる。

  • コンポーネントへの渡し方は \<Hoge hoge={値} />

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