20220224のReactに関する記事は6件です。

ReactでGoogleログイン

前提 以前作成した下記記事の環境からスタート react-google-login yarn add react-google-login react-router-dom yarn add react-router-dom yarn add -D @types/react-router-dom ※ログインページからサイトトップページに移動したいため、react-router-domを導入 ログインページの作成 src > componentフォルダ > login.tsx import React from "react"; import { Link } from "react-router-dom"; export const Login = () => { return ( <> <h1>ログイン画面</h1> <Link to="/site">サイトへ</Link> </> ); }; サイトトップページの作成 src > componentフォルダ > siteTop.tsx import React from "react"; import { Link } from "react-router-dom"; export const SiteTop = () => { return ( <> <h1>Hello World</h1> <Link to="/">ログインへ</Link> </> ); }; app.tsxの編集 app.tsx import React, { FC } from "react"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import { Login } from "./component/login"; import { SiteTop } from "./component/siteTop"; const App: FC = () => { return ( <> <BrowserRouter> <Routes> <Route path="/" element={<Login />} /> <Route path="/site" element={<SiteTop />} /> </Routes> </BrowserRouter> </> ); }; export default App; react-router-domを使ってページを分けれるようにしておく /でログインページへ /siteでサイトトップページへ ここまでで確認 yarn startを実行し、ブラウザでlocalhost:8111を入力 ログイン画面とサイトへが表示されて、サイトへを押すとHello Worldが表示されればOK! Googleログインの導入 import React from "react"; import GoogleLogin from "react-google-login"; import { useNavigate } from "react-router-dom"; const CLIENT_ID = "作成したクライアントID"; export const Login = () => { const navigate = useNavigate(); const success = () => { navigate("/site"); }; const error = () => { navigate("/"); }; return ( <div> <h1>ログイン画面</h1> <GoogleLogin clientId={CLIENT_ID} buttonText="Googleでログイン" onSuccess={success} onFailure={error} cookiePolicy={"single_host_origin"} /> </div> ); }; 確認のために作成したlogin.tsxの<Link>は削除する ※クライアントIDが必要なため、作る必要があります。 Google Cloud Platform => APIとサービス => 認証情報 => 認証情報を作成 => OAuthクライアントID => ウェブアプリケーション => 作成 最後に 実際に作成したクライアントIDを入れる yarn startを実行し、ブラウザでlocalhost:8111を入力 Googleでログインというボタンを押し、ログインが成功、Hello Worldが表示されたらOK! 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

色変数の命名補助

ちょっとしたことですが カラーコード用の変数の命名に迷う 現在開発に携わっているプロダクトでは、カラーコードを格納する用の変数がこんな感じで定義されています。 variables.tsx export const clrWhite = '#ffffff' export const clrBlack = '#393939' export const clrGray = '#ACACAC' export const clrLightGray = '#FAFAFA' export const clrDarkGray = '#DDDDDD' export const clrBlue = '#468BE8' export const clrLightBlue = '#0CD7D7' export const clrOrange = '#FCA316' //以下ずっと続く 扱う色が増えてくると名前が枯渇しがちです。できれば名前を考える手間を省きたいところです。 やったこと https://github.com/meodai/color-names ↑のAPIをコールするシェルスクリプトを作成してnpm scriptで使えるようにしました。 手順 jqコマンドのインストール brew install jq 確認 $ jq --version jq-1.6 shellを書く 簡単なのでいいと思います。(というか簡単なのしか書けない) 引数で#なしのhexカラーコードを受け取り https://api.color.pizza/v1/hexValueWithout# にリクエストを送って色名の部分だけ出力するという内容になっています。 colorname.sh #!/bin/sh RES=$(curl -sX GET "https://api.color.pizza/v1/$1") echo $RES | jq ".colors[0].name" package.jsonのscriptsにコマンド追加 package.json "scripts": { "colorname": "bash ./colorname.sh" } 使い方 たとえば #f5564c の色名が欲しい場合 $ npm run colorname f5564c とすると "Pumping Spice" と返ってきます。なのでclrPumpingSpiceみたいな変数名にすればいいというわけです。 デメリット 変数名から色が推測しにくい。 これは変数の命名としてはかなり致命的ですが、カラーコードを変数に格納する一番の理由は、そもそもカラーコードをベタ書きすることを防ぐこと(だと思ってる)なので、色変数に関してはわかりやすさは二の次でいいのではと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

touchやwheelのイベントリスナーにpassive: trueを明示的に指定する必要があるのか調べてみた

これは何 EventTarget.addEventListener() #パッシブリスナーによるスクロールの性能改善 - Web API | MDN にあるように、以下のようなイベントに対してイベントリスナーを設定すると、スクロールの処理性能が大幅に低下する可能性があります。 touchstart touchmove wheel mousewheel それに対して、passiveオプションをtrueで設定するとこの問題を防ぐことができるようですが、一部のブラウザではデフォルトでpassive: trueになっているということで、毎回明示的に設定する必要があるのかどうか調べてみました。 結論 ChromeやFirefoxではデフォルトでpassive: trueになっている (window, document, document.bodyが対象のとき) EventTarget.addEventListener() # ブラウザーの互換性 - Web API | MDN MDNの互換性の表ではNoになっているmacOS Safari(15.3)でも touchイベント: Safari 11.1でデフォルトpassiveになっていそう (実機で試してはいない) Safari 11.1 wheelイベント: 「実験的な機能」の中に、Wheel Event listeners on the root made passiveがあり、デフォルトオンになっている React17でイベントハンドラを設定する場合もデフォルトでpassive: trueになっている Keep onTouchStart, onTouchMove, and onWheel passive by gaearon · Pull Request #19654 · facebook/react ただし、逆にpassive: trueにする方法がないため、これらのイベントに対してe.preventDefault()をすることができない React 18 not passive wheel / touch event listeners support · Issue #22794 · facebook/react という前提で必要なときに指定する passive: trueについて touchやwheelイベントはイベントリスナーを設定すると、スクロールをする前にスクロールをブロックする処理がないかを確認するため、非同期のスクロールがブロックされてスクロールがカタつくという、いわゆるScroll Jankが発生します。1 それを防ぐために、スクロールをブロックする処理 (e.preventDefailt()) がない場合はpassive: trueというオプションをイベントリスナー設定時に付与してあげることでScroll Jankを防ぎ、スクロールをスムーズに行うことができるようになります。 ブラウザデフォルトの挙動について 実はこのtouchやwheelの一部イベントのpassive: trueオプションですが、ChromeやFirefoxではデフォルトでpassive: trueになっています。2 (対象はwindow, document, document.body3) しかしMDNの互換性の表を見てみると、Safariはデフォルトがtrueになっていないようです。 Safariでの挙動について macOS Safari version: 15.3 実際に以下のようなコードを実装し、Safariで動作確認をしてみます。 document.addEventListener('wheel', (e) => { e.preventDefault() console.log('handler called') }); 確認をしてみると、スクロールは無効化されず、普通にスクロールができてしまいました。 次に明示的にpassive: falseを渡してみます。 document.addEventListener('wheel', (e) => { e.preventDefault() console.log('handler called') }, { passive: false }); すると予想通りスクロールを無効化することができました。 実はSafariの「実験的な機能」の中に、Wheel Event listeners on the root made passiveがあり、オンになっていることでこのような挙動になっていたようでした。これはSafari15.3ではデフォルトでオンのようです。 (いつからこうなっているのかまでは調べきれませんでした) 試しにオフにしてみると、最初のコードでもスクロールが無効化されました。 また、touchイベントも、Safari 11.1からデフォルトでpassive: trueになっているようです。4 Reactでの挙動 React 17ではこちらのissueでデフォルトの挙動について議論がされた後、passive: trueがデフォルトになったようです。 対象のイベントハンドラは、onTouchStart, onTouchMove, onWheelです。5 ただ、passive: trueになったのはいいのですが、現時点(react@17.0.2)ではこのpassiveを操作できるオプションが存在していないようで、逆にpassive: trueにすることができない問題もあるようです。これにより、onTouchStart, onTouchMove, onWheelのイベントハンドラ内ではe.preventDefault()でイベントをブロックできないようです。 const handler = (e) => { e.preventDefault() // スクロールを止めることはできない console.log('handler called') } const App = () => { return ( <div onWheel={e => handler(e)}> <Contents /> </div> ); } これについては、issueも存在している(React 18 not passive wheel / touch event listeners support · Issue #22794 · facebook/react)ようなので、今後対応されるかもしれません。 まとめ ChromeやFirefoxではデフォルトでpassive: trueになっている (window, document, document.bodyが対象のとき) EventTarget.addEventListener() # ブラウザーの互換性 - Web API | MDN MDNの互換性の表ではNoになっているmacOS Safari(15.3)でも touchイベント: Safari 11.1でデフォルトpassiveになっていそう (実機で試してはいない) Safari 11.1 wheelイベント: 「実験的な機能」の中に、Wheel Event listeners on the root made passiveがあり、デフォルトオンになっている React17でイベントハンドラを設定する場合もデフォルトでpassive: trueになっている Keep onTouchStart, onTouchMove, and onWheel passive by gaearon · Pull Request #19654 · facebook/react ただし、逆にpassive: trueにする方法がないため、これらのイベントに対してe.preventDefault()をすることができない React 18 not passive wheel / touch event listeners support · Issue #22794 · facebook/react という前提で必要なときに指定する 参考 MDN EventTarget.addEventListener() #パッシブリスナーによるスクロールの性能改善 Chrome Making touch scrolling fast by default  |  Web  |  Google Developers Safari Safari 11.1 218842 – Force wheel event listeners on the root to be passive React Touch/Wheel Event Passiveness in React 17 · Issue #19651 · facebook/react Keep onTouchStart, onTouchMove, and onWheel passive by gaearon · Pull Request #19654 · facebook/react React 18 not passive wheel / touch event listeners support · Issue #22794 · facebook/react https://dom.spec.whatwg.org/#observing-event-listeners ↩ https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7 ↩ https://developers.google.com/web/updates/2017/01/scrolling-intervention ↩ https://developer.apple.com/library/archive/releasenotes/General/WhatsNewInSafari/Articles/Safari_11_1.html ↩ https://github.com/facebook/react/pull/19654/files ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

素人がまとめた Reactのいろは(React17)

はじめに Reactは初心者です。チュートリアルしか触ったことありません。 基本はサーバサイドの仕事してます。 Reactの設計を考える必要が出てきたものの、ネット上は情報が散乱しててよくわからないので、個人的にまとめてみたものになります。 「これから取り組むにあたって、整理したメモ」です。実践してみた結果ではありません。(要は机上の空論なので間違ってるものもあるかもしれません) ぜひ、ご経験者のかたいれば、ご意見いただけると助かります。 「べき」とか書いてる部分もありますが、「あくまで自分に向けての表現」であって、「誰にとってもやるべき」という意味合いでは無いです。 当たり前ですが、PJ前提が異なれば、適用する考え方も変わってくると思いますので。 絶対に読むべきもの 公式:https://ja.reactjs.org/docs/thinking-in-react.html 何はともあれ公式を見るべき 書籍:https://oukayuka.booth.pm/ プロジェクトメンバーでReactに初めて関わる人は全員読んでもらうべきだと思うくらい良書。 最新のテクニックが整理されている上、過去の経緯も含まれて丁寧に説明されているので、これを読めばWEBの情報で変な情報を掴まずにすみそう。 当然この記事に書いてないような情報が本当に盛り沢山! 全部で3部(+1部)あります。全部読もう! https://oukayuka.booth.pm/items/2368045 https://oukayuka.booth.pm/items/2368019 https://oukayuka.booth.pm/items/2367992 https://oukayuka.booth.pm/items/1312815 記事の前提 React: v17 想定PJ規模: そこそこ大規模 Reactのいろは コンポーネント設計のいろは 原理原則・考え方(およびその例) 単一責任の原則・関心の分離 コンポーネント指向:1コンポーネントは単一の責任を持つ(複数の責任を持たない)。 コンポーネントは小さく保つ。 親の状態・子の状態に依存しないようにする。(やむを得ない場合はstateという手段がある。くらいのスタンスが良さそう。) 再利用性 可能な限りprops/stateは少ない方が基本的には再利用性は高い。 可変にすることで再利用性が高まる内容にとどめてprops/stateを用いる。 適用方法としての基本スタンス 最初のコンポーネント設計 大前提として、最初から完璧な設計を目指すのは避ける。実装の中で微調整が確実に生まれるため、最初はドラフト版くらいの感覚で作る。 設計の最初の立脚地点としてAtomicDesignの適用から考えていくのはわかりやすい。 ただし、そのまま適用は苦しむことが多いらしい。下記の「重要な考え方」に反してまで形に拘るのは避けるのが良さそう。 一番最初はみんなでワーワー話しながらベースラインを決めるのが良さそう。 Atomic Designから学ぶべき「重要な考え方」 コンポーネントの分け方 コンポーネント分割の基準が一定であり、わかりやすいこと。 プロジェクト内で共通認識が持てれば良い。AtomicDesignはあくまで一例、こだわる必要はない。 デザイナーと協業する案件の場合、デザイナーとデベロッパーで共通認識を持てる基準であること。 何かしらのメタファーを用いて、わかりやすい名前を見つけられるとテンションが上がる。 AtomicDesignだと科学だが、音楽(ハーモニー、メロディ、リズム)を採用しているプロジェクトもある。https://logmi.jp/tech/articles/300657 再利用を想定した分割/設計であること。 再利用しないものを過度に分割する必要はない。 可読性を維持するため、階層を深くしすぎない。 AtomicDesignは5階層。できればそれより少ない階層の方がわかりやすいはず。 ロジックをどの層に実装するかを事前に決めておく PresentationComponent/ContainerComponentパターン:https://www.yuuniworks.com/blog/2018-05-18-presentational-component%E3%81%A8container-component/ まずは静的に表示できる範囲のものを作成する=PresentationComponent 動作については別で定義する=ContainerComponent もし親子関係を定義するならPresentationComponentが親だと思う(けど、Presentationが子って記事も見かけた) 宣言的ということを考えるなら、Presentationが親の方が理解しやすい気がするけど・・・ コンポーネント設計の育て方 「Reactの流儀」をベースとして最初の実装および設計の調整を行う モックから始めよう Step 1: UI をコンポーネントの階層構造に落とし込む Step 2: Reactで静的なバージョンを作成する Step 3: UI 状態を表現する必要かつ十分な state を決定する Step 4: state をどこに配置するべきなのかを明確にする Step 5: 逆方向のデータフローを追加する 「3度目の法則」によるリファクタリングを行う 3度同じことを書くことになったタイミングでリファクタリングを行う。 再利用性を無駄に事前検討しなくて良いのが利点。 必要になった時に、再利用可能な形でリファクタリングする。 コンポーネント設計を見直すタイミングを作っておく 最初に考えたものはドラフト版。 どのタイミングが適切かはプロジェクトによるのでなんともいえない。 ディレクトリ構造について クックパッドのノウハウ:https://note.com/tabelog_frontend/n/n07b4077f5cf3 抽象度や依存の有無に応じてディレクトリをわける 特定画面のみで使うのか、複数画面をまたいで使うのか? Entryポイントを独立させるのは保守性高そう。 ドメインが入るか入らないかを意識してディレクトリをわける 単なるUIパーツなのか、動作や意味合いが含まれるコンポーネントなのか? bulletproofのノウハウ:https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md 読んでみてとしか言いようがない。世界的なノウハウが溜まってる物なので、これをベースに考えるのが良さそうな気がする。 実装における基本スタンス 宣言的な実装にこだわりたい(Reactの流儀Step3〜5):https://qiita.com/erukiti/items/fb7bcbd9d79696579d06 Reactに限った考え方ではなく、宣言的な実装はメインストリームになってきているため、考え方はシフトしていくべき。 宣言的とは、ソースコードの処理の流れを追わなくても、結果の状態が推測できるようになっていること。 基本的にはReact構文に従えば書き方自体は自動的にそうなるはず。 ただし、変数名から関連性がわからず、結果の状態が推測しにくくなることはありえる。 また、if文の乱用でもわかりづらくなりやすい。 レビュー観点としてこの観点を持っておいてもいいかもしれない。 例えば、分岐の多い表示処理は結果状態を想定しづらいので、コンポーネントを分割するか、Compound Componentを用いる。 あまり多用すると単一責任の原則から離れてしまいそうなので、上手く使うこと。 https://qiita.com/teradonburi/items/6828635d2e70dba6637d#compound-component%E3%81%A7%E5%88%86%E5%B2%90%E6%9D%A1%E4%BB%B6%E3%82%92%E9%9A%A0%E8%94%BD%E5%8C%96%E3%81%99%E3%82%8B stateは必要十分なだけ使う。(Reactの流儀Step3) まずは使わずに済む方法を考える。 次に、propsで代用できないかを考える。 「props drilling」など、シンプルなpropsでの実装では問題が生じる場合は、hooksのuseContext/useReducerの適用を考える。(これが実質的なstateの適用にあたる?) stateが必要になる場合、適用範囲を最小限に絞る。 単一責任の原則および性能の観点:コンポーネント分割を前提に、stateで影響するコンポーネントを減らしてもいいかも。 単一責任の原則および性能の観点:useMemoを乱用は避けたいが、多少の利用なら許容する。 state管理が複雑になってきた場合はReduxの導入を検討する。 VoidFunctionComponentを使おう。(Reactの流儀Step5を実現しやすくする?) propsがchildrenにアクセスできないようになっている。(より厳密なコンポーネント指向が実現しやすい)(v18でFunctionComponentから廃止予定のため、早めに対応できるようにするための存在) 型指定を強制できるため静的解析でエラー検知できるようになり、一定の開発に秩序をもたらしやすい。 Presentational Component / Container Component(Reactの流儀Step1〜2と3〜5を分けて考える) 「表示」と「動作」を分けて記載する方が望ましいという考え方。 「コンポーネント分割」での分離をまずは考える。(stateなどが絡みそうなら「React Hooks」の導入かな・・・?) 表示:「どのように見えるか」に関心を持つ。データや振る舞いを変更しない。(JSX(.jsx/.tsx)で構成すれば良さそう?) 動作:「どのように動作するか」に関心を持つ。データや振る舞いを変更する。(Typescript(.ts/.tsx)で構成すれば良さそう?) 古いテクニック:ググってこれらが出てきてもすぐに飛びつかないこと クラスコンポーネント → 今は「関数コンポーネント」が強く推奨されている。(徐々に推奨の強さが上がってるように感じる) ライフサイクル → v17から大きく変わっているため、古い情報かどうか確実に確認すること。 mixin/HOC/render props/Recompose → 今は「React Hooks」が強く推奨されている。 PresentationalComponent/ContainerComponent → 今は「React Hooks」が強く推奨されている。が、基本的な考え方(表示と動作の分離)はまだ活用可能。 Redux → 「React Hooks」との使い分けないしは共存のさせ方が議論されている。hooksに触れてないReduxの情報は避けた方がいいかも。 ContextAPI → 「React Hooks」との使い分けないしは共存のさせ方が議論されている。hooksに触れてないReduxの情報は避けた方がいいかも。 全般的に → 「React Hooks」登場以前と登場以降で結構考え方が変わってそうなので、hooks絡めるとどうなるんだろう?みたいな考え方は持っておいた方が良いかも。 掘り下げた方が良さそうなこと v18機能 https://qiita.com/uhyo/items/bbc22022fe846fd2b763 平行レンダリング機能 サスペンスを使ったストリーミングサーバレンダリング 注意事項 全てを厳密に適用しようとすると、相反する要求が発生する場面がある想定。その場合は、PJの状況に応じて判断する。 テストのいろは 全世界のノウハウ。といっても簡潔にまとまってるので、ひとまず見てみよう。 React Testing Libraryは公式でサポートされるくらい有名なライブラリ。 これを使わない手はない。 (使い方はちょっと読み取りきれなかったけど、使うのは確定で良さそう。) 基本はレイアウトに関するテストのように見える。 どちらかというと途中から導入するケースの話がメイン。ただ、単体テストの位置付けは参考にできそう。 フロントエンドでTDDはほぼ不可能。(実装前に正しい振る舞いを定義することが困難というか、実装しながら設計/調整するようなのが基本。) 単体テストは、今後の修正が入った時に防衛的に機能するためのものとして作るスタンスが妥当。 UXのテストは単体テストでは評価できない。ただ、UXに影響する変数は関数側に定義されているべき。 単体テストを途中から導入する場合、不要な依存が切れて、モジュールの境界面が自明になった状態にたどり着くことが最初のゴール。 React Testing Libraryを使ってテストできるところはテストすればいいが、限界はありそうな予感。 この記事以外に事例を見つけられなかった(検索能力の問題か・・・)のと、いいねが少ないのが気になる。 何かテストの実装で苦しむ場面あれば思い出して見返してもいいかもしれない。 おまけ 事例 「このリポジトリの目標は、Reactアプリケーションを開発する際のリソースとグッドプラクティスのコレクションとして機能することです。」 という海外のリポジトリの紹介をしてくれているページ。かなりわかりやすいので読むのが良さそう。 その上で、その先のリポジトリも一読しておいた方が良い。 SlackのようなチャットツールをSPA(シングルページアプリケーション)で作った場合の事例。(ヘイシャでした) (今回自分のPJとはちょっとアプリの性質が異なるが、ある程度参考になる部分はありそう) 「おてつたび」さんの事例。 「wantedly」さんの事例 デザインのための仕組みづくりみたいなニュアンス。 Atomic Designについて 公式:https://atomicdesign.bradfrost.com/ 一番わかりやすかった日本語記事:https://uxdaystokyo.com/articles/glossary/atomic-design/ Atomic Design自体はデザインの考え方であって、実装の世界の話ではない。実装の世界に当て嵌めようとすると苦しみが多いという話が多かった。 でも、頑張って適用してるPJも、卒業してるPJも言ってることは一緒だった。 「開発者とデザイナーが同じ基準で会話するための土台の考え方を合わせる」 「メンテナンス性を高めるために、一定の基準・考え方でコンポーネントを分割し、適切なフォルダ構成を用意する。」:何が適切かはプロジェクトによって異なる→Reactの流儀を適用しながら見つけていくしかなさそう いくつかの事例 sansanさんの例(Atomic Design導入タイプ):https://buildersbox.corp-sansan.com/entry/2022/01/06/110000 食べログさんの例(Atomic Design卒業タイプ):https://note.com/tabelog_frontend/n/n07b4077f5cf3 OHEYAGOさんの例(Atomic Design卒業タイプ):https://tech.ga-tech.co.jp/entry/react-give-up-atomic-design Atomic Component:https://qiita.com/kahirokunn/items/b599d2cf04d2580c412c Atomic Componentは、Atomic Designの進化系と表現されているケースもあったけど、見た限りは、適用事例の一つのように感じた。 実践:https://b3s.be-s.co.jp/programming-language/javascript/2989/ 個人的な感想 小さくてシンプルな案件ならそのまま適用でもいいかも? ある程度の規模以上は、たたき台として用いて、すぐに卒業するくらいの使い方が良さそう。 細かいテクニック 実装テクニック setStateメソッドは、2種類の指定の仕方(便宜上、絶対値設定と、相対値設定とでも呼ぶか?)を使い分けよう 三項演算子よりもnullish coalescingがスマートな場合がある {} unknown null は使い分けたい(他にもありそう SyntheticEvent:元々のイベントを抑制して、自身のイベントを優先して動かす。 実装テクニック集 https://react-typescript-cheatsheet.netlify.app/ https://tech.uzabase.com/entry/2020/03/31/081351 https://qiita.com/baby-degu/items/ea4eede60bbe9c63a348 Javascriptを読みやすくするテクニック:https://github.com/ryanmcdermott/clean-code-javascript 命名チートシート:https://github.com/kettanaito/naming-cheatsheet cssテクニック集 https://blog.uhy.ooo/entry/2020-12-19/css-component-design/ https://github.com/alan2207/bulletproof-react/blob/master/docs/components-and-styling.md 性能テクニック集 https://qiita.com/teradonburi/items/5b8f79d26e1b319ac44f さいごに いやー、情報量が多い。 ひとまずこれをベースに始めてみようかしら。 実践した結果は余裕あれば記事にします。 ご意見・ご指摘あれば是非是非!!! 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React による シングルページWebアプリケーション (SPA) の開発

demoru.net でのサービス公開イメージ demore.net とは、弊社で提供している SPA 体験サイトです。 SPA とは何か? (アプリ利用者の目線) Actor (アプリ利用者) にとって シングルページWebアプリケーション (以下 SPA) とは、いくつかの JavaScript ファイルと css ファイルで構成されている、ブラウザ上で動作するアプリです。 SPA は通常、ユーザ認証を行なってユーザを特定し、Web API を用いたさまざまな情報サービスを提供します。サーバサイドのデータベースやディスクスペースを利用することもありますが、これらのデータアクセスも例外はありますが Web API を用いています。 SPA ではない、従来のサーバサイドの Webアプリ は、ボタンやリンクをクリックするごとに以下の動作を繰り返します。 ブラウザはサーバに情報取得要求 (リクエスト) を送出する (ex. 情報提供サーバに japan の covid-19 新規感染者数の日別データを要求する) ブラウザはサーバからの応答 (レスポンス) を待つ ブラウザはレスポンスを受信したらレスポンスに含まれる情報をブラウザ上に描画する 通常この描画はブラウザ全体を再描画する動作を伴う (ex. japan の covid-19 累計感染者数の時系列グラフを表示する) ブラウザとサーバのやりとりは、このリクエストしてレスポンスを待ち、再描画するという「ギッコン、バッタン」スタイルで進んでいきます。 SPA は、必要なアプリケーションとデータを可能な限りまとめてブラウザに渡します。アプリの操作や、他のユーザからの操作によってブラウザ画面上の情報を変更する場合は、変更が必要な箇所だけを再描画する仕組みをもっています。「ギッコン、バッタン」スタイルから解放されることから UX は向上します。 SPA とは何か? (開発者の目線) いにしえの昔、SPA は javaアプレット や Adobe Flash などのブラウザプラグインに依存していましたが、現在は JavaScript を用いた実装になっています。2014年にオライリージャパンから「シングルページWebアプリケーション」(ISBN978-4-87311-673-0)という書籍が発行され、フロント (ブラウザ) 側だけでなく、バックエンド (サーバ) 側の Webサーバ (Node.js) とデータベース (MongpDB) もすべて JavaScript で実装するというアプローチが紹介されました。 しかしながら JavaScript でアプリを実装するというのは、小規模なものであれば実現可能でも、多機能かつ大規模なアプリ開発では非現実的と考えていました(個人の感想です)。しかし次のスタックを用いることによって、高品質なアプリをカジュアルに、なおかつ楽しんで開発できるようになりました。 React + redux tool kit + TypeScript による開発 React Facebook (現在 Meta) 社がつくってくれた JavaScript ライブラリで、2013年にオープンソースになりました。 コンポーネント単位での実装 とかく複雑になりがちな UI 実装を、独立したコンポーネント単位に分割することができるようになり、地方分権を容易にしてくれた コンポーネントでは JSX という簡易 JavaScript 構文を使用することができ、複雑な JavaScript と同等の実装を HTMLタグのように簡潔に記述できるようになった React コンポーネントは JavaScript で実装することも可能だが、JSX で実装することによりプログラムの可読性が大きく向上する フック (Hooks) の採用 フックとは何か? は後述 React には組み込みのフックが用意されており、定番の処理を React らしく実装できる サードパーティが提供するフックが多く存在するので、難しい実装はこれらに任せることが可能になる カスタムフックの実装も容易でかつ実用性が高い 仮想 DOM の採用 React は 仮想 DOM を更新し、実際の (ブラウザの) DOM に対しては仮想 DOM との差分のみを反映することにより、効率のよい画面描画を実現している フック (Hooks) とは何か? フックは Reactコンポーネント をクラスを使わないで実装するための機能で、React 16.8 (2019年公開) より利用できるようになった。React コンポーネントの実装には、クラスコンポーネントと関数コンポーネントという2つの実装方式がある。Reactコンポーネントは、関数コンポーネント + フック を採用する方が容易でかつ簡潔に実装できる。 redux tool kit (RTK) とても有用なライブラリであるが「アプリケーションの状態管理のためのライブラリ」というように簡単に紹介されてしまうことが多い。Reactフック を用いた実装になっており、2019年に公開されている。 state (ある状態、ステータス) を保持できる (ex. ログイン済、未など) 各コンポーネントは useSelectorフック を使って state の値を参照できる 各コンポーネントは useDispatchフック を使って state の値を更新できる state の値は Redux Dev Tools でブラウザからいつでも参照することができる RTK のおかげで複雑なデータ構造を持つ大規模アプリケーションであっても、データの見通しが悪くなることなく、快適かつ安全に開発を進めることができる。 TypeScript TypeScript は、JavaScript の型定義を厳密にする「型付け」に特化した JavaScript のスーパーセット (JavaScript を拡張した言語) である。 TypeScript を用いることによって、コーディングにかかる工数は若干増加するものの、コーディングアップ時の品質が向上するので、トータルでの導入効果は高く「使わない手はない」環境といえる。ちなみに Web 上で見かけるサンプルコードも、TypeScript のものが圧倒的に品質が高い。 TypeScript は Microsoft社が開発した言語であるためか、VSCode との相性が良く、両者を採用することで最適な開発者体験が得られている。 開発環境イメージ 開発者のローカル環境に、プロジェクトごとの Dockerコンテナ を立てて開発している イメージは node:current-slim という node.js 公式イメージを使用している docker-compose.yml は後述 プログラムコードは docker volume を使ってコンテナからローカルディスクをマウントした領域に配置している このマウントした領域のフォルダをポイントして VSCode からコードを編集している このマウントした領域は GitHub でホストされているリモートリポジトリになっている ex. docker-compose.yml $ pwd /Users/r/Docker/unrwebpage $ cat docker-compose.yml version: '3' services: node: image: node:current-slim container_name: 'unr_webpage' hostname: 'unr_webpage' ports: - '3001:3001' stdin_open: true tty: true working_dir: '/usr/src/app' volumes: - ./app:/usr/src/app networks: - dumynet networks: dumynet: external: true $ 開発環境の構築手順 Mac (Intel chip) での開発に必要なツールのインストール Docker container を用いた React + Redux Toolkit + TypeScript Webフロントエンド開発環境の構築 開発スタイル 現在は以下のスタイルで開発しています slice をつくる コンポーネントをつくる テストする まず slice をつくる コンポーネントより先に slice をつくります。 slice とは state と、以下に示す reducer を用途 (ex. ログイン機能) ごとにまとめたものになります。 slice をつくることにより、以下が明確になります。 slice の名称 (ex. auth) state の構造 state の名称やデータ形を interface として定義することが多い (ex. ユーザID, ロール, displayName, アバター画像のURL など) state の初期値 reducer の定義 reducer とは コンポーネントから受け取るデータと、state を更新する論理を action (ex. login) として定義したもの コンポーネントは useDispatchフック を使って action を呼び出す コンポーネントから参照可能な state の定義 コンポーネントは useSelectフック を使って state を参照する 現実の開発スタイル slice からつくって、次にコンポーネントをつくり、結合してテストする、というスタイルは、小人数で開発する際のスタイルだと思っています。現実には slice で定義されるデータ構造と action をドキュメント等に定義し、この定義に従って、 同時に開発する slice はダミーコンポーネント (弊社で dumymain と呼んでいるもの) と結合してテストする コンポーネントはモックと結合してテストする 単体でテスト済みの slice とコンポーネントを結合してテストする という流れになるんだと思っています。 成果物 demoru.net 組織 unremoted.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React + Styled-components(CSS )

簡単な動きのあるローディング作成 以前作成した下記記事の環境からスタートします。 リセットCSS import React from "react"; import { Reset } from "styled-reset"; const App = () => { return ( <> <Reset /> <h1>Hello World</h1> </> ); }; export default App; yarn add styled-reset 今回は、Reactで使えるリセットCSSを使用 ※デバイスやブラウザによってデフォルトのスタイリングがついてしまうので、忘れないためにも記載してます。 styled-components yarn add styled-components @types/styled-components app.tsx import React from "react"; import { Reset } from "styled-reset"; import styled from "styled-components"; const App = () => { return ( <> <Reset /> <StyledHello>Hello World</StyledHello> </> ); }; export default App; const StyledHello = styled.h1` font-size: 50px; text-align: center; `; yarn startを実行しHello Worldが真ん中に表示されていたらOK! - <h1> → <StyledHello>(自分で命名可能) - styled.の後に要素を指定 - バッククウォートで囲った中にCSSを記入 ローディング import React from "react"; import { Reset } from "styled-reset"; import styled, { keyframes } from "styled-components"; const App = () => { return ( <> <Reset /> <StyledHello>Hello World</StyledHello> <Load>Loading</Load> </> ); }; export default App; const StyledHello = styled.h1` font-size: 50px; text-align: center; `; const opacityLoad = keyframes` 0% { left: 0; opacity: 0; } 10% { left: 10%; opacity: 0.5; } 30% { left: 30%; opacity: 1; } 50% { left: 50%; opacity: 1; } 70% { left: 70%; opacity: 0; } 100% { left: 100%; opacity: 0; } `; const Load = styled.p` animation: ${opacityLoad} 2.3s linear infinite; font-size: 50px; position: relative; width: 300px; `; yarn startを実行し、左から右に動くLoadingが出ていればOK! - styled-componentsから{ keyframes }を追加(アニメーション) - 透明度をopacityLoadに記載して、LoadにopacityLoadを追加 最後に styled-componentsを導入することで、HTMLとCSSの要素を1つのファイルで見れるのは、非常に管理しやすいです。また、HTML,CSSよりもクラス名などで困ることが少ないのもメリットかなと思います。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む