
t0mmy
学習履歴詳細
実践 Next JS 9章 データ更新とUI 読了
やったこと
- 実践 Next JS 9章 データ更新とUI 読了
学んだこと
Server Action とは
Form からサーバーの非同期関数を直接呼び出すことが出来る機能。
従来の「API Client を介して Route Handler (API Route) を呼び出す」という手法と比較して、以下のメリットがある。
- API Client (中間コード)が不要
- Browser 向けにバンドルされていた API Client が少なくなる
- ハイドレーションが完了する前に実行できる
Typescript を採用していると、呼び出したい関数の型情報を参照できるため、型安全性が高まる。
Server Action を活用する
基本的な使い方
use server
ディレクティブを宣言する。
関数内部で宣言すると、該当関数のみ Server でのみ実行される非同期関数とみなされる。
export default function ServerComponent() { const myAction = async () => { "use server"; ... } }
または、use client
の時のように、ファイルの先頭に記述することもできる。
この場合、同ファイルに記述された関数全てが Server でのみ実行される非同期関数とみなされる。
Server Action は、 <form>
要素の action 属性に渡して使用するのが一般的。
import {myAction} from "ServerActionを定義したファイルのパス" ... <form action={myAction}> // any code </form> ...
form の action 属性に設定した関数は、第一引数に、 Web 標準の FormDate オブジェクトが渡される。
受け取り側(上記コードだと myAction
関数)は、 formData.get("id)
のように、<input>
要素のデータを参照する。
参考: https://developer.mozilla.org/ja/docs/Web/API/FormData
引数のバインド
関数オブジェクトの bind
メソッドを使用して、 Server Action に引数をバインドできる。
これにより、引数バインド済みの新しい Server Action を作成できる。
この時、FormdData は、バインドされた引数の後ろに渡される。
Progressive Enhancement
従来、<form>
の aciton にはイベントハンドラをアタッチしていた。
これには、ハイドレーションが完了するまで Form イベントを送信できないという欠点がある。
Server Action のように、action に、 Server 側で動作する非同期関数を直接アタッチする場合、ハイドレーションは不要となる。
これは、ハイドレーションが機能しない(≒Javascript が動かない)状態でも動作することを意味している。
この、「Javascript が機能しない状態でも <form>
を機能させる」という実装方針を、Progressive Enhancement と呼ぶ。
action
イベントに加え、 onSubmit
イベントハンドラも同時に設定可能。
この場合、以下のように動作する。
- ハイドレーション未完了:
action
- ハイドレーション完了:
onSubmit
onSubmit
だと、バリデーションをはじめとしたクライアント側ロジックを実装できる。
このため、 onSubmit
は優れた UX を提供できる(代わりに、ハイドレーションが完了するまで動かない)。
action
と onSubmit
を同時に実装すると、
action
により最低限の動作を保証しつつ、 Javascript が有効な環境では、onSubmit
に実装したロジックにより、優れた UX を提供できる。
On-demand Revalidation
キャッシュを無効化するプロセスを On-demand Revalidation と呼ぶ。
(fetch の引数ではなく)以下専用の API を使用することで、任意のタイミングでキャッシュを無効化できる。
- revalidatePath... 特定の Route のパスでキャッシュを無効化する
- revalidateTag 特定のタグ文字列でキャッシュを無効化する
キャッシュを無効化するだけで、キャッシュを再作成するわけではない。
(キャッシュを作成するのは、次のリクエスト時)
On-demand Revalidation との対比で、 Time-based Revalidation を「Stale-whilre-revalidate」とも呼ぶ。
Server Action で On-demand Revalidation を使用すると、以下の利点がある。
- router.refresh() を実行せずとも「Router キャッシュ」がクリアされる
- redirect 関数を使用すると、更新と同時に画面遷移可能
この利点が大きいため、「データの作成、更新、削除」において、Route Hander よりも Server Action を優先した方が良い。
例外的に、「Next.js アプリケーションの外側から On-demand Revalidation を呼び出す」という処理を実現する場合は、Route Handler の On-demand Revalidation を採用できる。
ユースケース: ブログ記事
- 外部 CMS でブログ記事を書き、Next.js が、CMS からデータを取得するような構成
- ブログ記事更新後、 On-demand Revalidation を実行して、効率よくキャッシュを無効化できる
useFormState
Server Action と組み合わせて使用する、React 標準の Hook。
Form の状態を保持できる。Hook なので、 Client Component でのみ使用可能(そもそも Form 用だし、Client Compoennt でしか使わない)。
使い勝手は、React の useReducer 。
サーバー側で発生した事象を、「状態」という形でフロント側と共有できる。
const [state,dispatch] = useFormState( ServerAction に相当する関数, {state情報} );
useOptimistic
楽観的更新を実装できる、React 標準の Hook。
const [optimisticState,addOptimistic] = useOptimistic( state, (currentState,optimisticValue) => {} //楽観的に更新した状態を返す )
Revalidate の設計
On-demand Revalidation の考察
データの変更を反映させたい場合は、更新直後に On-demand Revalidation
を使用するとよい。
以下二種類が存在する(再掲)。
- revalidatePath... 特定の Route のパスでキャッシュを無効化する
- revalidateTag 特定のタグ文字列でキャッシュを無効化する
複数のキャッシュをまとめて無効化したい場合、 revalidateTag
を使用することになる。
fetch
関数に定義したtags["具体的なタグ名"]
を参照し、該当するタグをもつキャッシュをまとめて無効化できる
広範囲のキャッシュを頻繁に無効化すると、キャッシュが活きず、データアクセス速度が低下する。
一方で、局所的なキャッシュ無効化だけだと、キャッシュが悪さ(≒古い情報を提供)し、それを解消するための複雑なロジックを要する。
データ更新のユースケースに併せて、適切なタグの付与が必要。
- ページをまたいだデータの更新が必要 => 抽象的なタグを用いて一括キャッシュ無効化
- ID で区別された、一ページ分のデータの更新が必要 => 具体的なタグを用い、スコープを絞ってキャッシュ無効化
参考文献
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations
2024年06月02日(日)
2.0時間