Next.js のServer Actionsを使ってみる 

# Next.js Server Actions

Next.jsのServer Actionsについて簡単なアプリを作ってみたので情報を共有します。

すぐ動くコードが見たい方は、Codesandboxで御覧ください。

https://codesandbox.io/p/sandbox/brave-davinci-j8ft92

## Server Actionsの設定

### Configファイル

Server Actionsを使うためには、next.config.jsファイルのexperimentの項目にServer Actionsを追加します。

```tsx
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true, //<=======!!!
  },
}

module.exports = nextConfig
```

## serverファイル

次にAppフォルダー内に新しいファイル（ここではserver.tsとします）を作成し、その中に先頭に'use server'を追加し、あとはサーバー側の関数などを追加していきます。

```tsx
// server.ts
"use server";

const votes:votes = {
    cat: 3,
    dog: 2,
};

export const getVotes = () => votes;

export const vote = (animal: keyof typeof votes) => {
    votes[animal] += 1;
    return votes;
};

declare global {
    interface votes {
        cat:number
        dog:number
    }
}
```

use server内のファイルで定義された関数がエクスポートされると、それらは全て非同期関数となります。asyncの有無によらずです。

[公式ドキュメントに次のように書いています。](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#convention)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552712/58705b51-d732-6dd4-50eb-da02254a1f3d.png)


[Actionsとは](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#glossary)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552712/72e4762f-8bfe-bf94-dfc7-6cd8e583e23b.png)

### clientファイル

server.tsからエクスポートされたファイルは'use client'が先頭についたファイルでインポートすることができます。

インポートする側のファイル（App.tsx）の先頭に'use client'をつけましょう。

```tsx
// App.tsx
"use client";
import { useEffect, useState } from "react";
import { getVotes, vote } from "@/app/server";
export default function Home() {
  const [votes, setVotes] = useState<votes>({ cat: 0, dog: 0 });
  useEffect(() => {
    getVotes().then(setVotes);
  }, []);
  const totalVotes = votes.cat + votes.dog;
  return (
    <div>
      <p onClick={()=>vote("dog").then(setVotes)}>犬が好き {votes.dog}</p>
      <p onClick={()=>vote("cat").then(setVotes)}>猫が好き {votes.cat}</p>
    </div>
  );
}
```

繰り返しになりますが、インポートした関数は全て非同期関数となっています。

よって、_then()_ メソッドを使うことができます。

暗黙で非同期関数となっているので、次のようなTypeScriptのチェックが入りますが、気にせずに行きましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552712/69a7ab0c-0cf1-ab0f-c122-d2e5e11be2c9.png)


今回の実装では、アプリを立ち上げた時にuseEffectを通じて、getVotesを実行してサーバー側に保存してある値を取得し、セットします。

```tsx
"use client";
import { useEffect, useState } from "react";
import { getVotes, vote } from "@/app/server";
export default function Home() {
  const [votes, setVotes] = useState<votes>({ cat: 0, dog: 0 });

//-------------
  useEffect(() => {
    getVotes().then(setVotes);
  }, []);
//-------------
```

pタグで囲まれた”猫が好き”または”犬が好き”をクリックします。

すると、vote()関数がサーバー側で実行され、犬または猫、クリックされた方の値が更新されます。サーバーから戻ってきた値がuseStateを使ってセットされ、画面上の値が更新されます。

```tsx
"use client";
import { useEffect, useState } from "react";
import { getVotes, vote } from "@/app/server";
export default function Home() {
  const [votes, setVotes] = useState<votes>({ cat: 0, dog: 0 });

  ...

  return (
    <div>
      <p onClick={() => vote("dog").then(setVotes)}>犬が好き {votes.dog}</p>
      <p onClick={() => vote("cat").then(setVotes)}>猫が好き {votes.cat}</p>
    </div>
  );
}
```

これでサーバー側とクライアント側でロジックを分離することができました🎉

Server Actionsは実験的な機能で問題もあるかもしれないので使用の際はご注意ください。

以上です！

今回のコードが見たい方はこちら、

https://codesandbox.io/p/sandbox/brave-davinci-j8ft92

[元ネタはこちらのYouTubeです](https://youtu.be/fKSemrudovo)

ツイッターもやってます！

https://twitter.com/_adamof_