Open14

チュートリアルをやりながらRemixに入門する

sugisugi

--template フラグを付けずによりミニマムに始めることもできるようだが、今回はチュートリアル通りにテンプレートを使用する。

npx create-remix@latest --template remix-run/indie-stack blog-tutorial
sugisugi

ルーティングは app/routes/ の中に作成されたディレクトリやファイルが、そのままアプリケーションのURLとなるようだ。このあたりはNext.jsと同様。

以下のように app/routes/posts._index.tsx を作成すると、 /posts としてアクセスが可能。

app/routes/posts._index.tsx
export default function Posts() {
  return (
    <main>
      <h1>Posts</h1>
    </main>
  );
}
sugisugi

データの読み込み

ブログ記事となるデータを読み込む。
Remixでは loader という関数がサーバーのデータを読み込む際に使用される。
そして useLoaderData 関数を通して、コンポーネントへ繋がれる。

app/routes/posts._index.tsx
+ 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>
  );
}
sugisugi

データベースから取得する

今回使用したテンプレートではPostgreSQL + Prismaが使用されている。

  • schema.prisma を定義
  • マイグレーション
  • prisma/seed.ts に投稿データを追加

上記3つの手順で、無事DBからの取得に切り替えられた。

sugisugi

ダイナミックルーティング

ブログ記事のような体裁は同じで内容だけ異なるページの動的な生成は app/routes/posts.$slug.tsx という形式でファイルを作成することで実現できる。

sugisugi

Remixは以下のように相対パスが使えるとのこと。
わざわざ書かれているってことは他のフレームワークでは使えないことも多いのか?

<Link to="admin" className="text-red-600 underline">
  Admin
</Link>
sugisugi

Reactの <Outlet /> を使えば共通部分はそのままに差異のある部分だけを変更可能。

app/routes/posts.admin.tsx
import { json } from "@remix-run/node";
import {
  Link,
  Outlet,
  useLoaderData,
} from "@remix-run/react";

import { getPosts } from "~/models/post.server";

export const loader = async () => {
  return json({ posts: await getPosts() });
};

export default function PostAdmin() {
  const { posts } = useLoaderData<typeof loader>();
  return (
    <div className="mx-auto max-w-4xl">
      <h1 className="my-6 mb-2 border-b-2 text-center text-3xl">
        Blog Admin
      </h1>
      <div className="grid grid-cols-4 gap-6">
        <nav className="col-span-4 md:col-span-1">
          <ul>
            {posts.map((post) => (
              <li key={post.slug}>
                <Link
                  to={post.slug}
                  className="text-blue-600 underline"
                >
                  {post.title}
                </Link>
              </li>
            ))}
          </ul>
        </nav>
        <main className="col-span-4 md:col-span-3">
          /* 以下が置き換わる */
          <Outlet />
        </main>
      </div>
    </div>
  );
}
sugisugi

<Outlet /> はコードの共通化ができるだけでなく、共通部分は再レンダリングしないので、描画コストが抑えられるとのこと。

sugisugi

しかも特に設定は必要なく、親のパスがマッチしていると <Outlet />内にレンダリングするようになるらしい。

チュートリアルでは

  • app/routes/posts.admin.tsx
  • app/routes/posts.admin.new.tsx

これで子の内容が <Outlet /> にレンダリングされる。

sugisugi

Web標準に逆らわない哲学

Remixの哲学にはWeb標準に逆らわず取り入れるという内容がある。

RemixはHTTPとHTMLの基礎の上で構築されている為、JavaScriptがなくても動かせる。実際にJavaScriptを切って試してみると面白い。だからと言ってJavaScriptが不要なわけではなく、ユーザー体験の向上のためにはJavaScriptが欠かせないということをRemixが言っていることを付け加えておく。

つまりRemixを上手く扱えればWeb標準が上手く扱えることに繋がり、フレームワークに囚われないスキルが身に付く。

sugisugi

以上で公式のチュートリアルをもとにRemixに触れてみた。

フロントとバックで言語が統一されている点や、Web標準に忠実な点はRemixの魅力だと思う。