Closed18

Learn Next.jsをやってみる

mm

やること

https://nextjs.org/learn

進め方

  • 基本は手元でコードも動かしながら。
  • 不明点が出てきたら公式Docsも見ながら順次掘り下げる
  • できる日に1~3chapterずつ。
mm

Chapter1 Getting Standard

https://nextjs.org/learn/dashboard-app/getting-started

Next.jsが用意したサンプルがあるのでそれをローカルに落としてくる
おおよそのディレクトリ構造ができている

気になったこと1

構造がこんな感じだけど

  • app/
  • app/lib
  • app/ui

app配下のrouteって全部スキャン対象(?)なんじゃなかったっけ。。。
libやuiのディレクトリもスキャン対象なんじゃない?

この辺を見ると良さそう。
https://nextjs.org/docs/getting-started/project-structure#app-routing-conventions
file nameで識別するから、これらのfolder配下にpage.jsとかがいなきゃいいってことかな。

試しにlibとかにpage.tsxを生やしてみる

// app/lib/page.tsx
export default function Page() {
  return (
    <div>
      <h1>This is /lib Page.</h1>
    </div>
  );
}

ページとして見えるようになる。
まあ基本的に生やさない運用だから良いよねってことなんだろうけど、こういうのをPrivate Folderとかにしちゃダメなのかな。(運用的に)
https://nextjs.org/docs/getting-started/project-structure#route-groups-and-private-folders
app/_libみたいなイメージ。

公式も下記でそういう運用もあるよと示してはいる
https://nextjs.org/docs/app/building-your-application/routing/colocation#private-folders

ただ、下記もあるようにprivate folder使わなくても結局はルーティング対象になるファイル名は決まってるしそれで良いよ的な記載がある。

If you don't use private folders, it would be helpful to know Next.js special file conventions to prevent unexpected naming conflicts.

ここはチームでどういう運用するか次第なんでしょうな。

Chapter まとめ

  • サンプルプロジェクトをローカルに落とした
  • 構造をざっくり把握した
  • app routerとしてのフォルダ構造、ファイル命名に関してざっくり確認できた
mm

Chapter2 CSS Styling

https://nextjs.org/learn/dashboard-app/css-styling

グローバルCSSのimportとTailwindの導入。

このプロジェクトでは基本的にTailwindを扱うが、CSS Modulesの扱い方も一応紹介されてる。
あとはclsxも扱うのでその紹介も。

とはいえ基本的にはスタイリングにあまり力を入れて説明している感じはないので、最低限のお作法程度の内容。

layoutについては多分セクションを進めていくと出てくるだろうけど、軽く内容を把握しておく。
https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts

Chapterまとめ

  • グローバルcssのimportをRoot Layoutに設定
  • Tailwind, CSS Modulesの紹介
mm

Chapter3 Optimizing Fonts and Images

フォントと画像の最適化
https://nextjs.org/learn/dashboard-app/optimizing-fonts-images

next/fontとnext/imageの扱い方の説明

next/font

memo

  • フォントの最適化が必要な理由
    • CLS対策のため。
      • 通常はブラウザがデフォルトフォントを読み込んで、その後にカスタムフォントを読み込むため、その際にCLSが起きちゃうよとのこと。
      • それを回避すべく、next/fontがビルド時にアセットの最適化を行ない、他の静的アセットとともにホストしている。

カスタムフォントってWebフォントを指すのかな。

chapter内ではGoogleフォントを扱うことでそれを試している。
(next/font/googleでもう使えるんですね)

扱い方の話

サンプル内ではカスタムフォント用に app/ui/fonts.tsを作成して、カスタムフォントの設定を施した変数をnamed exportしている

利用箇所ではそのフォント情報を読み込んだ上で classNameにあてている。

// `inter`というフォントの場合
// app/layout.tsx
import { inter } from "@/app/ui/fonts";
.
.
<body className={`${inter.className} antialiased`}>{children}</body>
.
.

next/image

memo

  • 画像の最適化について
    • 通常のHTMLでは画像の最適化は画面サイズごとの最適化、レイアウトずれなどを防ぐ必要がある
    • これを楽にしてくれるのが next/imageだぜということ

<Image>コンポーネントを使うことで解決する
=> 色々最適化をしてくれる。デフォルトで遅延読み込みするようになってるんだね。

扱い方の話

next/imageからImageコンポーネントを利用して、レンダリング後のimgタグの内容を見てみる

ポイントになりそうなのは

  • loading="lazy"
  • decoding="async"
  • srcset内で解像度ごとの画像指定

decoding="async"はお恥ずかしながら知らなかった。
この辺りの記事とか参考になりそう。
https://zenn.dev/ixkaito/articles/deep-dive-into-decoding

そもそもの最適化とかの話はMDNとか見てねというのもあるので、この辺りはページの内容を見てもらうと良さそう。

mm

Chapter4 Creating Layouts and Pages

レイアウトとページの作成

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

file base routingや、nested layoutsあたりの話。

結局はChapter1でもみた https://nextjs.org/docs/getting-started/project-structure#app-routing-conventions を知っていればなんということはなさそうではある。

ページ作成

app dir配下でページをパスごとに作りたければ、page.tsxを作りましょうという話

/dashboardを作りたい : app/dashboard/page.tsx
/dashboard/invoicesを作りたい : app/dashboard/invoices/page.tsx

レイアウト作成

ページを作る際と同様に、特別なファイルとしてlayout.tsxを作ればOK

layout.tsxでは、propsにchildrenを受け取る。(children: React.ReactNode)

演習では下記3ページを用意している状態で、その中で最上位のapp/dashboard直下にlayout.tsxが配置されている状態。

app/dashboard/layout.tsx // /dashboard/**用のレイアウト
app/dashboard/page.tsx // /dashboard
app/dashboard/customers/page.tsx // /dashboard/customers
app/dashboard/invoices/page.tsx // /dashboard/invoices

/dashboardのパスをベースとして、それ以降の階層でも共通のレイアウトが使用される。

また、このようにページとレイアウトとがある中で、共通のレイアウトを使用している階層でのナビゲーションにおいては、部分レンダリング(Partial Rendering)が行われる。

(つまり、/dashboardから /dashboard/customers へのナビゲーションであれば、レイアウト以外の要素が再レンダリングの対象となる。)

ルートレイアウト

app/layout.tsxがルートレイアウトに該当する。
これは必須なファイルであり、htmlやbodyなど、これらを配置する。(Next.jsが自動生成するような要素ではないので定義が必要である)

metaデータの追加に関してはこの後のチャプターで出てくる。

mm

Chapter5 Navigating Between Pages

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

ページ間の移動(ナビゲーション)を学ぶ

next/linkを中心とした話になりそう

next/linkが提供する<Link>コンポーネント

通常のHTMLでは <a>を用いたナビゲーションを行うが、これはページ全体のリロードを行うので、
変更して欲しい箇所のみの更新を求める際は next/linkが提供する<Link>コンポーネント を使うと良いよ
というお話

これ、個人的には勝手にクライアントサイドのルーティング(SPA的イメージ)でいたけど、どうやらそうではなさそう。下記記述。

Next.js automatically prefetches the code for the linked route in the background.

リンクされたルートのコードをバックグラウンドでプリフェッチしてくれるとのこと。
これでスムーズな遷移もできるし、ページごとのロードがスムーズってことなのかな。

下記も参照とのことなのでみてみると良さそう
https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#how-routing-and-navigation-works

サーバサイドのデータフェッチとかとは別の口になるのかな?その辺は特に記述が見当たらないのでこの後のデータフェッチの実装あたりで試してみても良いかも。

usePathname()でアクティブなリンクを表示可能

現在どこにいるかを教えてくれるもの。
返却はstringで、path名を返す。(e.g. /dashboard/invoices, /dashboard/customers)

mm

Chapter6

https://nextjs.org/learn/dashboard-app/setting-up-your-database

ここまでの実装分をGitHubにpush, vercelにホスティング、その上でDBのセットアップをするだけ。こだわりがなければ、vercelのpostgresqlを使おうねという章になっている。

なので特にここに残すこともないかも。。。

mm

Chapter7 Fetching Data

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

ここからデータをフェッチしてダッシュボードを作るために必要な処理の実装を行なっていく。

データ取得方法の選択

API

外部APIを利用する際や、データベースをクライアントから利用する場合の中間層としての選択肢。
これにはNext.jsを使うならRoute Handlersが使えるよという紹介がある。

データベース

APIからデータベースクエリの実行をするという選択肢か、
RSC (React Server Components)ならばAPI層を介する必要はないよという紹介がある。

If you are using React Server Components (fetching data on the server), you can skip the API layer, and query your database directly without risking exposing your database secrets to the client.

サーバサイドでの実行なので機密情報の漏洩の心配がないということでしょう。

RSC(React Server Components)によるデータベースからのデータフェッチ

Next.jsではデフォルトでRSCが利用される。

RSCだと useEffectuseStateを利用せずにasync/awaitを用いての非同期データ取得が可能とのこと。

これは後述のコンポーネントで出てくるが、下記のようなコンポーネント定義でのデータ取得ができるということ。

export default async function Page() {
  const revenue = await fetchRevenue();
  const latestInvoices = await fetchLatestInvoices();
  // ...
}

実際に扱ってみる

ドキュメント内では下記のような依存関係でデータをfetchしている。

- app/dashboard/page.tsx (ページ)
  - app/lib/data.ts(データ取得のsql実行用関数群)

デフォルトがRSCであるということもあり、依存関係は深くなく構造的にはスッキリしている印象。
慣れてないというのが一番だが、コンポーネントの依存にすぐSQLがあることに違和感がまだ拭えない。。
(これが良くないということではなく)

用意されてる演習ではSQLを自分で書く必要はなく、「RSCを使っているとこんなにも簡単にSQLを扱えて良いですよ」ということを伝えたいのかなあという印象を抱く。

request waterfalls

演習を進めていると、このワードが出てくる。
これが何者ですかという話。
https://nextjs.org/learn/dashboard-app/fetching-data#what-are-request-waterfalls

例えばこの演習で進めてきた内容だと、3つのDBからのデータ取得処理をシーケンシャルに行なっており、
「1が終わったら2を実行、2が終わったら3を実行して、...」のような処理になっていた。

このようにシーケンシャルにやることは、そうしないといけないケースもあるけれど、パフォーマンスには影響を及ぼすので並列化できるならやろうよというのが次の話。

parallel-data-fetching

じゃあ並列でのデータ取得が許容されるものならばそうしようよという話
https://nextjs.org/learn/dashboard-app/fetching-data#parallel-data-fetching

ここでの事例では 「Promise.allPromise.allSettledを使って並列化させちゃおう」という通常のJavaScript的アプローチが記載されている。

このセクションの最後の文章に「there is one disadvantage of relying only on this JavaScript pattern: what happens if one data request is slower than all the others?」とだけ記載があってセクションが終わる。

ほう、ほならどうするんでしょうなという期待感を持たせて次のセクションに行くらしい。

mm

Chapter8 Static and Dynamic Rendering

https://nextjs.org/learn/dashboard-app/static-and-dynamic-rendering

ほぼタイトル通りで、静的レンダリングと動的レンダリングのセクションとなっている。

Static Rendering

デフォルトでは静的レンダリングになっているらしく、静的レンダリングでは下記のタイミングでレンダリングが行われる。

このレンダリング結果はキャッシュされる。
高速化・サーバの負荷軽減・SEO(ページは事前読み込みが可能)などの利点がある。

静的レンダリングはユーザー間で共有可能なページ(ブログ・製品情報等)に向いており、
パーソナライズされたデータが必要となるページには向かない。

Dynamic Rendering

動的レンダリングは静的レンダリングとは逆で、ユーザー固有の情報であったり最新の情報が必要とされるページにおいて有効。

ただここのセクションではそれ以上の言及がないですねえ。。。

というわけで下記を見てみよう。
https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering

  • cookies(), headers()をRSCで扱うと、動的レンダリング対象として認識される。
  • useSearchParams()の話が出てくるが、これ動的レンダリングとどう関係があるのだ...? ※
    • 説明を見ると、これを扱う場合はクライアントコンポーネント内のSuspense境界に近いところまでを静的レンダリングの対象としないらしい。
    • なのでクライアントサイドで静的レンダリングをしたい場合はコンポーネント内で useSearchParams()を扱うコンポーネントを<Suspense>でwrapしなさいとある。
    • つまり、useSearchParams()を扱うと動的レンダリングされるから、静的に取り扱いたい時は気をつけようねということを言いたいのかな。
    • 例: https://nextjs.org/docs/app/api-reference/functions/use-search-params#static-rendering

※ ちなみにhttps://nextjs.org/docs/app/api-reference/functions/use-search-params#returns をみると、下記のような記述があるので、そもそもRSCで扱えるものではないということはわかる。

useSearchParams is a Client Component hook and is not supported in Server Components to prevent stale values during partial rendering.

@vercel/postgresを扱うレンダリングについて

By default, @vercel/postgres doesn't set its own caching semantics. This allows the framework to set its own static and dynamic behavior.

冒頭からこれの意味がわからなかったです。。。

https://nextjs.org/docs/app/api-reference/functions/unstable_noStore
を見ると、

unstable_noStore can be used to declaratively opt out of static rendering and indicate a particular component should not be cached.
つまり、静的レンダリングをさせたくない場合のオプトアウトの手法 (= 動的レンダリングとする) のもの。
試験的機能なので。。。という説明もあるのが気になる。

そして最後に、動的レンダリングにおいて依存する処理(e.g. 遅いデータ取得処理)などがある場合、その処理に依存してレンダリングがブロックされちゃうよとある。

なので動的レンダリングを使うのにも課題があるよというお話。
(仕方ないところはあるのだろうけど、まあここはデータをどう取り扱うかという工夫の余地はあるのだろうな。そこがNext.jsの範疇かは置いといて。)

mm

Chapter9 Streaming

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

前チャプターで、動的レンダリングを扱う際にデータフェッチが遅い場合はパフォーマンスに影響があるよという終わり方をした中で、それを解決するためのStreaming(ストリーミング)を学びましょうというchapterのよう。

ストリーミング

ストリーミングはページ内の各コンポーネントをチャンクに分割して、準備が整ったら段階的にサーバからクライアントにストリーミングする転送技術のこと。

これによって、前チャプターでも話に出た「遅い処理があるとページ全体のレンダリングをブロックしてしまうよ」という問題を解決する。

ストリーミングを扱うにはloading.tsxを扱う。

ストリーミングとloading.tsxの詳細については下記を参照した方がよさそう。
https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming

loading.tsxを使うとその階層に対して全体でローディングUIが利用できるようになる。
個別に<Suspense>を設定することも可能。

この個別に<Suspense>を扱うということが、コンポーネントごとにデータフェッチの責務を閉じ込めることができるようになり、ページ全体の表示パフォーマンスにも効果がありそう。
※Suspenseのフォールバック相当のloadingコンポーネント (skeleton)を用意するという前提もあるかもしれないが。

ページのトップから取得したデータを各コンポーネントに渡していくというような設計から、
コンポーネントごとがデータの取得含めた責務を担うということが可能になるという理解。

こうすると「同じAPIのコールが頻発しないか?」となるところだが、多分これがData Cacheと呼ばれるキャッシュによって緩和されるのでしょうな。
https://nextjs.org/docs/app/building-your-application/caching#data-cache
https://speakerdeck.com/mugi_uno/next-dot-js-app-router-deno-mpa-hurontoendoshua-xin?slide=15

ユーザー体験の向上という意味ではNext.jsを扱っていればこの辺りの懸念みたいなものはユーザーが特別に意識することは減るのかもしれない。

mm

Chapter10 Partial Prerendering (Optional)

https://nextjs.org/learn/dashboard-app/partial-prerendering

部分的な事前レンダリング。
早速冒頭に注記のある通り、これはNext.js v14で投入された実験的機能らしい。

とりあえず流して読んでおくくらいにしておく。

document
https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering

demo
https://vercel.com/templates/next.js/partial-prerendering-nextjs

部分的事前レンダリングとは何か

ページ全体の構成においては、「静的に表現可能なコンポーネント」と「動的に表現可能なコンポーネント」がある。
「静的な部分は事前にレンダリングが可能なので、動的コンポーネントに依存する必要は本来ないよね」という発想から、静的な部分は事前にレンダリングしておいて、動的な部分はユーザー訪問時にロードすれば良いというもの。

実現方法

  • 動的コンポーネントを<Suspense>でwrapする
  • React Concurrent Featuresを利用する ※
  • (Next.js v14限定/それ以降のバージョンでなくなるかも) next.config.jsexperimental.ppr = trueを設定する

※ こういうものを知っておくべきなのかな https://ja.react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react

ひとまず今回のアプリケーションとは関係ないが、ふーん程度にとどめて次へいく。

動作とかはデモを見るのがわかりやすそうでした。

mm

Chapter11 Adding Search and Pagination

https://nextjs.org/learn/dashboard-app/adding-search-and-pagination

検索とページネーションの追加

searchParams, usePathname, useRouter を使って実装する。

searchParams, useSearchParams

一緒やん?と思ったんですが、一応使い分けはあるみたい。

searchParams

https://nextjs.org/docs/app/api-reference/file-conventions/page
にある通り、ページのpropsとして渡されるとのこと。
こちらはサーバコンポーネントでの利用が可能で、componentにpropsとして渡すことも可能。

useSearchParams

https://nextjs.org/docs/app/api-reference/functions/use-search-params
クライアントコンポーネント用のhooksになるらしい。

つまり、大まかな使い分けとしては

  • サーバ = searchParams
  • クライアント = useSearchParams

でよさそう。

Reactにおける入力イベントの遅延について

特に何もせずに、inputにおけるonChangeイベントを扱うと、キーストロークイベントごとにイベントが走るので、余計なレンダリングコストが発生する。
これを緩和するためにuseDebounceというフックを使うと良いよという紹介がある。
(これはNextというよりはReactとしての紹介なのでしょうけど、一応知っておきたいというところでメモ)

https://www.npmjs.com/package/use-debounce

mm

Chapter12 Mutating Data

(多忙により時間が空いてしまった)
https://nextjs.org/learn/dashboard-app/mutating-data

ここでデータの更新処理を行うが、いよいよ目玉のServer Actionsが出てきそう

Server Actions

サーバ上で非同期コードを実行できるため、
例えばこれまでのようにAPI Routesでエンドポイントを作る必要はなく、サーバサイドの処理実行が可能になるもの。

Server Actionを作成するとき、app/lib/actions.tsを作成するが、このファイルにおいては冒頭に use-serverを宣言する必要がある。
これは定義している関数をサーバー関数としてマークしてNext.jsに認識させるため。

個人的には「Appディレクトリ配下のコンポーネントはサーバコンポーネントがデフォルト」という感覚でいる上で、関数の場合も特に意識しないような気持ちでいたが、これは注意ですな。

例の中でも紹介されているが、通常のHTMLだとformにおけるaction属性はURLを記載すると思うが、Reactにおいては特別な扱いを受けることになり、特別なアクションを設定することが必要になる。

Form Validationはzodと組み合わせて行なっている。
Server Actionでも通常通りバリデーション処理を書いてるだけなので、特別なことは特になさそう。

そしてServerActionsでもSQLを扱ったDB処理も同一箇所で行なっている。

ここでDBへの更新処理を行うと、Client-Side Router Cacheというキャッシュの更新を行わないとキャッシュによって更新されない。
その対応として revalidatePathを実行する必要がある。

引数に与えるべきパスはこの例題では '/dashboard/invoices'となっており、これはこのページパスに対してのキャッシュのrevalidateを行うことを意味する。

参考
https://nextjs.org/docs/app/api-reference/functions/revalidatePath
https://zenn.dev/cybozu_frontend/articles/server-actions-and-revalidate

結果として何が良いか悪いか

参考資料として下記を引用させていただきつつ
https://azukiazusa.dev/blog/why-use-server-actions

特に最近よく聞くようなプログレッシブエンハンスメントへの効果が高そうです。
https://developer.mozilla.org/ja/docs/Glossary/Progressive_Enhancement

そして、SQL実行やクライアント実行 or サーバ実行という見分けがつかない時にうっかりpropsで機密情報を流し込んでしまうなどの危険性も考えられてしまうのが怖いところだなとも思う。

この辺りは記述として import "server-only";という記述をファイルに施しておくことで、クライアント側へのimportをしようとするとビルドエラーが発生させることができる。

結局そういう記述を手作業で追加することにはなってしまいそうだが、ここは特にチーム開発などではルールとして一定の基準を定めておけば良いものだろう。
(e.g. SQL利用がある, Actionsにおけるレイヤ分割を行う (※ファイル構造の問題))

mm

Chapter13 Handling Errors

https://nextjs.org/learn/dashboard-app/error-handling

try-catchによるエラーハンドリングや、error.tsxnotFound関数などの扱いについてチェック

try-catchについては通常のJavaScriptの扱いと同様なので特に触れない。

error.tsx

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

実行時エラーや予期しないエラー等を全てキャッチするために必要なファイル。
これによってエラー時のUIを準備します。

ここではサンプルにもある通り、下記のようなことが可能であることがわかる。

  • use client;によりクライアントコンポーネントであることを明示する
  • errorというJSのエラーオブジェクトと、resetというルートセグメントの再レンダリングを行う関数をpropsとして受け取れる(= エラーの回復用の関数)

このファイルが行なっていることは How error.js Worksにも記載のある通り、「ネストされた子セグメントまたは page.js コンポーネントをラップするReact Error Boundaryの生成」を行う。

notFound function

404な状況 (データのリソースがない等)におけるエラーハンドリングとして利用可能な関数。
多分こちらを見た方が良い。
https://nextjs.org/docs/app/api-reference/functions/not-found

Invoking the notFound() function throws a NEXT_NOT_FOUND error and terminates rendering of the route segment in which it was thrown.

とあるので、レンダリングを修了してくれるらしい。

この場合にエラーがthrowされるわけだが、この時not-found.tsxを用意しなさいという案内こそあるが、そのファイルがない時はどうなるんだろう?
(not-found.tsxの not found...)

請求書データを一件削除してみて、その請求書データの編集画面だったURLにアクセスしてみる
/dashboard/invoices/[id]/edit

まずは何もしてない状態から。(error.tsxによるハンドリングができている状態)

この時、コンソールにはレンダリングをしようとしてエラーが度々表示されている。

次に app/dashboard/invoices/[id]/page.tsxで、invoiceの存在チェックをして、存在しない場合はnotFound()をコールするだけ。
この時点ではnot-found.tsxは存在しない。

見た目は変わらない。
ただレンダリングをやめているのでコンソールエラーは無くなった。

次に not-found.tsx
app/dashboard/invoices/[id]/not-found.tsxを用意してみる。

import Link from 'next/link'
 
export default function NotFound() {
  return (
    <div>
      <h2>Not Found</h2>
      <p>Could not find requested resource</p>
      <Link href="/">Return Home</Link>
    </div>
  )
}

見た目はとりあえず良くて、ちゃんとnot-found.tsxの内容が表示されている。

ハンドリングをする上ではこういう細かいところも知っておけるとよさそう。

mm

Chapter14 Improving Accesibility

https://nextjs.org/learn/dashboard-app/improving-accessibility

アクせシビリティの改善

  • eslint-plugin-jsx-a11yを用いたアクセシビリティ考慮
  • Server Sideのフォーム検証
  • useFormStateReact を使ったフォームエラー処理

前提として、アクセシビリティとはなんぞやを知る場合はweb.dev見てねとのこと
https://web.dev/learn/accessibility/

eslint-plugin-jsx-a11y

https://www.npmjs.com/package/eslint-plugin-jsx-a11y

デフォルトではNext.jsに含まれてるらしい。

eslint-config-nextがすでにサンプルのアプリケーションにはインストールされていたが、そこに含まれているということだろう。
https://nextjs.org/docs/app/building-your-application/configuring/eslint#migrating-existing-config

試しに <Image>なコンポーネントにすでに設定されている alt属性を削除するとlintで怒られる。

ここではこのeslint-pluginを使えばアクセシビリティに適した実装考慮もされるよという紹介。

フォームのアクセシビリティ向上

フォームのアクセシビリティ向上の対応案として、サンプルアプリケーションではすでに下記が行われている。

  • セマンティックHTML (divではなくinput, optionsなどの要素を用いたフォーム実装)
  • ラベリング (labelを用いたフォーム部品ごとの適切なラベル付)
  • フォーカスアウトライン (現在フォーカスのあたっているフォーム部品がどれかをユーザーが把握できるようにする)

これに加えて、フォームの検証とエラーハンドリングを別途行う必要がある。

フォームのエラーハンドリングに関しては useFormStateフック, zodを用いたハンドリングを行なって、
その結果として必要に応じたaria属性を付与するというもの。

useFormState vs react-hook-form useForm?

というのは気になった。
「Reactでformを扱う時ってreact-hook-formな印象が強い」というのが正直なところだが、、、

ReactのuseFormState

https://react.dev/reference/react-dom/hooks/useFormState

そもそもまだ新しい機能っぽい?
アクションの関数と初期状態(state)を渡すことで、フォームで扱うことのできるアクション、stateを渡すことが可能。

chapterの例を見ても特に通常の扱いであれば苦労することはなさそうな印象は受ける。

react-hook-formのuseForm

https://react-hook-form.com/docs/useform

react-hook-formにも useFormStateという関数があって混同しそうだが、フォーム全体の管理ということで比較するなら useForm になるだろう。

useForm自体にはフォームそのものに対しての検証や実行タイミングのカスタマイズなどを可能にする引数やオプションが多く用意されており、機能の充実度でいうとこちらがやはり安定か。
ただ、そこまでReact慣れしていない個人的には、複雑すぎる、too muchな印象もあったので、無理にこちらを使う必要は今後はないのではないか。。。

mm

Chapter15 Adding Authentication

https://nextjs.org/learn/dashboard-app/adding-authentication

ダッシュボードに対して認証処理を追加していく。

「認証認可とは」という話から説明がされている。

NextAuthを使って認証認可処理を施すにはどうするかというchapterになっていそう。

NextAuth

現時点ではNext 14に対応したnext-authのパッケージはbeta版のようなのでbetaをインストールする。

そこから secretの作成、auth.config.tsの作成を進める。

その上でNext.jsのmiddlewareを用いた認証処理を組み込む。
https://nextjs.org/docs/app/building-your-application/routing/middleware

Next.jsでのmiddlewareは、特定のパスにマッチするルートにおいて、リクエストが完了する前に実行したい処理を施すことが可能になる。

Chapter内でも下記のように説明がある通り、middlewareの処理が完了するまではレンダリングが開始されないとのこと。

The advantage of employing Middleware for this task is that the protected routes will not even start rendering until the Middleware verifies the authentication, enhancing both the security and performance of your application.

あとは説明通りにNextAuthの認証が可能なように実装を進めていくのみ。

mm

Chapter16 Adding Metadata

https://nextjs.org/learn/dashboard-app/adding-metadata
メタデータの追加を行う。

SEOにおいて重要なtitle, description, og:imageなどのWebページにおける情報。

Next.jsにおけるメタデータ定義方法

構成ベースでの定義

  • metadata object
    • page.tsxやlayout.tsxでmetadataという命名でnamed exportすることで構成可能な静的meta。
  • generateMetadata function
    • metadata objectとは異なって、動的生成を行う場合はこちらを利用する。こちらもpage.tsx or layout.tsxで定義する。

ファイルベースでの定義

faviconやopengraph-imageなど、app dir以下にファイルを配置することで認識してくれるらしい。。すごい

こちらも参照:https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons

title.template

階層を掘っていくごとにtitleを変えたい場合、その階層ごとにmetadataを定義すれば良いが、例えば「ページ名 | サイト名」のようなtitleにおいては、サイト名は毎回記述することになる。

これを回避できるように、title.templateなるものが用意されている。

.
.
export const metadata: Metadata = {
  title: {
    template: '%s | Acme Dashboard',
    default: 'Acme Dashboard',
  },
.
.

こうしておくと、固有ページにおいては下記のようにしておくだけでよい。

export const metadata: Metadata = {
  title: 'Invoices',
};

これで「Incoices | Acme Dashboard」と表現される。
この仕組みいいな。

mm

wrap up

app dirの扱い方自体はざっくり把握できた。

巷で話題な「Next.js特有のキャッシュの概念」という点においてはこれ以降は深堀りしていく必要はあるだろうが、おおむねの扱い方を学ぶ上では十分すぎる内容だと感じた。

サンプルアプリも内容が充実していると感じたので、Next.jsのApp Routerを学びたいという人にはオススメできると感じる。

ちまちまやってて長くかかりましたがここまで

このスクラップは2023/12/15にクローズされました