【11章】Next.jsのチュートリアルをやってみた

Next.js

 

これはNext.jsの公式チュートリアルの11. Adding Search and Pagination に関するメモです

 

前章のメモ

 

Next.jsの公式チュートリアルの該当ページ

 

Learn Next.js: Adding Search and Pagination | Next.js
Add search and pagination to your dashboard application using Next.js APIs.

 

学ぶこと

 

  • useSearchParams, usePathname, useRouter の使い方
  • クエリパラメータを使用した検索とページネーションの実装

 

下準備

 

/app/dashboard/invoices/page.tsx を下記のコードにまるっと書き換えます

 

使用しているコンポーネントを見ていくと

  1. <Search/> :検索窓
  2. <Pagination/> :ページネーション
  3. <Table/> :請求書データを表示する表

検索処理の挙動としては

  1. ユーザが請求書を検索
  2. クエリパラメータを更新
  3. サーバでデータを取得
  4. 取得したデータをもとにサーバ上で請求書の表を再レンダリング

 

クエリパラメータを使う検索のメリットは??

 

  1. URLをブックマークに登録したり、共有できる
    • 検索キーワードによってURLが異なる
    • 検索後のURLに後からアクセスしたり、他の人に共有できる
  2. サーバ側レンダリングと初期ロード
    • クエリパラメータをもとにサーバ側で初期状態からレンダリングできる
    • ので、サーバレンダリングの処理が簡単になる
  3. 分析&追跡
    • 検索時のクエリやフィルターをURLに含めることでユーザの行動が追跡しやすい

 

検索機能の実装

 

検索機能の実装に必要なNext.jsのフックは

  1. useSearchParams
    • クエリパラメータを取得する
    • /dashboard/invoices?page=1&query=pending なら、 {page: ‘1’, query: ‘pending’}
  2. usePathname
    • URLのパスを取得する
    • /dashboard/invoices?page=1&query=pending なら、’/dashboard/invoices’
  3. useRouter
    • コード上でページ間のナビゲーションできる
    • 公式ドキュメント
    • 特定のページへの移動やリロード、前のページへ戻るなどが可能

 

実装の流れとしては

  1. ユーザからの入力を受け取る
  2. クエリパラメータを使用したURLに更新
  3. URLと検索窓を同期
  4. 表の内容を更新

 

ユーザからの入力を受け取る

 

検索窓となる<Search> (/app/ui/search.tsx)を見てみます

特徴としては

  • ‘use client’;Client Componentへ ⇒ イベントリスナーやHooksが使える
  • <input> :検索する文字列を受け取る

 

/app/ui/search.tsx で<input>に文字列が入力されたときの挙動を見てみます

  1. 引数をコンソールに出力する handleSearch 関数を作成
  2. input の onChange に handleSearchを設定し、入力が変わり次第関数を実行

 

 

これでローカルサーバを起動(npm run dev) して、下記URLにアクセスしてみます

http://localhost:3000/dashboard/invoices

 

ブラウザで開発者ツールを開き(F12)、コンソールを表示した上で検索窓に文字を入力

 

クエリパラメータを使用したURLに更新

 

ここからは検索窓からの入力をクエリパラメータとしたURLに更新していきます

 

処理の内容としては

  1. useSearchParams (Client ComponentのHook) で現在のクエリパラメータを取得
  2. クエリパラメータを操作するために URLSearchParams を1. でインスタンス化
  3. 検索窓から取得した文字列によってクエリパラメータを操作
    1. 文字列があれば、それでクエリパラメータを上書き
    2.  〃 がなければ、クエリパラメータを削除
  4. 3. で生成したクエリパラメータを用いてuseRouterを使ってURLを更新
    1. 今回使用するrouter.replace() だとブラウザの履歴を上書き
    2. ちなみにrouter.push()だとブラウザの履歴を追加

 

useSearchParams (Client ComponentのHook) で現在のクエリパラメータを取得

 

 

クエリパラメータを操作するために URLSearchParams を1. でインスタンス化

 

 

検索窓から取得した文字列によってクエリパラメータを操作

 

 

生成したクエリパラメータを用いてuseRouterを使ってURLを更新

 

  1. usePathname()現在のパスを取得
    1. 今回の場合は、‘/dashboard/invoices’ を取得
  2. paramsに格納されているクエリパラメータをtoString()でURLに使える表記に変換
  3. router.replace() でパスとクエリパラメータを更新
  4. クライアント側のナビゲーションなのでページのリロードなしでURLが更新される

 

 

const params = new URLSearchParams(searchParams);
searchParams が ReadonlyURLSearchParams のため
エディター上でエラーになっている可能性ありです。そのままにしておくのは少しうーんという感じですが、
今回はチュートリアルをやることがもくてきなのであえてスルーします

 

http://localhost:3000/dashboard/invoices にアクセスして検索窓に文字を入力するとURLも更新されていることが確認できます

 

URLと検索窓を同期

 

検索窓に文字を入力してURLが更新されるようになりましたが、その逆はまだです。

つまり、URLで指定されたクエリパラメータを検索窓に反映させるということ。

 

なので、例えば http://localhost:3000/dashboard/invoices?query=search にアクセスしても

検索窓に何も文字列がありません

 

<input> の defaultValue にURLから取得したクエリパラメータを指定すればOK

 

 

http://localhost:3000/dashboard/invoices?query=search にアクセスしてみると

検索窓に’search’が表示されてます

 

検索結果を表示

 

検索ワードによってURLは更新したので、あとは検索結果を表示します

page.tsxのPageコンポーネントでは引数からクエリパラメータ等を取得できます

 

使用できる引数は2種類で

  1. params:dynamic route においてURLから取得する値
  2. searchParams:クエリパラメータ(今回はこちらを使用)
クエリパラメータの取得方法
useSearchParamsクライアントコンポーネントで使う
searchParamsサーバーコンポーネントで使う

/app/dashboard/invoices/page.tsx の引数で受け取って<Table>に渡しコメントアウトを外す

 

<Table>コンポーネントを見ると、受け取ったクエリとページをもとにデータをfetchしてます

 

これでページにアクセスするとテーブルが表示されます

 

検索窓に’lee’と入力してみると、ヒットする結果のみ表示されます

 

デバウンス

 

デバウンス関数が実行される頻度を制御すること

デバウンスの流れ

  1. トリガーイベント:イベントが発生するとタイマーを開始
  2. 待機:指定した時間が経過するまで待つ。新しいイベントが発生したらリセット
  3. 実行:時間が経過するとデバウンスした関数を実行

 

今回の検索機能だと文字が入力されるたびにDBからデータを取得しています

ユーザが多くなるほどDBへの負荷も高まりパフォーマンスが低下↓

なので、ユーザが入力を停止したタイミングでDBにデータを取りに行きたい

 ⇒  handleSearch() をデバウンス。

 

デバウンスするにはいくつか方法がありますが、今回はuse-debounceというライブラリを使用

まずはインストール。

 

useDebouncedCallbackをインポートしてhandleSearch()をラップ。

第2引数に待機時間(ms)を指定

 

これでページにアクセスして開発者ツール(F12)でコンソールを見ます。

検索窓に文字を入力すると、入力する度ではなく入力が停止したタイミングで表示されます

 

ページネーションの実装

 

さて、/app/dashboard/invoices/page.tsx の fetchFilteredInvoices() を見ると最大6のデータしか取得していないことがわかります

 

6件以上データがヒットする場合もあるのでページネーションを実装します。

検索機能と同じくクエリパラメータを使用

 

実装の流れ

  1. 検索ワードにヒットするデータ数からトータルのページ数を取得
    1. <Pagination>はクライアントコンポーネントなので
      ServerComponentでfetchInvoicesPages()を実行して結果を渡す。
    2. 1ページあたり6件のデータを表示する
      1. 例えば、12件ヒットしたら、fetchInvoicesPages()は2を返す
  2. <Pagination> でパスやクエリパラメータを取得
  3. <Pagination> でページネーションにて使用するリンクを作成
  4. <Search> で検索ワードが入力されたらページを先頭へ

 

検索ワードにヒットするデータ数からトータルのページ数を取得

 

<Pagination>はクライアントコンポーネント。

なので、ServerComponentでfetchInvoicesPages()を実行して結果を渡す。

 

また、1ページあたり6件のデータを表示。

例えば、12件ヒットしたら、fetchInvoicesPages()は2を返すようになってます。

 

 

<Pagination> でパスやクエリパラメータを取得

 

検索機能と同じくusePathnameuseSearchParams を使います

 

 

<Pagination> でページネーションにて使用するURLを作成

 

取得したクエリパラメータのpageを引数でURLを更新して返す

この関数で生成されたURLがページネーションで使用するURLになる

(同ファイルのPaginationNumberPaginationArrowのhref属性をご覧ください)

PaginationNumber:ページネーションの数字の部分
PaginationArrow:両端の矢印の部分また、表示する数字のテキストは /app/lib/util.tsgeneratePagination で定義

 

ついでにコメントアウトも外しておきます

 

 

<Search> で検索ワードが入力されたらページを先頭へ

 

クエリパラメータのpageに1をセットして検索後は最初のページを表示

 

 

 

これでページにアクセスしてみると下部にページネーションが表示されてます!!

 

まとめ

 

  • クエリパラメータの取得、追加、削除
  • URLのパラメータを使用した検索とページネーションの実装
  • useRouterを使用した画面遷移

 

次章のメモ

 

コメント

タイトルとURLをコピーしました