- 投稿日:2021-04-03T23:50:25+09:00
Material UIのData Gridでブラウザーバックに対応する
初めに
に関するTipsです。
本題
DataGridって便利ですよね。
React × Material UIで言うと、下記のようなライブラリがあります。
- https://material-ui.com/components/data-grid
- https://github.com/mbrn/material-table
- https://github.com/gregnb/mui-datatables
DataGridを使うことで、
- ページネーション
- フィルター
- ソート
などが簡単に実装できてしまいます。ただ、
ブラウザーバックをするとページやフィルター、ソートがリセットされてしまう。
というのを問題の一つと感じています。(特にデータ件数が詳細画面への遷移も多いAdmin系の画面だとより一層感じます。)その解決の解決策として、2つあるかと思います。
解決案その1
reduxなどのステート管理の仕組みを利用し、ページやフィルター情報を画面毎に保持しておき
再度画面が表示された際にページやフィルターの初期値として、ステートの情報をセットします。
ただ、この方法だと
- 画面毎にステートを管理する必要があり共通化しずらい。
- 通常の遷移の場合でもステートからセットされてしまう為、ステートのリセットや無視する実装が必要。
という問題があります。
解決案その2
URLパラメータにページやフィルター情報を含め、ページ操作などが行われた際にURLをreplaceします。
この方法だと、
- 共通化がし易い。
- ステート管理が不要。
- ページやフィルターを含めた状態のURLをブックマークやシェア出来る。
というメリットがあります。
実装方法
下記コードがその実装方法になります。
ポイントは、
DataGridのonPageChangeやonSortModelChangeのイベントを受け取り、
そのデータをqsというライブラリーを使用してURLに変換しています。
同様にqsを使ってURLからデータを復元し、DataGridにセットします。
※ 脆弱性は未検証です。各画面では、このDataGridを利用するだけで特に意識することなくブラウザーバックによる
ページやソートの復元が実現できます。URLのイメージはこんな感じになります。
ブラウザーバックでは一覧の状態が保持され、リンクをクリックした場合は初期状態になっています。
- 投稿日:2021-04-03T23:49:02+09:00
SPAって何?
Reactを勉強しててSPAという言葉よく聞くようになり、なんとなくの理解だった為改めて文字に起こしてアウトプットしていきたいと思います!
一番下に参考にした記事を載せておきます!
SPAってなに?
SPAとはSingle Page Application(シングルページアプリケーション)のことです。
簡潔にまとめると、必要な部分だけ更新して、必要ないところはそのままの表示にすることです!今までのwebアプリケーションの仕組みは、何かアクション(クリックなど)をすると、
①サーバーにリクエスト
②サーバー側でHTMLを生成
③②で生成されたHTMLを受け取り、ブラウザで描画する
流れでした。従来の仕組みだと不要なデータまで更新される為、表示するまでに時間がかかっていました。
そこでSPAの登場!!
①ユーザーがアクションを起こす
②①のアクションに必要なものだけをサーバーにリクエスト
③帰ってきたデータをJSで処理してHTMLで表示する
流れに変わりました!SPAの仕組みを利用することでユーザーのサービス利用時間が長くなる=購入確率などが高くなる=会社の売り上げに貢献!!←少し飛躍しすぎました。。。
だから今SPAへの温度感が非常に高まってるとか。。。
その他メリット
他のメリットデメリットについては他記事でも乗ってるので興味のある方はそちらを参考にしてみてください!
ここではざっくり説明していきます①よりリットなweb表現ができる
ReactやVue.jsなどのライブラリやフレームワークが誕生したことによって、簡単に様々な物が実装可能なり、いろんなUIを表現することができるようになりました!
②ネイティブアプリの代用ができる
ここに関しては自分自身が体験してない為、あまり実感が湧きませんがSPAを導入することで
ネイティブアプリで実装されてたことがSPAでも表現することが可能とのことです!!←いずれここも体験してみたいです!最後に
どうやらSPAは何かを実装する上でデファクトスタンダードそう。これからもSPAを意識しながら学習に取り組んでいきたいと思います!!
参考記事
https://rara-world.com/spa-single_page-merit/
https://www.oro.com/ja/technology/001/
- 投稿日:2021-04-03T23:31:15+09:00
Reactのコンポーネントについてまとめてみた
Reactを触っていると『コンポーネント』という言葉を頻繁に耳にします。
どのようなことか概念は分かっているのですが、以下に言語化して理解を深めたいと思います。コンポーネントとは?
コンポーネントを直訳すると『構成要素』『部品』や『コンピュータ機器やソフトウェアの部品』『ステレオで、チューナー・アンプ・プレーヤー・スピーカーなどの単独の機器』のことを表しますが、Reactでは『UI の一部分となるビュー (View) を切り出したもの』を表しています。
そもそもUIって何のことか曖昧だったのでついでに調べてみました。
UI(ユーザーインターフェイス)はユーザー(利用者)と製品やサービスとのインターフェース(接点)すべてのことを意味するということ。
具体的にはGoogleのトップページは大変シンプルでユーザーに分かりやすいように設計されているので優れているとUI言えます。そもそも今、目に入っているwebページもUIです。コンポーネント化するメリットは何か?
コンポーネント化をすることのメリットは何があるのか。
それはViewを切り出したものを使い回せることが一番大きいと思います。
コンポーネントごとにjs/jsxファイルを用意して、それを組み合わせてUIを構築・開発していきます。例えばボタンをコンポーネント化させることで、トップページで使ったボタンをユーザーページでも再利用することができます。つまり一つコンポーネントを作っておけば、そのコンポーネントを呼び出せば簡単に使い回すことができるということです。
(ちなみにHeaderやFooterなどもコンポーネント化させることができます!)これによりコードの記述量が減ったり、メンテナンス性が向上したりします。
他にもメリットはありますが以上のことからViewを切り出したものを使い回せることが一番大きいと思います。最後に
コンポーネントをもっと深く知りたい方はぜひ公式のドキュメントの三目並べを作ってもらうのが一番理解出来るかと思います!
コンポーネントと再レンダリングの関係性もとても大切なので、こちらもゆくゆくは記事にしたいです。
また内容に不明点などありましたらコメントいただければ幸いです。参考文献
- 投稿日:2021-04-03T23:31:15+09:00
Reactのコンポーネントのメリットは何か。
Reactを触っていると『コンポーネント』という言葉を頻繁に耳にします。
どのようなことか概念は分かっているのですが、以下に言語化して理解を深めたいと思います。コンポーネントとは?
コンポーネントを直訳すると『構成要素』『部品』や『コンピュータ機器やソフトウェアの部品』『ステレオで、チューナー・アンプ・プレーヤー・スピーカーなどの単独の機器』のことを表しますが、Reactでは『UI の一部分となるビュー (View) を切り出したもの』を表しています。
そもそもUIって何のことか曖昧だったのでついでに調べてみました。
UI(ユーザーインターフェイス)はユーザー(利用者)と製品やサービスとのインターフェース(接点)すべてのことを意味するということ。
具体的にはGoogleのトップページは大変シンプルでユーザーに分かりやすいように設計されているので優れているとUI言えます。そもそも今、目に入っているwebページもUIです。コンポーネント化するメリットは何か?
コンポーネント化をすることのメリットは何があるのか。
それはViewを切り出したものを使い回せることが一番大きいと思います。
コンポーネントごとにjs/jsxファイルを用意して、それを組み合わせてUIを構築・開発していきます。例えばボタンをコンポーネント化させることで、トップページで使ったボタンをユーザーページでも再利用することができます。つまり一つコンポーネントを作っておけば、そのコンポーネントを呼び出せば簡単に使い回すことができるということです。
(ちなみにHeaderやFooterなどもコンポーネント化させることができます!)これによりコードの記述量が減ったり、メンテナンス性が向上したりします。
他にもメリットはありますが以上のことからViewを切り出したものを使い回せることが一番大きいと思います。最後に
コンポーネントをもっと深く知りたい方はぜひ公式のドキュメントの三目並べを作ってもらうのが一番理解出来るかと思います!
参考記事・サイト
- 投稿日:2021-04-03T23:26:06+09:00
【とりあえずハンズオン】AWS AmplifyでReactやってみた
はじめに
この記事では
AWS Amplify を利用して React を実行してみたハンズオンの記事です。
ベストプラクティスや間違いがあれば
書き直していく予定です。AWS Amplify とは
ウェブアプリケーションを素早く構築してくれる AWS のサービス
数多くの言語がサポートされている。Amplify では、一般的なウェブフレームワーク (JavaScript、React、Angular、Vue、Next.js) や
モバイルプラットフォーム (Android、iOS、React Native、Ionic、Flutter) がサポートされています。
AWS Amplify で迅速な市場投入を実現してください。
最近、SPA 化が流行り出したこともあって
脚光を浴びているサービスに見えなくもないので実際どんなもんか
今回はハンズオンしてみた。結論
めっちゃ使いやすい!!
「嘘でしょ!?」ってくらい簡単にできてしまったのでハンズオンした記録を残しておきたい。
参考にしたページ
モジュール 1 をハンズオン
React レポジトリを作成
AWS Amplify で利用するレポジトリを GitHub から取得する為
GitHub に push する用のアプリケーションを作成する。create-react-app amplifyappディレクトリをチェンジして
ローカルホストで動作確認を行う。cd amplifyapp yarn startしばらくすると、ぬるぬる動く React おなじみの絵が表示される。
React レポジトリを GitHub に push
動作確認が終わったら一旦、アプリケーションを止める。
git init git add . git commit -m "initial commit" git branch -M main git remote add origin git@github.com:username/amplifyapp.git git push -u origin main※username には自分の GitHub アカウント ID を利用
AWS Amplify 開く
GET STARTED をクリックするとスクロールが下に案内される。
Develop と Deliver の 2 つがあるので「Host your web app」の「GET STARTED」をクリック
Host your web app
ホストするアプリケーションを Github から取得する為
「GitHub」を選択GitHub 認証済みだと以下のようになるが、まだ認証していない人は
ユーザ名とパスワードが聞かれ、アプリケーションを認証するかどうか聞かれます。ホストするレポジトリ名とブランチ名を選択肢して「次へ」
ビルド設定の構成
これはとりあえずデフォルトでヨシッ。
迷わずに「次へ」
保存してデプロイ
「保存してデプロイ」をクリックすると。。。
プロビジョニング、ビルド、デプロイ、検証というプロセスを踏んで
デプロイ作業が始まる。デプロイが終わると Amplify が生成した URL から
作成したアプリケーションを実行できる。AWS Amplify の凄いところ
手軽さに尽きる。
お手元のアプリケーションをサッとデプロイすることで
あとは良しなにやってくれる。そして、デプロイに成功したアプリ群はすべてのアプリから一覧で見ることができる。
今まで作ったアプリを容易に管理できるってわけ。さらに、チョット人に見せる程度の用途なら
インフラストラクチャの構築がいらないのは凄い。
※厳密にいえば、AWS Amplify が良しなにやってくれてる。GitHub との連携も効いているので CircleCI とも疎結合に連携できる。
何でもかんでも出来すぎて。。。出木杉君になったわ。
でも、お高いんでしょ?
お値段はこちら
https://aws.amazon.com/jp/amplify/pricing/2021/04/03(土) 現在では
Amplify フレームワーク
Amplify フレームワーク (ライブラリ、CLI、UI コンポーネント) を使用する場合は
基盤として使用する AWS のサービスに対してのみお支払いいただきます。
Amplify フレームワークの使用には、追加料金は発生しません。静的ウェブホスティング
AWS Amplify コンソールでは、2 つの機能に対して料金が設定されています。
ビルド & デプロイ、およびホスティングです。
ビルド & デプロイ機能の場合、ビルド分あたりの料金は 0.01USD です。
ホスティング機能の場合、対象の GB ごとの料金 0.01USD と保存された GB ごとの価格は 0.01USD です。AWS 無料利用枠を使用すると、無料で使い始めることができます。
サインアップすると、新規の AWS 顧客はビルドとデプロイ機能で 1 か月あたり 1,000 ビルド分、
ホスティング機能で 1 か月あたり 15 GB の提供**と 1 か月あたり **5 GB のデータストレージを受け取ります。つまりどういうことか
- Amplify フレームワーク とやらを利用すると料金が発生
- ビルドアンドデプロイするとビルド分だけ料金が発生
- 静的ウェブホスティングはGB分だけ料金が発生
- 無料利用枠ならイイ感じに施しを受けられる
おや?これはポートフォリオの公開にうってつけでは?
まとめ
今回は AWS Amplify を使って React アプリケーションをデプロイしました。
使い方を変えればノーコードアプリケーションもデプロイできるんかなこれ。
だとしたら結構優れものだぞ。おわり
- 投稿日:2021-04-03T20:45:38+09:00
jsx,tsxでEmmetを使用する方法
はじめに
ReactでEmmetが使えたら便利だな〜って思って調べたら利用出来たのでご紹介します。
VScodeだと素の状態でもEmmetがインストールされているのでおすすめです。方法
方法はsetting.jsonを弄るだけ。
command + ,(Windows: Ctrl + ,)で設定を開きます。
検索にsetting.jsonと入れてsetting.jsonを編集をクリックその後setting.jsonに
setting.json{ "emmet.triggerExpansionOnTab": true, "emmet.includeLanguages": { "javascript": "javascriptreact", "typescript": "typescriptreact", }, }を入れればtsx,jsxでEmmetが使用できます。
さいごに
Emmetを愛していたのでEmmetが使用できない環境は苦痛でしたが解決しました。
Emmetを使い慣れてる人にはおすすめです。
- 投稿日:2021-04-03T19:27:34+09:00
フロントエンド、バックエンド、モバイルアプリのボイラープレートを公開します。
弊社で作成中のWebアプリ、モバイルアプリ用のボイラープレートを公開します。
公開した経緯
作ったは良いものの、実プロジェクトで使うかは微妙です。
まだまだ完成度は低いですが、完成せずに放置する可能性も出てきました。
個人の時間も使って作ったので、少しでも役立てたいと思い公開の許可を頂きました。使用している主な技術
使用している主な技術はこんな感じです。
環境 言語 フレームワーク Backend PHP Laravel Frontend Typescript React, Redux Native Dart Flutter, Provider 注意点
- LaravelやFlutterは初めて使ってます。
- 未熟な部分も多々ありますので、優しく教えていただければ幸いです。
- 実プロジェクトでまだ利用してませんので、有用かは分かりません。
- ボイラープレートを良いことに雑な箇所もあります。。
主な特徴
- テスト、静的解析自動化 (Github Actions)
- Backend -> PHPStan, PHP_CodeSniffer, PHPUnit
- Frontend -> ESLint, Typescriptのビルド
- Native -> Flutter format, Flutter test
- ステージング環境へのデプロイ自動化 (Github Actions)
- Backend -> Heroku
- Frontend -> Firebase Hosting
- Native -> Firebase App Distribution
- ステージング環境は激安運用可能 (Freenom, CloudFlare)
- 多言語、タイムゾーンを考慮 (検証不十分です。特にタイムゾーン。)
- フロントエンドやモバイルとバックエンドの繋ぎ込みを極力共通化
- API呼び出しのローディングやサーバーサイドのエラー反映も共通化
詳細
今後、各アプリの詳細や得られた知見を記事にできたらなと思います。
画面イメージ
ボイラープレートですので、あえて(?)シンプルです。
Webアプリ
モバイルアプリ
- 投稿日:2021-04-03T17:10:17+09:00
Reactでページ内遷移のある横スクロールスライドメニューを実装する
はじめに
スマホ向けのWebアプリケーションをReactで開発していて、横スクロール型のスライドメニュー(ナビゲーション)を作りたい!という時にご参考ください。 React×Railsで開発しています。
HTMLのアンカータグで#を使って特定のid属性に遷移させたい!それをReactでやりたい!みたいな時を想定。
やりたいこと
- アプリライクな横スクロール型のメニューを作りたい(スクロールバー非表示にして)
- メニューの各要素をクリックしたらそれぞれの要素までぬるっとアニメーション付きでページ内遷移させたい
- ヘッダーの高さを考慮してページ内遷移させたい
- mapではき出した動的な要素にid属性を振って遷移先にしたい
方針
Reactでページ内遷移するいい方法ないかなーと探していたところ、こちらの記事(Reactでページ内リンクを実装する)を発見。今回はこちらを参考に
react-router-hash-linkを使うことに。Githubはこちら:React Router Hash Link
前提として本実装では、
itemCategoryのcategory_nameがカテゴリ名で、それをメニューバーにし、同一ページ内のそれぞれのcategory_nameの部分に遷移させています。実装
1. yarn add かnpmでインストール
Terminalnpm install --save react-router-hash-link2.
react-router-hash-linkとMemoryRouter(後述するエラーが出たため)をimporthoge.tsximport {MemoryRouter} from 'react-router-dom'; import {HashLink} from 'react-router-hash-link';3.遷移元となる横スクロールのメニューを作成
smoothをつけるだけでぬるっといい感じの遷移にHashLinkの機能。とても便利。
href=#hogeになるようにid設定JSX記法で
to={'#' + itemCategory.category_name}とした。これで#hogeとなり、idに遷移できる。謎のエラーを
MemoryRouterで解決
HashLinkを使ったところ、Uncaught Error: Invariant failed: You should not use <Link> outside a <Router>のエラーが表示されたため、こちらの記事(解決方法: "Error: Invariant failed: You should not use outside a ")を参考に、MemoryRouterでHashLinkを囲う。ヘッダーの高さ調整
scroll={el => { el.scrollIntoView(true); window.scrollBy(0, -160) }}でヘッダー部分の高さを考慮。この場合は160px分下げている。hoge.tsx<div className={classes.scrollMenuList}> <div className={classes.scrollMenuListMask}> {props.shopItemCategories.map((itemCategory) => ( <Typography component="h2" variant="h3" > <MemoryRouter> <HashLink smooth to={'#' + itemCategory.category_name} scroll={el => { el.scrollIntoView(true); window.scrollBy(0, -160) }} className={classes.scrollMenu}>{itemCategory.category_name} </HashLink> </MemoryRouter> </Typography> ))} </div> </div>4.スクロールバーを非表示に
こちらの記事(Appleに学ぶ、横スクロールナビを組む時のCSSメモ)を参考に、Maskとなる
scrollMenuListMaskを用意し、親要素のscrollMenuListをoverflow:"hidden"としラッパーする。各要素はアンダーバーがダサいので、textDecoration:"none"に。hoge.tsxscrollMenuList: { width: '900px', overflow: "hidden", height: 60, }, scrollMenuListMask: { overflowX: "auto", whiteSpace: "nowrap", display: 'flex', height: 80, //Maskのheightを親要素より高くすることがミソ }, scrollMenu: { margin: theme.spacing(1, 2, 1), fontSize: 36, color: "#8f8f8f", textDecoration: "none" },5.遷移先にid属性を振る
id属性を振るだけなので簡単。ここで振ったidを遷移元となるHashLinkで指定することでページ内遷移できる。
hoge.tsx{props.shopItemCategories.map((itemCategory) => ( <div className={classes.root}> <Typography component="h2" variant="h3" id={itemCategory.category_name}> {itemCategory.category_name} </Typography> </div> ...省略 ))}完成
完成したものが以下の通り。
hoge.tsx//Import import {MemoryRouter} from 'react-router-dom'; import {HashLink} from 'react-router-hash-link'; //該当部分のみのCSS const useStyles = makeStyles((theme: Theme) => createStyles({ scrollMenuList: { width: '900px', overflow: "hidden", height: 60, }, scrollMenuListMask: { overflowX: "auto", whiteSpace: "nowrap", display: 'flex', height: 80, }, scrollMenu: { margin: theme.spacing(1, 2, 1), fontSize: 36, color: "#8f8f8f", textDecoration: "none" }, }) ) //スクロールスライドメニューとなる部分 <div className={classes.scrollMenuList}> <div className={classes.scrollMenuListMask}> {props.shopItemCategories.map((itemCategory) => ( <Typography component="h2" variant="h3" > <MemoryRouter> <HashLink smooth to={'#' + itemCategory.category_name} scroll={el => { el.scrollIntoView(true); window.scrollBy(0, -160) }} className={classes.scrollMenu}>{itemCategory.category_name} </HashLink> </MemoryRouter> </Typography> ))} </div> </div> //メニューからの遷移先になる要素たち {props.shopItemCategories.map((itemCategory) => ( <> <Box> <div className={classes.root}> <Typography component="h2" variant="h3" id={itemCategory.category_name}> {itemCategory.category_name} </Typography> </div> </Box> ...省略 </> ))}今回も色々な記事を参考にさせていただきました。横スクロールのスライダーみたいなのは結構使いたい場面多いと思うので、少しでも参考になれば嬉しいです。
参考
横スクロールナビゲーションを実装する3つの方法
Reactでページ内リンクを実装する
【React Router】画面遷移時に#を用いて特定の要素に移動させる方法
Reactで動的に属性の値を生成する方法
- 投稿日:2021-04-03T13:25:41+09:00
Rails 6.1・React・Docker・MySQLで環境構築
前書き
2021年3月からE2Eテストの自動化を担当し、初めて仕事でコードを書いている。
テスト対象のアプリケーションのapiがRails、フロントがReactで作られているので、
興味本意でプライベートで何か作ってみることにした。
(職場でテストコードをJSで書くので、JSに慣れるという意味合いもある。)早速、Dockerで環境構築してみたので、備忘として残す。
環境
・macOS Big Sur 11.2.3
・Ruby 3.0
・Rails 6.1.3.1
・Docker 20.10.5
・docker-compose 1.28.5
・Mysql 8.0手順
全体をざっくりと説明すると以下の通り。
1.ファイルを全て準備する
2.docker-compose buildする
3.railsとreactの各種コマンド実行
4.docker-compose upする1.ファイルを全て準備する
最終的に、以下のような構造になる。
spa-chat |-- docker-compose.yml |-- api |-- entrypoint.sh |-- Gemfile |-- Gemfile.lock |-- Dockerfile |-- front |-- DockerfileRails関連
Gemfile
Gemfilesource 'https://rubygems.org' gem 'rails', '~>6'Gemfileには、使いたいRailsのバージョンを記入。
この場合、Rails6系の最新のバージョンを採用。Gemfile.lock
touch Gemfile.lock空のGemfile.lockを作成。
entrypoint.sh
entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"Dockerfile
DockerfileFROM ruby:3.0 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update -qq \ && apt-get install -y nodejs yarn \ && mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"]Dockerfileについては、Rails5系とRails6系で書き方が異なるので注意が必要。
Rails6系では、JavascriptコンパイラがWebpackerになったことに起因する。React関連
Dockerfile
DockerfileFROM node:15.13.0-alpine RUN mkdir /myapp WORKDIR /myappdocker-compose.yml
docker-compose.ymlversion: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3306:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql-data:/var/lib/mysql api: build: ./api command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - ./api:/myapp - gem_data:/usr/local/bundle ports: - "3000:3000" depends_on: - db stdin_open: true tty: true front: build: ./front command: yarn start ports: - '8000:3000' volumes: - ./front:/myapp depends_on: - api volumes: mysql-data: gem_data: driver: local2.docker-compose buildする
各種ファイルを揃えたので、イメージをbuild。
docker-compose build3.RailsとReactの各種コマンド実行
コマンドを実行し、必要なファイルを揃えていく。
React
ローカルで立ち上げる時と同じような感じで、以下のコマンドを実行。docker-compose run front npx create-react-app front※原因は突き止められなかったが、フロント用のDockerfileを置いたfrontディレクトリに、
さらに別のfrontディレクトリが作成され、その配下に各種フォルダが作成された。
なので、Dockerfileと同じ階層に、先程のコマンドで作成したフォルダを移動する必要がある。
(移動させないと、ブラウザからフロントにアクセスできない。)アドバイスいただけますと幸いです。
Rails
まず、以下のコマンドを実行。
docker-compose run api rails new . --force --no-deps --database=mysql --skip-test --webpacker --apiapiモードでrails new。
RSpecでテストコードを書くので、testディレクトリは作成しない。次に、config/database.ymlを編集。
database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= ENV.fetch("MYSQL_USERNAME", "root") %> password: <%= ENV.fetch("MYSQL_PASSWORD", "password") %> host: <%= ENV.fetch("MYSQL_HOST", "db") %> development: <<: *default database: myapp_development test: <<: *default database: myapp_test production: <<: *default database: myapp_production username: myapp password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>そして、データベースを作成。
docker-compose run api rake db:create4.docker-compose upする
準備が整ったので、コンテナを起動。
docker-compose up -dlocalhost:8000で見慣れたReactの画面が表示。
localhost:3000で見慣れたRailsの画面が表示。
参考にしたサイト
https://qiita.com/nsy_13/items/9fbc929f173984c30b5d
Rails側の設定を書くときにとても参考になりました。https://nakatanorihito.com/programming/docker-rails-api-react-postgresql/
React側の記述や、ディレクトリ構造を参考にしました。
docker-compose.ymlは上記2サイトをどちらも参考にしています。https://docs.docker.com/compose/rails/
docker docsに記載がある、Railsの環境構築方法。
Rails5系を使うことを前提に書かれている。
- 投稿日:2021-04-03T12:22:33+09:00
ReactでonClickで指定したイベントハンドラがレンダー時に発火する
環境
- react 17.0.1
内容
Material-UIのchipのタイトルをイベントハンドラの引数に指定したら、レンダー時に発火した。
レンダー時に発火するコードexport default function TagsSerch() { const handleClick = (title) => { console.info(title); }; /* 色々省略 */ return ( <Box> <Typography variant="subtitle1">タグ検索</Typography> {tagList.map((tag, index) => ( <Chip className={classes.tags} name={tag.title} label={tag.title} clickable color="primary" size="small" onClick={handleClick(tag.title)} /* ⇦ここ */ key={tag.title} /> ))} </Box> ); }React公式サイトによると、こんな感じで引数を渡してくださいとのこと。
レンダー時に発火せず引数をイベントハンドラに渡すexport default function TagsSerch() { const handleClick = (title, e) => { console.info(title); console.info(e); }; /* 色々省略 */ return ( <Box> <Typography variant="subtitle1">タグ検索</Typography> {tagList.map((tag, index) => ( <Chip className={classes.tags} name={tag.title} label={tag.title} clickable color="primary" size="small" onClick={onClick={(e) => handleClick(tag.title, e)}} /* アロー関数かまして呼び出すよ */ key={tag.title} /> ))} </Box> ); }今回はイベントオブジェクトはいらないので、最終的にこんな感じになりました。
レンダー時に発火せず引数をイベントハンドラに渡す(イベントオブジェクトなし)export default function TagsSerch() { const handleClick = (title) => { console.info(title); }; /* 色々省略 */ return ( <Box> <Typography variant="subtitle1">タグ検索</Typography> {tagList.map((tag, index) => ( <Chip className={classes.tags} name={tag.title} label={tag.title} clickable color="primary" size="small" onClick={onClick={() => handleClick(tag.title)}} /* アロー関数かまして呼び出すよ */ key={tag.title} /> ))} </Box> ); }
- 投稿日:2021-04-03T10:32:54+09:00
webpack + babel + TypeScript + React メモ
webpack + babel + TypeScript + React 環境構築メモ
- init
npm init -y- Babel
npm i -D @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript- .babelrc か babel.config.json を作成する(.babelrcはディレクトリ単位、babel.config.jsonはプロジェクト全体)
- webpack
npm i -D webpack webpack-cli babel-loader ts-loader- webpack.config.js
- React、TypeScript
npm i -S react react-domnpm i -D typescript @types/react @types/react-domnpx tsc init- src/index.ts
- webpack-dev-server
npm i -D webpack-dev-server- dist/index.html
- package.json
npm startコマンドの説明
npm init -y・・・ package.json をデフォルト設定で作成npm i -S=npm install --save・・・ package.json の dependenciesに追加npm i -D=npm install --save-dev・・・ package.json の devDependenciesに追加babel
.babelrc{ "presets": [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript" ] }webpack
webpack.config.jsmodule.exports = { // 本番にデプロイするときはモード値を production に設定 // development に設定すると元のファイルとの関連性がわかるソースマップと一緒に出力される mode: 'development', // 環境によってはsrc/index.ts entry: 'src/index.tsx', module: { rules: [ { test: /\.tsx?$/, // ? で、.ts or .tsx use: 'ts-loader', // TypeScript用のloader }, ], }, resolve: { // React × TypeScriptで使う可能性のある拡張子を全て記述 extensions: [ '.ts', '.js', '.tsx', '.jsx' ], }, };React + TypeScript
src/index.tsximport React from 'react'; import { render } from 'react-dom' render( <h1>React x TypeScript!</h1>, document.getElementById('root') )dist/index.html<!DOCTYPE html> <html> <head> <title>Hello, React!</title> </head> <body> <div id="root"></div> <script src="./bundle.js"></script> </body> </html>webpack-dev-server
package.json{ // 省略 "scripts": { "start": "webpack serve", // 追加 }, // 省略 }npm start参考
https://tech.playground.style/javascript/babel-webpack/
https://tech.playground.style/javascript/babel-webpack-build/
- 投稿日:2021-04-03T10:19:54+09:00
Reactで複数要素に動的にクリック→スクロールする(createRef, useRef)
Reactでクリック→スクロールする方法について
Reactで『クリック→該当部分までスクロール』を行う場合、以下のようにcreateRefを使用することがありました。
App.tsxconst ref = createRef<HTMLDivElement>()ここで定義したrefをスクロールさせたい箇所に渡して、
App.tsxconst handleJump = useCallback(() => { ref!.current!.scrollIntoView({ behavior: "smooth" }) }, [ref])クリック時にスクロールするよう関数を定義する。すると以下のようにクリック→スクロールができます。
ソースコードは以下を参照いただければ幸いです。
CodeSandboxですが、これだとスクロール箇所が増える度に、ユニークなrefを定義していく必要があるので、
複数の要素に動的にスクロールさせることを検討しました。結論
以下がソースコードです。
CodeSandboxApp.tsxtype Item = { title: string; background: string; service: string; otherContent?: boolean; }; const items: Item[] = [ { title: "コンテンツ1", background: "skyblue", service: "サービス1" }, { title: "コンテンツ2", background: "yellow", service: "サービス2" }, { title: "コンテンツ3", background: "green", service: "サービス3", otherContent: true } ];↑のような配列のデータがある場合、
App.tsxconst pageRef = useRef(items.map(() => createRef<HTMLDivElement>())); const scrollToView = (id: number) => { pageRef.current[id]!.current!.scrollIntoView({ behavior: "smooth" }); };↑のコードのように
- mapを使ってrefの配列を作り
- 関数(scrollToView)の引数にはidを受け取るとすることでrefを何度も定義せずに動的にクリック→スクロールをさせることができました!
参考
- 投稿日:2021-04-03T10:05:59+09:00
GitHub Actionsの基礎③ CI/CDパイプラインの構築
GitHub Actions学習のまとめとして、Create React AppのプロジェクトのCI/CDパイプラインを構築してみました。
使用したnpmパッケージについては以下の記事でまとめています。
プロジェクトの作成
npx create-react-app react-app --use-npmでプロジェクトを作成します。ワークフローの構成
Git-flowでの運用を想定して、以下のタイミングで実行するワークフローを作成します。
- featureからdevelop向けのプルリクを作成したとき
- プルリクが承認されてfeatureがdevelopへマージされたとき
- developからmaster向けのプルリクを作成したとき
- プルリクが承認されてdevelopがmasterへマージされたとき
加えて、リリースされたらSlackに通知するワークフローも作成します。
ワークフローの作成
前項の図でワークフローが4つありましたが、これらを1つのymlファイルに記述します。
ワークフロー①の作成(一部分)
まずは①(featureからdevelop向けのプルリクを作成したとき)のワークフローの内容を記述します。
ci.ymlname: CI on: pull_request: branches: [develop] #develop向けのプルリクが作成されたときにジョブを実行 jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 #チェックアウトするアクション - name: Use NodeJS uses: actions/setup-node@v1 #特定バージョンのnodeを設定するアクション with: node-version: "12.x" - run: npm ci #dependencyのインストール - run: npm run format:check #コードフォーマットチェック - run: npm test -- --coverage #テスト実行 env: CI: true #現在の環境がCI環境であることを仮定ここではdependencyのインストール、コードフォーマットチェック、テスト実行を記述しています。
CIにおいては、npm installではなくnpm ciでdependencyのインストールを行います。npm ciでは、依存関係の更新をせずに整合性チェックと依存パッケージのダウンロードのみを行うため、npm installより高速に動作し、CIで必要なことだけを行うことができます。
CI: trueではテストがCI環境で実行されることを仮定しています。
ワークフロー②の作成
developからworkflowというfeatureブランチをきって、
git pushしてdevelop向けのプルリクをつくるとワークフローが実行されます。
ワークフロー②(プルリクが承認されてfeatureがdevelopへマージされたとき)の内容のステップを追記します。
ci.ymlname: CI on: pull_request: branches: [develop] push: branches: [develop] #developにマージされたときに実行 jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Use NodeJS uses: actions/setup-node@v1 with: node-version: "12.x" - run: npm ci - run: npm run format:check - run: npm test -- --coverage env: CI: true - name: Build Project #ステージング用のビルドを行うステップ if: github.event_name == 'push' #pushイベントのときだけ実行 run: npm run build #ビルド実行 - run: npm install -g surge #surgeのインストール - name: Deploy to Staging #ステージングデプロイを行うステップ if: github.event_name == 'push' #pushイベントのときだけ実行 run: npx surge --project ./build --domain awesome-quiet.surge.sh #デプロイ env: #surgeの認証 SURGE_LOGIN: ${{ secrets.SURGE_LOGIN }} #surge whoamiで取得 SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} #surge tokenで取得developへのマージをトリガにするために、develop向けのpushイベントを追加します。
また、ワークフローは①と②で共有されているので、ビルドやデプロイがpull_requestイベントで実行されないように、if: github.event_name == 'push'という実行条件を追加します。今回、デプロイにはsurgeというnpmパッケージを使用しています。ログイン名とパスワード(token)を予め作成して、GitHubリポジトリのsecretsに環境変数として登録しておきます。
ワークフローの実行内容を確認すると、developへのプルリク時点ではbuild, deployステップを飛ばしています。
developへマージ(push)するとbuild, deployが実行されています。
dependencyのキャッシュ
ワークフローごとに
npm ci行うのは無駄なので、dependenciesのキャッシュ用のアクションをステップに追加します。
steps: - uses: actions/checkout@v2 - name: Cache node_modules uses: actions/cache@v1 with: path: ~/.npm #キャッシュしたファイルとキーを格納する場所(OSによって異なる) key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} #キャッシュのリストアや保存をするためのキー restore-keys: | #キャッシュをリストアするためのキーのリスト ${{ runner.os }}-node-
hashFilesを使用することで、depencencyに変更があったときに新しいキャッシュをつくることができます。最初のワークフローを実行すると、以下のようにキーが生成されます。
Artifact(テストカバレッジ、ビルドファイル)のアップロード
テストとビルドの後にGitHubのActionsタブからArtifactをダウンロードできるようにするため、これらをアップロードするアクションをステップを追加します。
ci.yml- run: npm test -- --coverage env: CI: true - name: Upload Test Coverage uses: actions/upload-artifact@v1 #artifactをアップロードするアクション with: name: code-coverage #ダウンロード時の表示名 path: coverage #アップロードするフォルダのパス - name: Build Project if: github.event_name == 'push' run: npm run build - name: Upload Build Coverage if: github.event_name == 'push' uses: actions/upload-artifact@v1 with: name: build path: buildワークフローを実行すると以下のようにArtifactがアップロードされます。
masterにマージされたときだけ実行するステップ
ワークフロー④(プルリクが承認されてdevelopがmasterへマージされたとき)のときだけ実行されるステップを作成します。
リリースの作成
masterにマージされたときにリリースを作成するステップを追加します。
リリースの作成にはsemantic-releaseを使います。ci.yml- name: Create a Release if: github.event_name == 'push' && github.ref == 'refs/heads/master' run: npx semanic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}また、リリースの画面からbuild.zip, coverage.zipをダウンロードできるようにします。
ci.yml- name: ZIP Assets if: github.event_name == 'push' && github.ref == 'refs/heads/master' run: | zip -r build.zip ./build zip -r coverage.zip ./coverageリリースの設定は以下のようになります。
release.config.jsmodule.exports = { branches: "master", repositoryUrl: "https://github.com/suzuki0430/react-app", plugins: [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", [ "@semantic-release/github", { assets: [ {path: "build.zip", label: "Build"}, {path: "coverage.zip", label: "Coverage"}, ], }, ], ], };codecovへのテストカバレッジのアップロード
codecovはコードのカバレッジを計測し、可視化したりslackに通知したりできる外部サービスです。
envでcodecovで作成したTOKENを指定するだけで、カバレッジレポートをアップロードすることができます。
ci.yml- name: Upload Coverage Reports if: github.event_name == 'push' && github.ref == 'refs/heads/master' run: npx code-coverage env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}Slack通知用のワークフローの作成
リリースが作成されたときにSlack通知を行うワークフローを作成します。
ci.ymlのCreate releaseステップをトリガにするため、CUSTOM_TOKENを新しくつくります。リリースの作成(published)をトリガにしたワークフローを以下のように作成します。
release.ymlname: Notify on Release on: release: types: [published] jobs: slack-message: runs-on: ubuntu-latest steps: - name: Slack Message run: | curl -X POST -H 'Content-type: application/json' --data '{"text":"New release ${{ github.event.release.tag_name }} is out, <${{ github.event.release.html_url }}|check it out now.>"}' ${{ secrets.SLACK_WEBHOOK }}おわりに
GitHub Actionsの使い方が大体わかったので、今度は社内業務で役に立つようなワークフローを作成してみたいと思います。
参考資料























