Remix@フレームワーク¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. Remixとは¶
Reactパッケージを使用したフレームワークである。
loader
---> component
---> action
の順に処理が実行される。
loader
は、レンダリング前にAPIからデータを取得する。component
は、レンダリング処理を実行する。action
は、APIリクエストやブラウザ操作に応じて、データを変更する。action
は、バックエンドのコントローラーと同様にクエリストリングやリクエストのコンテキストを受信する。
02. Remixの仕組み¶
loader¶
▼ loaderとは¶
loader
(ユーザーがloader
と命名した関数) は、レンダリング前にAPIからデータを取得する。
各エンドポイントごとに定義できる。
DBにクエリを送信し、データを取得できる。
認証処理がある場合、loader
関数の前に実行する必要がある。
*実装例*
import {json} from "@remix-run/node";
import {useLoaderData} from "@remix-run/react";
export const loader = async () => {
return json({
posts: [
{
slug: "my-first-post",
title: "My First Post",
},
{
slug: "90s-mixtape",
title: "A Mixtape I Made Just For You",
},
],
});
};
▼ useLoaderData¶
loader
関数で取得したデータを出力できる。
*実装例*
import {json} from "@remix-run/node";
import {useLoaderData} from "@remix-run/react";
export const loader = async () => {
return json({
posts: [
{
slug: "my-first-post",
title: "My First Post",
},
{
slug: "90s-mixtape",
title: "A Mixtape I Made Just For You",
},
],
});
};
export default function Posts() {
const {posts} = useLoaderData<typeof loader>();
return (
<main>
<h1>Posts</h1>
</main>
);
}
component¶
component
は、レンダリング処理を実行する。
action¶
action
は、APIリクエストやブラウザ操作に応じて、データを変更する。
バックエンドのコントローラーと同様にクエリストリングやリクエストのコンテキストを受信する。
componentを同じファイルに実装する以外に、.server
ディレクトリに切り分ける方法もある。
import type { ActionFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form } from "@remix-run/react";
import { TodoList } from "~/components/TodoList";
import { fakeCreateTodo, fakeGetTodos } from "~/utils/db";
// loaderでレンダリング前にデータを取得する
export async function loader() {
return json(await fakeGetTodos());
}
// componentで、レンダリング処理を実行する
export default function Todos() {
// useLoaderDataでloaderによる取得データを出力する
const data = useLoaderData<typeof loader>();
// Todoリストを出力する
return (
<div>
<TodoList todos={data} />
<Form method="post">
<input type="text" name="title" />
{/*
Create Todoボタンを設置する
同一ファイルのactionをコールする。
*/}
<button type="submit">Create Todo</button>
</Form>
</div>
);
}
// actionで、受信したリクエストに応じた処理を実行する
export async function action({request}: ActionFunctionArgs) {
const body = await request.formData();
const todo = await fakeCreateTodo({
title: body.get("title"),
});
return redirect(`/todos/${todo.id}`);
}
03. ディレクトリ構成¶
構成¶
.
├── app/
│ ├── components/ # ユーザー定義のRemix component
│ │
│ ├── models/ # モデルのCRUD処理
│ │
│ ├── routes/ # ルーティングとレンダリングの処理
│ │
│ ├── utils/ # 汎用的な関数
│ │
│ ├── entry.client.tsx
│ ├── entry.server.tsx
│ └── root.tsx
│
├── prisma/ # モデルの定義
...
root.tsx¶
アプリケーションのルートである。
link
タグ、meta
タグ、script
タグを定義する。
entry.client.tsx¶
マークアップファイルのハイドレーション処理のエントリーポイントである。
entry.server.tsx¶
レスポンス作成処理のエントリーポイントである。
RemixServer
で設定を変更できる。
04. ルーティング¶
UIとAPI¶
Rえmixでは、ブラウザまたはAPIへのルーティングが区別されず、両方を兼ねている。
ただし、ファイル名によって区別することもできる。
app/routes/api.<任意のパス>
ファイルまたはapp/routes/api/<任意のパス>
ファイルを作成する。
このファイルの処理は、APIとして処理される。
ドッド分割¶
▼ _index.tsx
¶
ルートパスになる。
app/ # URLパス
├── routes/
│ ├── _index.tsx # /
└── root.tsx
▼ <ルート以降のパス>.tsx
¶
ルート以降のパスを設定する。
app/ # URLパス
├── routes/
│ ├── _index.tsx # /
│ ├── home.tsx # /home
│ ├── home.contents.tsx # /home/contents
└── root.tsx
*実装例*
// <ルート以降のパス>._index.tsx
export default function Foo() {
// 返却するHTML要素
return (
<main>
<h1>Foo</h1>
</main>
);
}
▼ <ルート以降のパス>.<変数>.tsx
(動的セグメント)¶
動的にURLを決定する。
URLに規則性があるようなページに適する。
app/ # URLパス
├── routes/
│ ├── _index.tsx # /
│ ├── home.tsx # /home
│ ├── home.contents.tsx # /home/contents
│ ├── user.$id.tsx # /user/{任意の値}
└── root.tsx
*実装例*
// posts.$postId.tsxファイル
export default function Post() {
return (
<div>
<h1 className="font-bold text-3xl">投稿詳細</h1>
</div>
);
}
以下のURLでページをレンダリングできる。
- /posts/1
- /posts/2
- /posts/3
▼ 子の_<ルート以降のパス>.tsx
¶
子のファイル名にプレフィクスとして _
(パスレスルート) をつける。
これにより、親からレイアウトを引き継ぎつつ、パスは引き継がない。
*実装例*
_home.auth.tsx
ファイルは、親のhome.tsx
ファイルのレイアウトを引き継いでいる。
しかし、/home/auth
パスではなく、/auth
パスになる。
app/ # URLパス 引き継ぐレイアウト
├── routes/
│ ├── _index.tsx # / root.tsx
│ ├── home.tsx # root.tsx
│ ├── home._index.tsx # /home home.tsx
│ ├── home.contents.tsx # /home/contents home.tsx
│ ├── home_.mine.tsx # /home/mine root.tsx
│ ├── _home.auth.tsx # /auth home.tsx # 親からパスを引き継がない
│ ├── user.$id.tsx # /user/{任意の値} root.tsx
│ ...
│
└── root.tsx
▼ 親の_<ルート以降のパス>.tsx
(親がパスレスルート)¶
親のファイル名にプレフィクスとして _
(パスレスルート) をつける。
*実装例*
_auth.<任意の名前>.tsx
ファイルは、親の_auth.tsx
ファイルのレイアウトを引き継いでいる。
しかし、全てのファイルのURLにauth
が含まれない。
app/ # URLパス
├── routes #
│ ├── _auth.tsx #
│ ├── _auth.login.tsx # /login
│ ├── _auth.password.reset.tsx # /password/reset
│ ├── _auth.register.tsx # /register
...
// _auth.tsxファイル
import {Outlet} from "@remix-run/react";
import {SiteFooter, SiteHeader} from "~/components";
export default function AuthCommon() {
return (
<div className="grid grid-rows-[auto_1fr_auto] h-dvh">
<SiteHeader />
{/* Outletに子 (login、password.reset、register) を出力する */}
<Outlet />
<SiteFooter />
</div>
);
}
ディレクトリ分割¶
ディレクトリ名がパスとして認識される。
05. componentの種類¶
ユーザー定義¶
Remixがcomponentであることを認識するために、名前の先頭を大文字する。
Form¶
form
タグをレンダリングする。
action
値を省略した場合、フォームの入力データは他に送信されず、そのコンポーネント内のみで処理される。
action
値を/foos?index
パスとした場合、routes/foos/index.jsx
ファイルにデータを送信する。
一方で、action
値を/foos
パスとした場合、routes/foos.jsx
ファイルにデータを送信する。
*実装例*
ここではaction
値を省略している。
import {Form} from "@remix-run/react";
function NewEvent() {
return (
<Form action="/events" method="post">
<input name="title" type="text" />
<input name="description" type="text" />
</Form>
);
}
Meta¶
Webページのmeta
タグ (Webサイト名、説明など) をレンダリングする。
import {Meta} from "@remix-run/react";
export default function Root() {
return (
<html>
<head>
<Meta />
</head>
<body></body>
</html>
);
}
Outlet¶
親ページ内に子ページをレンダリングする。
import {Outlet} from "@remix-run/react";
export default function SomeParent() {
return (
<div>
<h1>Parent Content</h1>
<Outlet />
</div>
);
}
06. エラー¶
データ名 | 説明 | 例 |
---|---|---|
state | ステータスコード | 405 |
statusText | ステータスコードのエラーメッセージ | Method Not Allowed |
data | 詳細なエラー | Error: ***** |