Webサイト改築計画 No.6 -Next.js with microCMS-


2024年2月27日
目次
前回のあらすじ
Next.jsとTailwind CSSを使ってサイトに必要な要素を作りました。
microCMS -とりあえずAPIを使ってみよう-
今回は記事呼び出しをやっていきます。
作成中のサイトでは、記事一覧→記事という階層構造を持たせます。
要は本サイトとおおよそ同じ構造です。そこで、まずはmicroCMSから記事の一覧を取ってきます。
こちらの内容を使います。
まずはAPIキーを隠します。ルートディレクトリに.env.localと.env.development.localファイルを作成し、APIキーを記述します。
gitignoreの書き換えを忘れずに。
そして、記事一覧の取得を試みます。(この段階のコードは参考サイトのものを流用)

これで一覧は読み込めました。次は中身です。
Next.jsのダイナミックルーティングを使い、リンクから記事を生成します。
Next.js -ダイナミックルーティング-
Next.jsにはダイナミック(動的)ルーティングという仕組みがあります。
任意の文字列を[]で囲んだ名前にしたディレクトリを作成し、その中にpage.tsxを作成します。
すると、例えば以下のような構造だった場合
src ┣articles ┃ ┣[slug] ┃ ┃ ┗page.tsx...② ┃ ┗page.tsx...① ┗page.tsx
https://hogehoge.xxx/articles/ にアクセスすれば、①に
https://hogehoge.xxx/articles/aaa にアクセスすれば、②にアクセスできます。
この時のaaaは任意の文字列で動きます。
aaa部分には記事のidを入れ、page.tsxはidからで記事を呼び出そうという魂胆です。
ちなみに、slugは新聞から来ている単語だそうです。
slugという単語の使い方は調べても曖昧だったので詳しく言及しませんが、上記のaaaに当たる部分をさす単語だと理解しました。
microCMS -記事の内容を呼び出す-
ここでVercelやNext.jsの仕様に悩まされました。
URLから指定したidを抽出することは以下の構文でできました。
const Pathname = usePathname();
const pathArray = Pathname.split('/');
const current = pathArray[pathArray.length - 1];
しかし、先述した記事と同じコードを打ち込んでもVercelで動きません。
始めに、asyncとawaitの構文問題にぶち当たりました。
こちらはpageのメイン部分に呼び出し部分を直書きするのではなく、コンポーネントを分けることで解決しました。
そして、上記のプログラムで得られたid(current定数)を使い、コンポーネントを呼び出します。
<FetchContents params={{postId: current}}/>
...まだ動きません。(それどころかアクセスすると真っ黒になる)
問題は環境変数にありました。
というのも、利用しているのはServer Componentsだったようで、環境変数の頭にNEXT_PUBLIC_という接頭語が必要だったみたいです。
というわけで export const revalidate = 0; をコードに挿入して
libsディレクトリに保存したtsxファイル、.env.local、.env.development.localの3ファイルに書いてある環境変数名を書き換えると。

遂に読み込みができました。基幹システムの完成まであと一歩です。
microCMS -記事に出てくる要素を配置する-
では次に、記事に出てくる画像を始めとした要素を並べます。
まずはアイキャッチ(サムネイル)の画像です。
microCMSの APIプレビュー → 取得 をやってみると、記事の内容がjson形式で送信されていることがわかります。
アイキャッチは"eyecatch"オブジェクトとして受け取られているので、ここから要素を取り出します。
ただし、単にpost.eyecatch.urlと書くとエラーが吐かれます。
この構文にすると、エラーがなくなります。
{post.eyecatch && (
<Image
src={post.eyecatch.url}
alt="eyecatch"
width={post.eyecatch.width}
height={post.eyecatch.height}
/>
)}
この意味をGPT3.5に問うと、こう返ってきました。
{post.eyecatch && ...} の部分は、JavaScript の短絡評価を利用しています。
post.eyecatch が truthy(真の値)である場合にのみ、その後の JSX コードが評価されます。
つまり、post.eyecatch が存在し、かつ空でない場合に、<Image> コンポーネントがレンダリングされます。
ただし、post.eyecatch が存在しない場合や、eyecatch プロパティの値が null や undefined である場合に、post.eyecatch.url として url プロパティを抽出しようとすると、エラーが発生します。このようなエラーを避けるために、短絡評価を使用しているのです。
したがって、このコードでは、post オブジェクトがAPI経由で取得されると仮定して、その中に eyecatch プロパティが存在する場合にのみ、その中の url、width、height プロパティを取得して
流石わかりやすいです。
しかし、Imageコンポーネントで外部リンクを呼び出すのは難儀です。
imgタグを使うとすんなり解決します。
加えて、microcmsから呼び出されるタグはimgです。
故に、microcms内ではImageコンポーネントを切り捨てます。
改めてeyecatchのを別の方法で定義します。
const post = await getDetail(postId);
const eyecatch = post.eyecatch && (post.eyecatch.url)
これでeyecatchにはURLのみが入ります。
<img src={eyecatch} alt="eyecatch"/>
普通にimgで呼び出せます。
Next.js -a client-side exception-
記事を見ていると、画面が暗転し、このようなエラーを吐きます。
Application error: a client-side exception has occurred (see the browser console for more information).
指示通りconsoleを見ると、3つのエラーが得られました。
Uncaught Error: Minified React error #329
ご丁寧なリンクがあったので英語ですが読みましょう。
エラーメッセージは"Unknown root exit status."でした。
全然なんのことなのかわかりません。
他には#482, #423のエラーが吐かれていました。
致命的なのはこっちの方なのでこちらの解決が先決です。
Minified React error #482
async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.
Minified React error #423
There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
この様子を見るに、サーバーサイドレンダリングとクライアントサイドレンダリングがかち合って何かしら問題を起こしている模様です。
先程記事を呼び出す際に、私は元のコードを弄って動くようにしました。
#482の指示通り、use clientを一部のコードから排除します。
現状エラーは吐かなくなりました。
基幹システムはほとんど完成しました。次回で仕上げを行い、公開までこぎつけます。