freks blog

about

Remixを使ってみる

created: 2024-08-26
おすすめ記事: 出会ってよかったプログラマー本

なんとなくRemixを使ってたりしましたが、ちゃんと入門してみます

Tutorial (30m) | Remix をやりつつまとめます

faviconやcssファイルを読み込む

links を使います

import type { LinksFunction } from "@remix-run/node";

import stylesHref from "../styles/something.css";

export const links: LinksFunction = () => {
  return [
    {
      rel: "icon",
      href: "/favicon.png",
      type: "image/png",
    },
    { rel: "stylesheet", href: stylesHref },
  ];
};

ファイル名でルーティング用意

app/routes にファイルを置くと、そのファイル名でルーティングされます
_ で始まるファイル名はルーティングから無視
ディレクトリで区切るか.で区切ると、ネストされたルーティングになります

Route File Naming | Remix
を読むのがいいです

全体のレイアウト

root | Remix

app/routes が全部のページの枠 <Outlet /> で子が読み込まれます

クライアントサイドのページ移動

Link | Remix を使うとページ全体を読み込みなおさない移動になります

データロード

サーバーサイドでデータロードします
loader | Remix を使います

export const loader = async () => {
  const contacts = await getContacts();
  return json({ contacts });
};

export default function App() {
  const { contacts } = useLoaderData<typeof loader>();
}

URLのパラメータを使うには

export const loader = async ({ params }) => {
  invariant(params.contactId, "Missing contactId param");
  const contact = await getContact(params.contactId);
  if (!contact) {
    throw new Response("Not Found", { status: 404 });
  }
  return json({ contact });
};

invariant でエラーを投げることができます

データ送信

action | Remix を使います

import type { ActionFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form } from "@remix-run/react";

import { updateContact } from "../data";

export const action = async ({
  params,
  request,
}: ActionFunctionArgs) => {
  invariant(params.contactId, "Missing contactId param");
  const formData = await request.formData();
  const updates = Object.fromEntries(formData);
  await updateContact(params.contactId, updates);
  return redirect(`/contacts/${params.contactId}`);
};

export default function EditContact() {
  return (
    <Form id="contact-form" method="post">
      ...
    </Form>
  );
}

RemixのFormでsubmitすると、サーバーサイドでactionが実行されます

リンクのスタイル

NavLink | Remix を使うと今開いてるページのリンクスタイルを変えられます

<NavLink
  className={({ isActive, isPending }) =>
    isActive
      ? "active"
      : isPending
      ? "pending"
      : ""
  }
  to={`contacts/${contact.id}`}
>
  {/* existing elements */}
</NavLink>

全体でローディング中の表示

useNavigation | Remix でローディング中かどうかを取得できます

import {
  Outlet,
  useNavigation,
} from "@remix-run/react";

export default function App() {
  const navigation = useNavigation();

  return (
    <html lang="en">
      <body>
        <div
          className={
            navigation.state === "loading" ? "loading" : ""
          }
        >
          <Outlet />
        </div>
      </body>
    </html>
  );
}

データを削除する

<Form
  action="destroy"
  method="post"
  onSubmit={(event) => {
    const response = confirm(
      "Please confirm you want to delete this record."
    );
    if (!response) {
      event.preventDefault();
    }
  }}
>
  <button type="submit">Delete</button>
</Form>

action="destroy" で削除処理になります

app/routes/contacts.\$contactId.tsx で削除処理を実行したとき
app/routes/contacts.\$contactId.destroy.tsx が呼ばれます

app/routes/contacts.\$contactId.destroy.tsx

import type { ActionFunctionArgs } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import invariant from "tiny-invariant";

import { deleteContact } from "../data";

export const action = async ({
  params,
}: ActionFunctionArgs) => {
  invariant(params.contactId, "Missing contactId param");
  await deleteContact(params.contactId);
  return redirect("/");
};

と削除のactionだけを書きます

トップページのコンテンツ

app/routes/_index.tsx がトップページのコンテンツになります

前の画面に戻る

useNavigate | Remix を使います

import { useNavigate } from "@remix-run/react";

function SomeComponent() {
  const navigate = useNavigate();
  return (
    <button
      onClick={() => {
        navigate(-1);
      }}
    />
  );
}

URLのクエリを使ってデータを取得

import type {
  LinksFunction,
  LoaderFunctionArgs,
} from "@remix-run/node";

// existing imports & exports

export const loader = async ({
  request,
}: LoaderFunctionArgs) => {
  const url = new URL(request.url);
  const q = url.searchParams.get("q");
  const contacts = await getContacts(q);
  return json({ contacts });
};

URLと同期しながら検索

remix.run/docs/en/main/start/tutorial#synchronizing-urls-to-form-state にいいサンプルがあります

画面遷移しないデータ送信

useFetcher | Remix を使います

export const action = async ({
  params,
  request,
}: ActionFunctionArgs) => {
  // some action
};

function SomeComponent() {
  const fetcher = useFetcher();
  return (
    <fetcher.Form method="post">
      <input type="text" />
    </fetcher.Form>
  );
}

これでページ遷移せずにデータ送信がされます

楽観的なUI

remix.run/docs/en/main/start/tutorial#optimistic-ui にいいサンプルがあります

お気に入りボタンを押す、サーバーサイドで反映される前に表示を変えておくというものです

まとめ

チュートリアルがよくできてていいです
あとは必要な時に公式ドキュメント Remix Docs Home | Remix を読んでいけばいいかなと思います


Amazonのアソシエイトとして、blog.freks.jp は適格販売により収入を得ています。
This site is managed by freks