actionとは
loaderと同様にサーバーサイドで処理される関数で、
GET以外のメソッド(POST、PUT、PATCH、DELETE)で呼び出しが行われたときに実行される関数。
フォームを送信したいときや、バックエンドにデータ送信を行いたい場合に利用する。
フォームから単にPOSTリクエストを送る
JSONPlaceholder - Free Fake REST API にPOSTリクエストを送信する処理を実装する。
jsonplaceholderは以下のようにダミーのPOSTメソッドが用意されていて、
叩くとリクエストボディと同じ結果が返ってくるので、POSTの確認はこれを用いて行う。
$ curl -XPOST "https://jsonplaceholder.typicode.com/posts" -H 'Content-Type: application/json' -d '{"title":"hoge"}'
{
"title": "hoge",
"id": 101
}
■ app/routes/posts.tsx
フォームに文字を入力し、jsonplaceholderにPOSTする処理を実装する。
Remixの Form
は少し特別で、input要素で入力された要素がaction関数の引数に渡り、action関数が実行される流れとなる。
参考:Form | Remix
import { ActionFunctionArgs } from "@remix-run/node";
import { Form, redirect } from "@remix-run/react";
export async function action({ request }: ActionFunctionArgs) {
// フォームに入力されたデータを取得
const body = await request.formData();
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: body.get("title"),
}),
});
const data = await response.json();
console.log({ data }); // ログ出力で実行を確認
return redirect("/"); // 任意のページにリダイレクト(今回はTOPページ)
}
export default function Posts() {
return (
<>
<h1>フォームサンプル</h1>
<Form method="post">
<input type="text" name="title" />
<button type="submit">Create post</button>
</Form>
</>
);
}
■結果

16:56:05 [vite] hmr update /app/routes/posts.tsx (x16)
{ data: { title: 'ほげほげ', id: 101 } }
フォームに入力された内容がjsonplaceholderにPOSTされ、レスポンスがログ出力されていることが確認できる。
POSTしたレスポンスデータを使って画面に表示する
POSTした結果のレスポンスボディを、画面に表示したいときもある。
その場合は useActionData
を使うと良い。
■ app/routes/posts.tsx
import { ActionFunctionArgs } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
export async function action({ request }: ActionFunctionArgs) {
const body = await request.formData();
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: body.get("title"),
}),
});
const data = await response.json();
// レスポンスボディを返却する
return {
id: data.id,
title: data.title,
};
}
export default function Posts() {
// これでactionの返り値を取得できる
// `typeof action`でレスポンスの型を推論してくれる
const data = useActionData<typeof action>();
return (
<>
<h1>フォームサンプル</h1>
<Form method="post">
<input type="text" name="title" />
<button type="submit">Create post</button>
</Form>
<div>
<ul>
<li>{data?.id}</li>
<li>{data?.title}</li>
</ul>
</div>
</>
);
}
■送信前

■送信後

番外編:ボタンを押して任意のPOSTリクエストを送る
これはクライアントサイドで実行することを想定する。
これはRemix関係なく、普通にReactのコードを書くだけで良い。
もし認証情報が必要な場合は、
📄【Remix】ブラウザから呼ぶプロキシAPIを作る を参考にプロキシAPIを作成し、そこを経由することで認証させるのが望ましい。
■ app/routes/posts.tsx
import { useState } from "react";
interface Post {
id: number;
title: string;
body: string;
}
export default function Posts() {
// ReactのuseStateにより状態管理
const [post, setPost] = useState<Post | undefined>(undefined);
// jsonplaceholderにPOSTリクエストを行い、レスポンスデータを`post`に設定する
const fetchPost = async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "title",
body: "body",
}),
});
const data = (await response.json()) as Post;
setPost(data);
};
return (
<>
<h1>フォームサンプル</h1>
<button type="submit" onClick={fetchPost}>
POSTデータを送信してレスポンスを取得するボタン
</button>
<p>id: {post?.id}</p>
<p>title: {post?.title}</p>
<p>body: {post?.body}</p>
</>
);
}
■ボタン押下前

■ボタン押下後
