React周りの技術情報収集
React導入時に検討するフレームワークやライブラリ
フレームワーク
公式ドキュメントに最初からNext.jsやRemixなど選ぶことが記載されている。
状態管理
Reduxが有名だが、MetaがRecoilを出している。(Recoilはまだver1はでてない)
参考にした記事
UIコンポーネント
採用するコンポーネントによってはRSCが使えないので注意が必要。
RSCはだめだが、SSRは使えるとかもあるので、そのあたりは要件・設計次第化も。
CSS
- CSS Modules
- CSS-in-JS
- zero runtime CSS-in-JS
UIコンポーネントによっては、内部で使っているものがあるので、依存ができてしまう。
例えば、MUIはemotion使ってるので、CSS-in-JSが含まれてしまう。
2024/01現在、CSS-in-JSはRSCでサポートされていない。
RCCでchakra-uiやMUIがサポートされていて、emotionをサポートしようとしている。
RSCだとCSS ModulesやTailwind CSSなどの仕様が必要。(そのうちRCCだけのものも、RSCサポートするかも)
ReactだとCSS-in-JSは非推奨とか?(ちゃんと公式読む
Next.jsだとTailwind CSSが名指しかつ一緒にインストールできるため推奨?
最近は、Panda CSSとかも流行ってるみたい。
参考
ベストプラクティスに関する記事(どれがベストではなく、情報としてまとめてるもの)
Next.jsチュートリアル
React Foundations
Reactの概要
アプリを作るときには下記のようなことを考慮しないといけない
User Interface - how users will consume and interact with your application.
Routing - how users navigate between different parts of your application.
Data Fetching - where your data lives and how to get it.
Rendering - when and where you render static or dynamic content.
Integrations - what third-party services you use (for CMS, auth, payments, etc.) and how you connect to them.
Infrastructure - where you deploy, store, and run your application code (serverless, CDN, edge, etc.).
Performance - how to optimize your application for end-users.
Scalability - how your application adapts as your team, data, and traffic grow.
Developer Experience - your team's experience building and maintaining your application.
引用元:https://nextjs.org/learn/react-foundations/what-is-react-and-nextjs
Reactは上記のUser Interface部分にあたる。
NextはReactのフレームワークであり、UI以外の部分を提供する。(全部ではない)
-
Babel
- JavaScriptコンパイラー。JSXはそのままの記法だとブラウザが解釈できないので、JSX->JavaScriptにしてくれる。Babel自体はJS(新しい記法)->JS(古い記法)などのようにJSを変換してくれる役割があり、JSXにも対応しているという感じ
Next.jsのインストール
- page.js
- index.jsに代わるメインページ
- layout.js
- メインのレイアウト。すべてのページで共有されるUI要素(ナビゲーション、フッターなど)を追加する
- titleやdescriptioなどもmetadataとしてここにある
Server and Client Components
RSCとRCCを考える場合、下記2つの概念を理解しておくとよい。
- The environments your application code can be executed in: the server and the client.
- The network boundary that separates server and client code.
サーバー コンポーネントがレンダリングされた後、 React サーバー コンポーネント ペイロード (RSC)と呼ばれる特別なデータ形式がクライアントに送信されます。RSC ペイロードには以下が含まれます。
- サーバーコンポーネントのレンダリング結果。
- クライアント コンポーネントをレンダリングする場所のプレースホルダー (またはホール) と、その JavaScript ファイルへの参照。
React はこの情報を使用してサーバー コンポーネントとクライアント コンポーネントを統合し、クライアント上の DOM を更新します。
パフォーマンス向上のためにサーバーコンポーネントがデフォルトとなっている。
サーバーコンポーネントとクライアントコンポーネントの使い分けが必要。
クライアントコンポーネントはUIをインタラクティブにする必要があるコンポーネントに使用する。
サーバーコンポーネント・クライアントコンポーネントを選択するチェックポイントは下記。
各記事でリンクされている記事で後で読む
Next.jsのサーバーコンポーネントとクライアントコンポーネントを使用する際の設計検討パターン
CSS-in-JS
SSRとRSCの違い
SSRとRSCは違うので、RSCをサポートしていないUIコンポーネントもSSRだと可能なことがある。
例えばMUIだと下記の情報がある。
設計時に考慮する必要がありそうなこと
プロジェクト(ディレクトリ)構成
- features
- components
- atomic designでフォルダ分けする方式とか
- atomic desginの分類にこだわらず、コンポーネント主体で分類する場合こちら
- 属性として似たものを分類
Next.jsチュートリアル
CSS Style
-
Tailwind CSS
- Nextインストール時に選択できる
-
CSS Modules
- 従来CSSと同じ、もしくはJSXから分離したい場合
-
clsx
- https://www.npmjs.com/package/clsx
- 状態や条件によって、クラス名を切り替えられるライブラリ。nextのチュートリアルで紹介している
import clsx from 'clsx';
export default function InvoiceStatus({ status }: { status: string }) {
return (
<span
className={clsx(
'inline-flex items-center rounded-full px-2 py-1 text-sm',
{
'bg-gray-100 text-gray-500': status === 'pending',
'bg-green-500 text-white': status === 'paid',
},
)}
>
// ...
)}
- その他の方法
- Sass(.scss)
- CSS-in-JS ライブラリ
- styled-jsx
- styled-components
- emotion
Next自体はCSS-in-JSライブラリについて、推奨・非推奨みたいな書き方はしていない?
→チュートリアルから、個別ページに飛ぶと注意書きがある。
→RSCでサポートしていない旨の注意書き。
Fontと画像の最適化
Font
Fontを最適化する理由はパフォーマンスに影響を与えるから。
フォントは Web サイトのデザインにおいて重要な役割を果たしますが、プロジェクトでカスタム フォントを使用すると、フォント ファイルをフェッチしてロードする必要がある場合にパフォーマンスに影響を与える可能性があります。
また、Cumulative Layout Shiftという考え方があり、Fontを取得しに行ってから、再レンダリングするとフォントがオッ変わることにより、レイアウトが変わってしまいCLSに影響を与えてしまう。
Next.jsでは、ビルド時にフォントをダウンロードして、他の静的アセットともにホストする。
これにより、フォントを取得しに行く処理がなくなるため、パフォーマンスに影響を与えない。
画像
Next.jsでは、画像などの静的アセットは/publcで提供する。
アプリケーション内部から/publcに配置したリソースにアクセスするには、下記のようにアクセスできる。
<img
src="/hero.png"
alt="Screenshots of the dashboard project showing desktop version"
/>
ただし、上記のように使用する場合、自分で画像の最適化処理(下記のようなこと)を考えないといけない。
画像がさまざまな画面サイズで応答することを確認します。
さまざまなデバイスの画像サイズを指定します。
画像の読み込み時にレイアウトがずれるのを防ぎます。
ユーザーのビューポートの外にある画像を遅延読み込みします。
画像の最適化自体が専門分野のようなものなので、自分で対応する代わりに、next/imageコンポーネントを使用すると自動的に最適化してくれる。
<Image>コンポーネントは、<img>の拡張で下のような画像最適化を自動で行ってくれる。
画像読み込み時に自動的にレイアウトがずれるのを防ぎます。
小さなビューポートを備えたデバイスに大きな画像が送信されるのを避けるために、画像のサイズを変更します。
デフォルトで画像を遅延読み込みします (画像はビューポートに入るときに読み込まれます)。
ブラウザがサポートしている場合、WebPやAVIFなどの最新の形式で画像を提供する
classに"hidden md:block"と書くと
- 基本hidden
- デスクトップ(表示サイズがmd:ミディアム以上)はhiddenさせない
になる。
mdはブレークポイントと呼ばれていて、レスポンシブデザインを構成する際に使用する要素。
下記bootstrapの記事だが、Material UIにもある。-
https://getbootstrap.jp/docs/5.3/layout/breakpoints/
今回のチュートリアルでは、Tailwind CSSの機能の模様。
-
https://getbootstrap.jp/docs/5.3/layout/breakpoints/
Next.jsチュートリアル
ルーティング・レイアウト
下に要約を記載するが、公式の図解を見るのが一番わかりやすい。
- Next.jsは、ファイルシステムルーティングを使用する
- フォルダーを使用して、ネストされたルートを作成する
- layout.tsxとpage.tsxを使って個別のUIをそれぞれのルートに作成できる
- page.tsxはReactコンポーネントをExportする特別なファイルで、ルート("/")にアクセスするために必要
- app/dashboard/page.tsxは"/dashboard"を表示する
- app/page.tsx -> ルートのページ
プロジェクト構成
Next.jsはプロジェクト構成について触れている。
- page/routeファイルのみがクライアントに返却されるため、同ルートフォルダ内にプロジェクトファイル(pageから参照するコンポーネントなど)を配置できる
プロジェクトファイルを整理するため、下記のような特殊な命名がある
Private Folder
- フォルダの前に"_"を付ける。例:_folderName
- フォルダ、サブフォルダがルーティング対象から除外される
デフォルトで使用してもpage/layout以外はクライアントに返されないが、下記のような観点で使用する。
- UI ロジックをルーティング ロジックから分離します。
- プロジェクトおよび Next.js エコシステム全体で内部ファイルを一貫して整理します。
- コードエディターでのファイルの並べ替えとグループ化。
- 将来の Next.js ファイル規則との潜在的な名前の競合を回避します。
最後の名前の競合は、layoutなど、将来的なNext.js側の特殊な名前と衝突しないように使う。
現状の特殊な名前は下記にまとまっている。
Route Groups
- フォルダを丸括弧で囲む。例:(folderName)
- ルーティングのURLパスに含めないようにする。(プロジェクト管理目的であり、外部に見せたくない場合)
Route Groupsは下記のような観点で使用する。
- ルートをサイトセクション、目的、チームなどのグループに編成します。
- 同じルート セグメント レベルでネストされたレイアウトを有効にする:
src Directory
- appをsrcフォルダ以下に配置できる
- アプリケーションコードをプロジェクトの直下に置きたくない場合に使用する
Module Path Aliases
tsconfig.json(jsconfig.json)で、パスのエイリアスを作成できる。
// before
import { Button } from '../../../components/button'
// after
import { Button } from '@/components/button'
デフォルトの設定だと、@がルート指すようになっている。
概要
下記で、フォルダ構成の大枠(正解ではなく、こんなのもあるよ。プロジェクト全体で一貫性があればよいと言っている)
- appにはルーティング用のフォルダ、ファイルのみで、appと同レベルにプロジェクトファイルを配置する最上位フォルダ(componentsなど)
- appの下にプロジェクトファイルを配置する最上位フォルダを切る
- appの下でルーティング・機能ごとにプロジェクトファイルを分割して配置
https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases
Layout
下記のような階層構造がある場合、layout.tsxはdashboard::page/customers::pageの2つに対して共通のUIを提供する
layout.tsxを作成して、複数ページにまたがった共通のUIを作成できる。(サイドナビなど)- dashboard
- layout.tsx
- page.tsx
- customers
- page.tsx
- customers
layoutを使用する利点の一つは、ナビゲーション時にpage部分だけ更新されて、layout部分は再レンダリングされないこと。(部分レンダリング)
ルーティングの詳細
Next.jsチュートリアル
ページの移動
- <Link>コンポーネントを使用してページ遷移する
- <a>タグだと、ページ全体が更新されてしまう
- Next.jsはRouteセグメントごとに自動的にコード分割する。従来のSPA は初期表示時にすべてのアプリケーションコードを読み込むが、Next.jsはRoute単位なので、動作が異なる
- コード分割されるとページが分離されるので、特定のページでエラーでもほかのページは動く
- <Link>コンポーネントがブラウザのViewPortに表示されるたびに、Next.jsはリンク先のページを自動でプリフェッチする。バックグラウンドでページを構成するため、ページがすぐに移動する
- Next.jsからusePathname()というhooksが提供されており、使用することで現在のパスを取得することができる
Server State
状態管理
Redux
Reduxをどんな時に使うべきかのQA。
ケースバイケースだが、必要と思った時に使えと記載されている。
Apollo Client
既存のプロジェクトにReactを追加する
既存のウェブサイトの一部に React を使う
- Reactをフレームワーク(Next.js等)を使ってビルド
- フレームワークの設定で置き換えたいページのURLをbase pathに指定する
- リバースプロキシやサーバー設定でReactで作成したページに遷移するように設定する
ページ全体をReactで置き換えるパターン。
アプリ内でページ遷移する場合に、リバースプロキシだと情報が正しく引き継げるか考える必要がありそう。(セッションとか)
サーバーなら処理できる?
既存ページの一部に React を使う
既存のページの一部にReactを埋め込むパターン。Metaでも使っていた。
徐々にReact+フレームワークに移行できるなら採用しやすい。
Babelやviteの設定などが必要で設定で詰まる可能性はあるかもしれないが、設定ができて置き換えたいコンポーネント部分にidが振れれば、置き換えられる。
React開発環境(Editor)
- eslint
- npmで設定をインストール
- VSCodeの拡張機能を使用する
- prettier
- VSCodeの拡張機能を使用する
- eslintの設定とバッティングしないように設定を変更する
eslint
Create React App以外でプロジェクトを作成した場合は以下に従う。
npm install --save-dev eslint-config-react-app eslint@^8.0.0
prettier
VSCodeの拡張機能を入れた後に下記コマンドを実施。
npm install --save-dev prettier eslint-config-prettier
React Roadmap
useState
- コンポーネント内にレンダー間で保持あれる変数を作成する
- ローカル変数はレンダー間で保持されない
- 上記の動作のため、コンポーネントのメモリと記載している
- useStateで作成した変数を更新するとコンポーネントが再レンダーされる
- 詳細は公式の下記
- レンダーされるタイミング
- 初回(createRoot)
- useStateで作成したstateの更新
- レンダーは再帰的に行われるので、親コンポーネントのstateが変わって再レンダーされたら子も再レンダーされる
使用する際の注意点
1.関連する state をグループ化する。2 つ以上の state 変数を常に同時に更新する場合、それらを単一の state 変数にまとめることを検討してください。
2.state の矛盾を避ける。state の複数部分が矛盾して互いに「衝突する」構造になっている場合、ミスが発生する余地があるということです。これを避けてください。
3.冗長な state を避ける。コンポーネントの props や既存の state 変数からレンダー時に何らかの情報を計算できる場合、その情報をコンポーネントの state に入れるべきではありません。
4.state 内の重複を避ける。同じデータが複数の state 変数間、またはネストしたオブジェクト間で重複している場合、それらを同期させることは困難です。できる限り重複を減らしてください。
5.深くネストされた state を避ける。深い階層構造となっている state はあまり更新しやすくありません。できる限り、state をフラットに構造化する方法を選ぶようにしてください。
propsをstateにコピーしない
下記のコードは親から渡されるpropsが変化してもcolor stateの初期値は変化しない。
state は最初のレンダー時にのみ初期化される。
function Message({ messageColor }) {
const [color, setColor] = useState(messageColor);
上記の場合は、下記のようにpropsをそのまま使用するように変更すること。
※setColorなどで変更すれば変化はするはず。
function Message({ messageColor }) {
const color = messageColor;
propsをstateにコピーするケースとしては、propsの変更を意図的に無視したい(最初のみ受け取りたい)場合。その場合は、慣習として名前をinitial/defaultで始める。
function Message({ initialColor }) {
// Thecolor
state variable holds the first value ofinitialColor
.
// Further changes to theinitialColor
prop are ignored.
const [color, setColor] = useState(initialColor);
再レンダリング
下記、公式の記事に記載がある。
気を付けることとしては、
- パフォーマンスの最適化について記載がある(古い記事にリンクされているがuseMemoへのリンクはあるし、古い記事でも使えるものがある気がする)
- 再レンダーされる際、コンポーネント全体が変更されるわけではなく、コンポーネント内の差分がある箇所のみ再レンダーされること
ライフサイクル
開発の準備
VSCodeのデバッグ
VSCodeのデバッグモードで実行しても設定しないとブレークポイントで止めることはできない。
設定はnext.js公式の下記を参照にすること。
サーバー側、クライアント側、フルスタックの設定があるので、デバッグしたいソースによって起動モードを変えること。
下記、Zennの記事に日本語でまとまっている。
StrictMode
ReactとTypeScript
便利な型
ReactNode
JSX で子要素として渡すことが可能なすべての型のユニオン型
ReactElement
JSX 要素のみを指し、文字列や数値のような JavaScript のプリミティブは含まれません
React.CSSProperties
この型は可能な CSS プロパティすべてのユニオンであり、有効な CSS プロパティを props として style に渡していることを保証し、エディタで自動補完を得るための良い方法です。
https://ja.react.dev/learn/typescript#typing-style-props
他の型
便利な型
React 型定義には多くの種類のイベントが提供されています。完全な[リスト(https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b580df54c0819ec9df62b0835a315dd48b8594a9/types/react/index.d.ts#L1247C1-L1373)はこちら]にあり、DOM で最も使われるイベントに基づいています。
チートシート
事前にレンダリングする(SSR)
CSR+ハイドレーション
Next.jsチュートリアル
データの取得
下記について学習する。
- データの取得方法にはいくつかの方法
- API, ORM, SQLなど
- サーバーコンポーネントがどのようにバックエンドのリソース安全にアクセスできるか
- ネットワークウォータフォールとは
- JavaScriptを使って並列にデータ取得する実装する仕方
データ取得の方法
API
Next.jsではルートハンドラを使ってAPIのエンドポイントを作成できる
DB クエリ
APIのエンドポイントを作成するとき、DBクエリをを記述する必要がある
サーバーコンポーネント(RSC)を使用している場合は、APIレイヤーをスキップしてDBのシークレットをクライアントに公開するリスクを負うことなく、DBにアクセスできる
サーバーコンポーネントを使用したデータ取得
サーバーコンポーネントを使用してデータの取得にはいくつかの利点がある
- useEffect, useState, データ取得用のライブラリを使用することなく、async/await構文が使用できる
- サーバーで実行されるため、高負荷のデータ取得やロジックをサーバーで実行して、結果だけクライアントに送信できる
- サーバーで実行されるため、API呼び出しすることなく、DBに直接クエリできる
SQL
ライブラリ(Vercel Postgres SDKなど)を利用して、SQLを使用できる
- SQLはRDBにクエリするための標準
- SQLの基本を理解することで、RDBの基礎を理解でき、その知識をほかのツールに活かせる
- SQLは汎用性が高い。特定のデータを取得・操作できる
- Verel Postgres SDKはSQLインジェクションからの保護を提供する
ダッシュボードのoverviewページ用のデータ取得
サンプルコードの修正+演習
コンポーネントからSQLを使用してデータ取得するように修正する
作成したコードには下記の注意点がある。
-
複数のデータ取得リクエストを記載する→データ取得リクエストが意図せず相互にブロックしてリクエストウォータフォールが作成されている
-
Next.jsはデフォルトではパフォーマンス向上のため事前レンダリングする。静的レンダリングと呼ばれ、データが変更されてもダッシュボードに反映されない
リクエストウォータフォールとは
前のデータリクエストが終わるまで、取得が開始できないこと
データ取得がシーケンシャルになっていて、一気にデータ取得ができない状態
このパターンは必ずしも悪いわけではない。
このパターンが必要なのは前のリクエストで、次のリクエストを行うために必要な情報を取得するときである。
→IDを取得。取得したIDを使って情報を取得する場合など。
並列データ取得
ウォータフォールを避けるには並列でデータ取得する必要がある。
JavaScriptだと、Promise.all() or PRomise.allSettled()を使用して同時にPromiseを開始できる。
このパターンを使うことで、
- パフォーマンス向上のため、同時にデータ取得ができる
- ネイティブJavaScriptのパターンを使うことでライブラリによらない解決ができる
ただし、このJavaScriptで解決するパターンは欠点が1つある。
それは、1つのデータリクエストがほかのすべてのデータリクエストよりも遅い場合だ。
Next.jsチュートリアル
静的レンダリングと動的レンダリング
今までのチュートリアルで下記の課題があった。
- データ取得で意図しないウォータフォールが発生している
→非同期取得(Promise.allなど) - ダッシュボードが静的に作成されているため、データの更新が反映されない
→本チャプター対応部分→動的レンダリングが必要
静的レンダリング
- データの取得、レンダリングがビルド時(デプロイ時)、または再検証時にサーバーで実行される
- サーバーで作成された生成物はCDNに配布・キャッシュできる
静的レンダリングの特徴
- 早い
- 静的レンダリングした結果はキャッシュして配布できる。早く・確実にアクセスできる
- サーバー負荷を軽減できる
- キャッシュされているのでユーザーのリクエストごとに動的にコンテンツを生成する必要がない
- SEO対策
- 検索エンジンのクローラーにとってインデックスがしやすい。(ページ読み込み時に利用可能なため)
静的レンダリングはデータのないUI、静的なブログや製品ページ、ユーザー間で共有されるデータに利用できる。動的に変更されるページには適さない。
動的レンダリング
- リクエスト時(ユーザーがアクセスした時)に、コンテンツがサーバー上でレンダリングされる
動的レンダリングの特徴
- リアルタイム更新
- ユーザー固有のコンテンツ。ユーザープロファイルなどユーザーとの対話に基づいてコンテンツを出せる
- CookieやURLの検索パラメーターなど、リクエスト時にしかわからない情報を使用できる
ダッシュボードを動的レンダリングにする
unstable_noStore
をサーバーコンポーネントのコードに埋め込むことで実施できる
import { unstable_noStore as noStore } from 'next/cache';
export async function fetchLatestInvoices() {
noStore();
// ...
}
unstable_noStore は、実験的なAPIのため、Segment Confit Optionの
export const dynamic = "force-dynamic"
でも対応できる。
Next.jsチュートリアル
ストリーミング
動的にデータ取得を行う場合、データ取得が遅いとユーザー体験が悪くなる。
データ取得が遅い場合にユーザー体験を向上させる方法を学習する。
- streamingについて
- loading.tsxとSuspenseを使用してストリーミングを実装する
- loading skeletons(ローディングスケルトン)
- route groups
- アプリケーション内でサスペンス境界を配置する
ストリーミングとは
ストリーミングは準備が整ったサーバーからクライアントに、ルートを小さいチャンク(データの塊)に分割して、徐々にデータを転送する技術のこと。
ストリーミングを使うことで、データ取得が遅いことによってページが全体がブロックされるのを防ぐことができる。
Reactのコンポーネントモデルは、各コンポーネントをチャンクと考えることができるのでストリーミングと相性が良い。
Next.jsでストリーミングを実現する方法は下記の2つがある。
- loading.tsxを使う
- <Suspense>コンポーネント使う
loadng.tsxを使ってページ全体をストリーミングする方法
dashboardフォルダと同階層にloading.tsxを配置すると、ページ全体がロードされるまで代替として表示されるUIを構築できる。
dashboardの例だと、先にSideNaviが表示される。SideNaviはユーザーが先に操作することができる。