Next.jsの Client Componentで async/await を使う
クライアントコンポーネントでも async/await は使える
クライアントコンポーネントをサーバ上で動かす際、async/await が使えないという誤解があります
その誤解を解くために最小限のサンプルを用意しました
このサンプルではサーバ上で非同期データを取得し HTML で出力、ブラウザ上で Hydration して動作します
重要な点は、throw property.promiseの部分です。ここで Promise が解決された際に、コンポーネントが再レンダリングされます。この機能は React のrenderToReadableStream実行時の標準動作です。<Suspense>で囲むと、非同期待ちではなくストリーミングになってしまうので注意が必要です
クライアントコンポーネントは Hydration 時、サーバ側で持っていたデータを失います。サーバコンポーネントと連携する際は、このデータの引き渡しが自動で生成されますが、クライアントコンポーネント単独の場合は、自分でやり取りする必要があります。この引き渡し作業は自動で生成される内容とほぼ同じです。
サンプルコード
https://github.com/SoraKumo001/next-client-async
サンプルを Vercel にデプロイしたもの
https://next-client-async.vercel.app/
1"use client";2import { useRef, useSyncExternalStore, type FC } from "react";34const DATA_NAME = "DATA_TEST";56const Weather: FC<{7 property: { data?: unknown; promise?: Promise<void> };8}> = ({ property }) => {9 const data = useSyncExternalStore(10 () => () => {},11 () => property.data,12 () => {13 if (!property.data) {14 if (typeof window !== "undefined") {15 // ブラウザ側でHydration直後にデータを受け取る16 const node = document.getElementById(DATA_NAME);17 property.data = JSON.parse(node.innerHTML);18 } else if (!property.promise) {19 // サーバ(物理)動作時に天気予報データを取得20 const getWeather = async () => {21 const result = await fetch(22 `https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json`23 );24 property.data = await result.json();25 };26 property.promise = getWeather();27 // Promiseを解決後、再レンダリングさせる28 throw property.promise;29 }30 }31 return property.data;32 }33 );34 return (35 <>36 {/* クライアントへ渡すデータをHTML化 */}37 <script38 id={DATA_NAME}39 type="application/json"40 dangerouslySetInnerHTML={{41 // データをJSON文字列に変換と、HTML用にエスケープ42 __html: JSON.stringify(data).replace(/</g, "\\u003c"),43 }}44 />45 {/* 確認表示用 */}46 <pre>{JSON.stringify(data, null, 2)}</pre>47 </>48 );49};5051const Page = () => {52 const property = useRef<{53 data?: unknown;54 promise?: Promise<void>;55 }>({}).current;56 return <Weather property={property} />;57};5859export default Page;
まとめ
Next.js のクライアントコンポーネントは、サーバ上で async/await を使うことができます。そこで取得したデータは、Server Component と同様に HTML に埋め込んでブラウザ側に引き渡し、Hydration 後も利用可能です。この動作は React の標準動作内で実行されるため、クライアントコンポーネントに限らず、Next.js の PagesRouter や React Router など、他の環境でも同様に利用できます。