Remix入門【はじめからそうやって教えてくれればいいのに!】
はじめに
この記事の内容は、以下の動画でも解説しています。アニメーションでわかりやすくなっているので、ぜひ見てみてください。他にもWebに関する解説動画を投稿しているので、気になる人はチャンネル登録よろしくお願いします!
Remixとは?
Remix とは、公式サイト によると、React ベースのフルスタックの Web フレームワーク のことです。
Remix is a full stack web framework that lets you focus on the user interface and work back through web standards to deliver a fast, slick, and resilient user experience.
(訳)Remixはフルスタックのウェブフレームワークで、ユーザーインターフェイスに集中し、ウェブスタンダードを通して、高速で、スムーズで、弾力性のあるユーザーエクスペリエンスを提供します。
...と言っても、これだけだとよく分からないですよね。そこで、今回の記事では「Remix とは何か?」を簡単に理解できるよう、Remix の基本的な機能について解説していきます。
準備
まず、Remix を動かす環境を準備します。
Remixプロジェクトを作成するには、npx create-remix@latest
を実行します。その後、プロジェクトに関していくつか質問されるので、次のように答えます。
- プロジェクトの作成場所:
./remix-tutorial
- git repository を初期化するか: 「No」(今回はどちらでもOK)
- npm でパッケージをインストールするか: 「Yes」
セットアップが完了したら、ローカルサーバーを起動しましょう。
cd remix-tutorial
npm run dev
ブラウザで「http://localhost:5173」にアクセスすると、次のような画面が表示されるはずです。
これで、準備は完了です。さっそく始めていきましょう!
Remixの基本
Remix には、代表的な機能が5つあります。一つずつ紹介していきます。
Outlet
まず1つ目は「Outlet」と呼ばれる機能です。
Outlet とは、親ルート内において、子ルートがレンダリングされる場所を示すために使われるコンポーネントのことです。
Renders the matching child route of a parent route.
Outlet について理解するために、app/root.tsx
の中身を一旦すべて削除して、次のコードに書き換えましょう。
import { Links, Meta, Scripts, Outlet } from "@remix-run/react";
import "./tailwind.css";
export default function App() {
return (
<html lang="ja">
<head>
<meta charSet="utf-8" />
<Meta />
<Links />
</head>
<body>
<main className="p-6">
{/* <Outlet /> */}
</main>
<Scripts />
</body>
</html>
);
}
上記のコードの <main>
の中では、意図的に <Outlet />
というコンポーネントがコメントアウトされています。このコードを保存したら、画面の表示はどうなっているでしょうか?
答えは、次のように真っ白な画面が表示されます。
これは何故でしょうか?
実は Outlet が無いと、親ルートは子ルートを「どこに配置すれば良いのか」分からなくなってしまうのです。
そのため、Outlet を使って、どこに子供を表示するべきか教えてあげる必要があります。次のようなイメージです。
実際にやってみましょう!次のように、 <Outlet />
をアンコメントするだけです。
import { Links, Meta, Scripts, Outlet } from "@remix-run/react";
import "./tailwind.css";
export default function App() {
return (
<html lang="ja">
<head>
<meta charSet="utf-8" />
<Meta />
<Links />
</head>
<body>
<main className="p-6">
+ <Outlet />
- {/* <Outlet /> */}
</main>
<Scripts />
</body>
</html>
);
}
すると、ブラウザでは、元どおり「Welcome to Remix」と表示されます。
このように、Outlet コンポーネントを使うことで、親ルートに対して、子ルートがレンダリングされる場所を示すことができます。
meta
2つ目に紹介するのは「meta関数」です。
meta関数とは、HTML内の メタデータ を変更するために使われる関数のことです。
The
meta
export allows you to add metadata HTML tags for every route in your app.
そもそも「メタデータ」とは何でしょうか?
メタデータは、直訳すると「データについてのデータ」という意味です。簡単に言うと、Webページに関する情報を提供するデータのことを指します。
例えば、Googleで「Remix」と検索する場合を考えてみましょう。
上記の画像のように、赤枠の部分は「タイトル」と呼びます。そして青枠の部分は「メタディスクリプション」と呼ばれます。このタイトルとメタディスクリプションのように、データを説明するためのデータのことを「メタデータ」と呼びます。
Remix では、このメタデータを簡単に変更できるように、「meta関数」というものが用意されています。では、試しにメタデータを変更してみましょう。
export const meta: MetaFunction = () => {
return [
+ { title: "ずんだブログ" },
- { title: "New Remix App" },
+ { name: "description", content: "ずんだもんのブログなのだ" },
- { name: "description", content: "Welcome to Remix!" },
];
};
上記のように変更すると、タイトルは「ずんだブログ」、メタディスクリプションは「ずんだもんのブログなのだ」に設定できます。
検証ツールを使って確認してみると、変更が反映されていることが確認できます。
このように、meta関数を使うことで、各ページのメタデータを簡単に変更することができます。
loader
3つ目は「loader関数」です。
loader関数とは、レンダリング時にデータを取得するために、サーバ側で実行される関数のことです。
Each route can define a loader function that provides data to the route when rendering.
例えば、トップページに、投稿(Post)情報のタイトルをリスト形式で表示することを考えてみましょう。
この場合、レンダリング時に、投稿一覧のデータを DB から取ってくる必要があります。この データ取得 の仕事を行うのが、「loader関数」です。図にすると、次のようなイメージです。
loader関数は、サーバーでのみ実行される関数で、ページが表示される前に自動で実行されます。実際に、使ってみましょう!
使い方は簡単です。次のような関数を定義するだけです。
import { json } from "@remix-run/node";
export const loader = async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
const data = await response.json();
console.log(data)
return json({ posts: data });
};
必ず名前に loader
と付けて、先頭に export
を付けます、そうすると、loader関数になります。今回は、loader関数の中で、JSONPlaceholder という無料のフェイク API を利用しています。
ターミナルを確認すると、次のように表示されているはずです。(loader関数はサーバー側で実行される関数なので、ターミナルで確認します)
このように、loader関数を使うことで、サーバーレンダリング時に、データを簡単に取得することができます。
取得したデータを画面上に表示するには?(useLoaderDataについて)
「せっかくloader関数を使ってデータを取得できたんだから、ブラウザ上でもpostデータを表示させたい!」って人は、以下のようにコンポーネント内でuseLoaderDataというフックを使う必要があります。
const posts = useLoaderData<typeof loader>();
useLoaderData について詳しく知りたい方は、記事の冒頭でも紹介した Youtube 動画を是非見てみてください。useLoaderData についても、詳しく解説しています。
Dynamic Segments
4つ目は「Dynamic Segments(ダイナミックセグメンツ)」です。
Dynamic Segments とは、動的に変わるURLのパス部分をマッチさせて、その値をコード上で使えるようにする機能のことです。
Usually your URLs aren't static but data-driven. Dynamic segments allow you to match segments of the URL and use that value in your code. You create them with the
$
prefix.
例えば、投稿詳細ページなどのように、URLのパスの一部分を「1」「2」「3」と、動的に変更させたい場合がありますよね。次のようなイメージです。
そこで登場するのが、この Dynamic Segments です。具体的には、ファイルを作成する際に $
という記号を使用します。
実際に、app/routes
の中に posts.$postId.tsx
という名前のファイルを作ってみましょう。
(補足)ドット分割について
ファイルを作成するときに、$
の直前で使われている .
が何なのか気になった人もいると思います。これは「ドット分割(Dot Delimiters)」と呼ばれるもので、簡単に言うと、URLの /
を表します。
詳細は以下の公式ドキュメント読んでみてください。
そして、作成したファイルに簡単なコンポーネントを定義します。
export default function Post() {
return (
<div>
<h1 className="font-bold text-3xl">投稿詳細</h1>
</div>
);
}
コードを保存したら、ブラウザで以下のURLを開いてみてください。
いずれのページにも、「投稿詳細」という文字が、ちゃんと表示されていることが分かります。
このように、Dynamic Segmentsを使うと、動的に変わるURLのパス部分をマッチさせることができます。
action
最後は、「action関数」についてです。
action関数とは、データの変更などを行うために、サーバー側で実行される関数のことです。
A route action is a server only function to handle data mutations and other actions. If a non-GET request is made to your route (DELETE, PATCH, POST, or PUT) then the action is called before the loaders.
先ほど紹介した3つ目の機能である「loader関数」は覚えているでしょうか?action関数は、このloader関数と似た機能を持っています。
少し復習ですが、loader関数はデータを取得するために使われていて、レンダリング時に実行されるのでしたよね。これに対してaction関数は、データを更新するために 使われる関数です。つまり、POSTやDELETE、PUT、PATCH リクエストが送られた時に実行されます。
実際に使ってみましょう。まず、app/routes
の中に、posts.new.tsx
というファイルを作成します。そこに、次のようなコードを追加します。
import { ActionFunctionArgs } from "@remix-run/node";
import { Form, redirect } from "@remix-run/react";
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const title = formData.get("title");
const body = formData.get("body");
console.log(title, body);
return redirect("/");
};
export default function New() {
return (
<div>
<h1 className="font-bold text-3xl">投稿作成</h1>
<Form method="post">
<input type="text" name="title" className="border-2 block" />
<textarea name="body" className="border-2 block" />
<button type="submit" className="border-2 p-2">
作成
</button>
</Form>
</div>
);
}
まず、<Form>
から説明すると、これは Remix が用意しているコンポーネントです。これを使うことで、通常の HTMLの <form>
よりも簡単にフォーム処理ができます(※詳しくはこちらの公式サイトを参照)。そして、このフォームの method
には、「post」を指定しています。これにより、フォームが送信されると action 関数が呼ばれるようになります。
次に、action関数の中身を見てみましょう。action関数は、request
オブジェクトを引数に取り、送られたフォームのデータを取得しています。今回は、簡単な例として、このフォームデータからタイトルと本文を抽出して、ターミナルで表示させています。そして、最後にトップページにリダイレクトするようにしています。
以下の手順で、新しい投稿を作成してみましょう。
- ブラウザで「http://localhost:5173/posts/new」にアクセス
- フォームに適当なタイトルと本文を入力して、「作成」ボタンをクリック
すると、ターミナルに Remix action関数のテスト
と表示された後に、トップページにリダイレクトされるはずです。
このように、action関数を使うことで、フォームのデータを受け取り、サーバー側で処理を行うことができます。
おわりに
この記事の他にも、「100秒で理解する」というシリーズを書いています。よかったら見てください!
- 100秒で理解するHTTP Cookie
- 100秒で理解するCDN
- 100秒で理解する仮想DOM
- 100秒で理解するBun
- 100秒で理解するPromise
- 100秒で理解するESM(ESModules)
Discussion