Remix入門: loader()とは?
Remix公式のINDIE STCKで説明します。
インストールはこのコマンドです。
npx create-remix@latest --template remix-run/indie-stack
ブログの記事ページです。
ディレクトリのroutes/notes/hogehogeがURLになります。
まずはファイルのルーティングからおさえていきますね。
これがファイル一覧です。
notes.がつくファイルが4つありますが、関連をもっています。
親のコンポーネントからOutletで子コンポーネントを呼び出します。呼び出したときにファイル名がURLになります。今回はブログ記事の単体ページなので$noteIDです。このドル記号をつけることで、記事のIDが動的にURLになります。
わかりやすく今回みていく記事の単体のページに緑色を付けます。
Reactをちょっと触ってる人はわかるかも知れません。コンポーネントがページになります。コンポーネントにURLを持たせるのがRemix流です。こまかく機能を管理できるので開発しやすいんだよね。
中身は、見出しと本文と削除ボタンのみなのでシンプルな構成です。
<div className="bg-green-500">
<h3 className="text-2xl font-bold">{data.note.title}</h3>
<p className="py-6">{data.note.body}</p>
<hr className="my-4" />
<Form method="post">
<button
type="submit"
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
>
Delete
</button>
</Form>
</div>
ブログの単ページのコード全文
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import {
Form,
isRouteErrorResponse,
useLoaderData,
useRouteError,
} from "@remix-run/react";
import invariant from "tiny-invariant";
import { deleteNote, getNote } from "~/models/note.server";
import { requireUserId } from "~/session.server";
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.noteId, "noteId not found");
const note = await getNote({ id: params.noteId, userId });
if (!note) {
throw new Response("Not Found", { status: 404 });
}
return json({ note });
};
export const action = async ({ params, request }: ActionFunctionArgs) => {
const userId = await requireUserId(request);
invariant(params.noteId, "noteId not found");
await deleteNote({ id: params.noteId, userId });
return redirect("/notes");
};
export default function NoteDetailsPage() {
const data = useLoaderData<typeof loader>();
return (
<div className="bg-green-500">
<h3 className="text-2xl font-bold">{data.note.title}</h3>
<p className="py-6">{data.note.body}</p>
<hr className="my-4" />
<Form method="post">
<button
type="submit"
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
>
Delete
</button>
</Form>
</div>
);
}
export function ErrorBoundary() {
const error = useRouteError();
if (error instanceof Error) {
return <div>An unexpected error occurred: {error.message}</div>;
}
if (!isRouteErrorResponse(error)) {
return <h1>Unknown Error</h1>;
}
if (error.status === 404) {
return <div>Note not found</div>;
}
return <div>An unexpected error occurred: {error.statusText}</div>;
}
コードの気になるところ解説
コードを俯瞰して見てみよう
インポートを含めた全体の機能一覧です。
こうみるとシンプルな構成です。
上から
- loader()
- action()
- コンポーネント
- ErrorBoundary()
loader()とaction()とは?
サーバーサイド処理を記述します。
サーバーサイドの処理をTypeScriptで書きます。
この2個の関数loader()とaction()がRemixのすべてと言っても過言ではありません。Reactにサーバー処理をつけてチューニングしたのがRemixです。機能は少なめです。
コンポーネントとは?
フロントエンドを記述します。Reactの部分です。
Form
を呼ぶと、サーバーサイド処理が走ります。
なのでサーバーサイドにfetchを書いておいてコンポーネントでsubmitするとサーバーサイド側にデータが飛んでサーバーサイド側でFecthします。
つまり、外部APIを使う場合はこうなります。
ブラウザ→サーバー→外部API
え?遠回りになってるって?
実際に遠回りになっています。
遠回りしないで
ブラウザ→外部API
にしたい場合は、clientLoader
が用意されてます。
ErrorBoundary()とは?
エラーなった時の処理を書く場所。
おわり
Remixは慣れるまでドキュメント片手にやっていくのおすすめします。
Discussion