❗️

Server Actionsはデータフェッチにも使える

2024/06/08に公開1

Next.js においてデータフェッチの主なアプローチは Server Component 内でのデータフェッチです。

ただし、Server Component ではなく、Client Component 内でデータフェッチしたい場合もあると思います。そんなとき、Server Actions を Client Component から呼ぶことでデータフェッチできます。

Next.js のドキュメントでは、Server Actions はデータミューテーションのためのものと紹介されています。ただ、Server Actions の関数の戻り値は呼び出し側から参照することができます。よって、データフェッチの目的でも Server Actions を使うことが可能です。

具体的には、下記のようなコードで実現できます。

fetchDataAction.ts
"use server";
export async function fetchDataAction() {
  // ここでDBからデータ取得など...

  return { message: "Hello, Hoge!" };
}
HogeClientComponent.tsx
"use client";
import { useEffect, useState } from "react";
import { fetchDataAction } from "./fetchDataAction";

export function HogeClientComponent() {
  const [message, setMessage] = useState("");

  useEffect(() => {
    fetchDataAction().then((data) => {
      setMessage(data.message);
    });
  }, []);

  return <div>message: {message}</div>;
}

HogeClientComponentではuseEffect内で Server Actions でデータフェッチしています。従来、useEffect内で Route Handler などへの HTTP リクエストを送っていた箇所を、Server Actions の呼び出しに変えただけです。

こうすることで、ClientComponent からデータフェッチしたいという目的のためにRoute Handler を定義して、それを fetch で呼び出すことをせずに、代わりにServer Actions を定義して、それを関数として呼び出すことができます。

ちなみに、useEffectではなく、SWR を使うこともできます。

HogeClientComponent.tsx
"use client";
import useSWR from "swr";
import { fetchDataAction } from "./fetchDataAction";

export function HogeClientComponent() {
  const { data, error, isLoading } = useSWR("fetchDataAction", fetchDataAction);

  return <div>message: {data?.message}</div>;
}

実は、useEffect内で Server Actions を呼び出すコード自体は Next.js のドキュメントで紹介されています。

app/view-count.tsx
// Next.js のドキュメントより引用
"use client";

import { incrementViews } from "./actions";
import { useState, useEffect } from "react";

export default function ViewCount({ initialViews }: { initialViews: number }) {
  const [views, setViews] = useState(initialViews);

  useEffect(() => {
    const updateViews = async () => {
      const updatedViews = await incrementViews();
      setViews(updatedViews);
    };

    updateViews();
  }, []);

  return <p>Total Views: {views}</p>;
}

このサンプルコードでもincrementViewsの戻り値を参照していますが、incrementViewsの命名の通り、どちらかというとデータミューテーションとして紹介されています。

Server Actions はもちろんデータミューテーションに使えますが、先ほど説明した通り、ミューテートせずに、フェッチだけのために使うことも可能です。

Discussion

akfm_satoakfm_sato

できるできないで言うとできるのですが、Next.jsの設計思想として「データ取得はRSCで」「mutationはServer Actionsで」という責務分離がなされてるので、あまり言及されてないものと私は認識しています。

Client Component 内でデータフェッチしたい場合

なぜClient Componentsでデータフェッチしたいかによりますが、上述の例で言うと何かしらの理由で遅延させたいだけだと思うので、Next.jsにおいてこれはSuspendしてしまえばStreaming SSRによって遅延レンダリングにすることが可能です。
https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#example

ちなみにServer ActionsはJSXを返すこともできるのですが、これはReactコアチームメンバーがXで「自己責任で」と言ってたので実質非推奨のようです。