Open20

React周りの技術情報収集

yhigoyhigo

React導入時に検討するフレームワークやライブラリ

フレームワーク

公式ドキュメントに最初からNext.jsやRemixなど選ぶことが記載されている。
https://ja.react.dev/learn/start-a-new-react-project

状態管理

Reduxが有名だが、MetaがRecoilを出している。(Recoilはまだver1はでてない)

参考にした記事
https://www.cxr-inc.com/blog/e5be58d4c5454f92ba0fcb5a2c772c8a
https://zenn.dev/kazukix/articles/react-state-management-libraries

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サポートするかも)
https://nextjs.org/docs/app/building-your-application/styling/css-in-js

ReactだとCSS-in-JSは非推奨とか?(ちゃんと公式読む
https://react.dev/reference/react/useInsertionEffect#injecting-dynamic-styles-from-css-in-js-libraries

Next.jsだとTailwind CSSが名指しかつ一緒にインストールできるため推奨?
最近は、Panda CSSとかも流行ってるみたい。

参考

https://zenn.dev/poteboy/articles/e9f63b87b3cd69
https://zenn.dev/ototrip/articles/tech-nextjs-approuter-2

ベストプラクティスに関する記事(どれがベストではなく、情報としてまとめてるもの)

https://zenn.dev/hrbrain/articles/437d0b7492ac47

yhigoyhigo

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 ペイロードには以下が含まれます。

  1. サーバーコンポーネントのレンダリング結果。
  2. クライアント コンポーネントをレンダリングする場所のプレースホルダー (またはホール) と、その JavaScript ファイルへの参照。

React はこの情報を使用してサーバー コンポーネントとクライアント コンポーネントを統合し、クライアント上の DOM を更新します。

https://nextjs.org/learn/react-foundations/server-and-client-components#network-boundary

パフォーマンス向上のためにサーバーコンポーネントがデフォルトとなっている。
サーバーコンポーネントとクライアントコンポーネントの使い分けが必要。
クライアントコンポーネントはUIをインタラクティブにする必要があるコンポーネントに使用する。

サーバーコンポーネント・クライアントコンポーネントを選択するチェックポイントは下記。
https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns

yhigoyhigo

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でサポートしていない旨の注意書き。
https://nextjs.org/docs/app/building-your-application/styling/css-in-js

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にもある。
yhigoyhigo

Next.jsチュートリアル

ルーティング・レイアウト

https://nextjs.org/learn/dashboard-app/creating-layouts-and-pages

下に要約を記載するが、公式の図解を見るのが一番わかりやすい。

  • Next.jsは、ファイルシステムルーティングを使用する
  • フォルダーを使用して、ネストされたルートを作成する
  • layout.tsxとpage.tsxを使って個別のUIをそれぞれのルートに作成できる
  • page.tsxはReactコンポーネントをExportする特別なファイルで、ルート("/")にアクセスするために必要
  • app/dashboard/page.tsxは"/dashboard"を表示する
  • app/page.tsx -> ルートのページ

プロジェクト構成

Next.jsはプロジェクト構成について触れている。
https://nextjs.org/docs/app/building-your-application/routing/colocation

  • page/routeファイルのみがクライアントに返却されるため、同ルートフォルダ内にプロジェクトファイル(pageから参照するコンポーネントなど)を配置できる

プロジェクトファイルを整理するため、下記のような特殊な命名がある

Private Folder

  • フォルダの前に"_"を付ける。例:_folderName
  • フォルダ、サブフォルダがルーティング対象から除外される

デフォルトで使用してもpage/layout以外はクライアントに返されないが、下記のような観点で使用する。

  • UI ロジックをルーティング ロジックから分離します。
  • プロジェクトおよび Next.js エコシステム全体で内部ファイルを一貫して整理します。
  • コードエディターでのファイルの並べ替えとグループ化。
  • 将来の Next.js ファイル規則との潜在的な名前の競合を回避します。

最後の名前の競合は、layoutなど、将来的なNext.js側の特殊な名前と衝突しないように使う。
現状の特殊な名前は下記にまとまっている。
https://nextjs.org/docs/getting-started/project-structure#routing-files

Route Groups

  • フォルダを丸括弧で囲む。例:(folderName)
  • ルーティングのURLパスに含めないようにする。(プロジェクト管理目的であり、外部に見せたくない場合)

Route Groupsは下記のような観点で使用する。

src Directory

  • appをsrcフォルダ以下に配置できる
  • アプリケーションコードをプロジェクトの直下に置きたくない場合に使用する

Module Path Aliases

tsconfig.json(jsconfig.json)で、パスのエイリアスを作成できる。
https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases

// before
import { Button } from '../../../components/button'
 
// after
import { Button } from '@/components/button'

デフォルトの設定だと、@がルート指すようになっている。

概要

下記で、フォルダ構成の大枠(正解ではなく、こんなのもあるよ。プロジェクト全体で一貫性があればよいと言っている)

Layout

layout.tsxを作成して、複数ページにまたがった共通のUIを作成できる。(サイドナビなど)
下記のような階層構造がある場合、layout.tsxはdashboard::page/customers::pageの2つに対して共通のUIを提供する

  • dashboard
    • layout.tsx
    • page.tsx
      • customers
        • page.tsx

layoutを使用する利点の一つは、ナビゲーション時にpage部分だけ更新されて、layout部分は再レンダリングされないこと。(部分レンダリング

ルーティングの詳細

https://nextjs.org/docs/app/building-your-application/routing

yhigoyhigo

Next.jsチュートリアル

ページの移動

https://nextjs.org/learn/dashboard-app/navigating-between-pages

  • <Link>コンポーネントを使用してページ遷移する
  • <a>タグだと、ページ全体が更新されてしまう
  • Next.jsはRouteセグメントごとに自動的にコード分割する。従来のSPA は初期表示時にすべてのアプリケーションコードを読み込むが、Next.jsはRoute単位なので、動作が異なる
    • コード分割されるとページが分離されるので、特定のページでエラーでもほかのページは動く
  • <Link>コンポーネントがブラウザのViewPortに表示されるたびに、Next.jsはリンク先のページを自動でプリフェッチする。バックグラウンドでページを構成するため、ページがすぐに移動する
  • Next.jsからusePathname()というhooksが提供されており、使用することで現在のパスを取得することができる
yhigoyhigo

既存のプロジェクトにReactを追加する

https://ja.react.dev/learn/add-react-to-an-existing-project

既存のウェブサイトの一部に React を使う

  1. Reactをフレームワーク(Next.js等)を使ってビルド
  2. フレームワークの設定で置き換えたいページのURLをbase pathに指定する
  3. リバースプロキシやサーバー設定でReactで作成したページに遷移するように設定する
    ページ全体をReactで置き換えるパターン。
    アプリ内でページ遷移する場合に、リバースプロキシだと情報が正しく引き継げるか考える必要がありそう。(セッションとか)
    サーバーなら処理できる?

既存ページの一部に React を使う

https://ja.react.dev/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page
手順は公式参照。
既存のページの一部にReactを埋め込むパターン。Metaでも使っていた。
徐々にReact+フレームワークに移行できるなら採用しやすい。
Babelやviteの設定などが必要で設定で詰まる可能性はあるかもしれないが、設定ができて置き換えたいコンポーネント部分にidが振れれば、置き換えられる。

yhigoyhigo

React開発環境(Editor)

  • eslint
    • npmで設定をインストール
    • VSCodeの拡張機能を使用する
  • prettier
    • VSCodeの拡張機能を使用する
    • eslintの設定とバッティングしないように設定を変更する

eslint

Create React App以外でプロジェクトを作成した場合は以下に従う。
https://www.npmjs.com/package/eslint-config-react-app#usage-outside-of-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

https://zenn.dev/crsc1206/articles/d92548257fb445#eslintとprettierと併用する

yhigoyhigo

useState

  • コンポーネント内にレンダー間で保持あれる変数を作成する
    • ローカル変数はレンダー間で保持されない
    • 上記の動作のため、コンポーネントのメモリと記載している
  • useStateで作成した変数を更新するとコンポーネントが再レンダーされる
    • 詳細は公式の下記
    • レンダーされるタイミング
      • 初回(createRoot)
      • useStateで作成したstateの更新
        • レンダーは再帰的に行われるので、親コンポーネントのstateが変わって再レンダーされたら子も再レンダーされる

使用する際の注意点

https://ja.react.dev/learn/choosing-the-state-structure#principles-for-structuring-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 }) {
// The color state variable holds the first value of initialColor.
// Further changes to the initialColor prop are ignored.
const [color, setColor] = useState(initialColor);

再レンダリング

下記、公式の記事に記載がある。
https://ja.react.dev/learn/render-and-commit

気を付けることとしては、

https://qiita.com/yokoto/items/ee3ed0b3ca905b9016d3
https://qiita.com/wangqijiangjun/items/c60bfe4fc6fb2c3e70b6

ライフサイクル

https://zenn.dev/yodaka/articles/7c3dca006eba7d

yhigoyhigo

開発の準備

VSCodeのデバッグ

VSCodeのデバッグモードで実行しても設定しないとブレークポイントで止めることはできない。
設定はnext.js公式の下記を参照にすること。
サーバー側、クライアント側、フルスタックの設定があるので、デバッグしたいソースによって起動モードを変えること。
下記、Zennの記事に日本語でまとまっている。
https://nextjs.org/docs/pages/building-your-application/configuring/debugging#debugging-with-vs-code

https://zenn.dev/s_takashi/articles/7bb10d74994b48

StrictMode

yhigoyhigo

ReactとTypeScript

便利な型

ReactNode

JSX で子要素として渡すことが可能なすべての型のユニオン型

ReactElement

JSX 要素のみを指し、文字列や数値のような JavaScript のプリミティブは含まれません

React.CSSProperties

この型は可能な CSS プロパティすべてのユニオンであり、有効な CSS プロパティを props として style に渡していることを保証し、エディタで自動補完を得るための良い方法です。
https://ja.react.dev/learn/typescript#typing-style-props

他の型

便利な型
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts

React 型定義には多くの種類のイベントが提供されています。完全な[リスト(https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b580df54c0819ec9df62b0835a315dd48b8594a9/types/react/index.d.ts#L1247C1-L1373)はこちら]にあり、DOM で最も使われるイベントに基づいています。

チートシート

https://react-typescript-cheatsheet.netlify.app/

yhigoyhigo

Next.jsチュートリアル

データの取得

https://nextjs.org/learn/dashboard-app/fetching-data

下記について学習する。

  • データの取得方法にはいくつかの方法
    • API, ORM, SQLなど
  • サーバーコンポーネントがどのようにバックエンドのリソース安全にアクセスできるか
  • ネットワークウォータフォールとは
  • JavaScriptを使って並列にデータ取得する実装する仕方

データ取得の方法

API

Next.jsではルートハンドラを使ってAPIのエンドポイントを作成できる
https://nextjs.org/docs/app/building-your-application/routing/route-handlers

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を使用してデータ取得するように修正する
作成したコードには下記の注意点がある。

  1. 複数のデータ取得リクエストを記載する→データ取得リクエストが意図せず相互にブロックしてリクエストウォータフォールが作成されている

  2. Next.jsはデフォルトではパフォーマンス向上のため事前レンダリングする。静的レンダリングと呼ばれ、データが変更されてもダッシュボードに反映されない

リクエストウォータフォールとは

前のデータリクエストが終わるまで、取得が開始できないこと
データ取得がシーケンシャルになっていて、一気にデータ取得ができない状態

このパターンは必ずしも悪いわけではない。
このパターンが必要なのは前のリクエストで、次のリクエストを行うために必要な情報を取得するときである。
→IDを取得。取得したIDを使って情報を取得する場合など。

並列データ取得

ウォータフォールを避けるには並列でデータ取得する必要がある。
JavaScriptだと、Promise.all() or PRomise.allSettled()を使用して同時にPromiseを開始できる。
このパターンを使うことで、

  • パフォーマンス向上のため、同時にデータ取得ができる
  • ネイティブJavaScriptのパターンを使うことでライブラリによらない解決ができる

ただし、このJavaScriptで解決するパターンは欠点が1つある。
それは、1つのデータリクエストがほかのすべてのデータリクエストよりも遅い場合だ。

yhigoyhigo

Next.jsチュートリアル

静的レンダリングと動的レンダリング

今までのチュートリアルで下記の課題があった。

  1. データ取得で意図しないウォータフォールが発生している
    →非同期取得(Promise.allなど)
  2. ダッシュボードが静的に作成されているため、データの更新が反映されない
    →本チャプター対応部分→動的レンダリングが必要

静的レンダリング

  • データの取得、レンダリングがビルド時(デプロイ時)、または再検証時にサーバーで実行される
  • サーバーで作成された生成物は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"
でも対応できる。

yhigoyhigo

Next.jsチュートリアル

ストリーミング

https://nextjs.org/learn/dashboard-app/streaming

動的にデータ取得を行う場合、データ取得が遅いとユーザー体験が悪くなる。
データ取得が遅い場合にユーザー体験を向上させる方法を学習する。

  • streamingについて
  • loading.tsxとSuspenseを使用してストリーミングを実装する
  • loading skeletons(ローディングスケルトン)
  • route groups
  • アプリケーション内でサスペンス境界を配置する

ストリーミングとは

ストリーミングは準備が整ったサーバーからクライアントに、ルートを小さいチャンク(データの塊)に分割して、徐々にデータを転送する技術のこと。
ストリーミングを使うことで、データ取得が遅いことによってページが全体がブロックされるのを防ぐことができる。
Reactのコンポーネントモデルは、各コンポーネントをチャンクと考えることができるのでストリーミングと相性が良い。
Next.jsでストリーミングを実現する方法は下記の2つがある。

  1. loading.tsxを使う
  2. <Suspense>コンポーネント使う

loadng.tsxを使ってページ全体をストリーミングする方法

dashboardフォルダと同階層にloading.tsxを配置すると、ページ全体がロードされるまで代替として表示されるUIを構築できる。
dashboardの例だと、先にSideNaviが表示される。SideNaviはユーザーが先に操作することができる。