Remix で loader() でも action() でも同じデータを返すパターン

例えば従業員の一覧を表示する画面を Remix で書くとします。その画面が

  • 普通にアクセスしたときは条件なしで従業員を表示する

  • 同じ画面で検索条件を指定し従業員を絞り込むことができる

という動きをする場合、loader() も action() も両方使い、loader() では条件なしで取得したデータを、action() では指定した条件に合致するデータを返すことになりそうです。そういうときの実装について考えてみました。

Remix の「Form vs. fetcher」というページによると、このパターンの場合(同じ URL 内で画面から送信したデータを元に表示する内容を変化させる)は fetcher を使うのが良さそうです。

以下のような実装にしてみました。Form から to と from を送信、action() ではそれを使って employees のデータをフィルターしています。loader() では employees そのままを返しています。

import { ActionFunctionArgs, json } from "@remix-run/node";
import { useActionData, useFetcher, useLoaderData } from "@remix-run/react";

type Employee = {
  name: string
  age: number
}

const employees:Employee[] = [
  {
    name: "Alice",
    age: 30,
  },
  {
    name: "Bob",
    age: 40,
  },
  {
    name: "Charlie",
    age: 50,
  }
]

export function loader() {
  return json(employees)
}

export async function action({request}:ActionFunctionArgs) {
  const body = await request.formData()
  const from = parseInt(String(body.get("from")), 10)
  const to = parseInt(String(body.get("to")), 10)
  return employees.filter(employee => (from <= employee.age && employee.age < to) )
}

export default function Index() {
  const fetcher = useFetcher<typeof action>()
  const employees = fetcher.data ?? useLoaderData<typeof loader>()
  
  return (
    <div>
      <EmployeeList employees={employees} />
      <fetcher.Form method="post">
      <div>
        <input type="number" name="from" placeholder="age from"/>
        <input type="number" name="to" placeholder="age to (exclusive)"/>
        <button type="submit" >Search</button>
      </div>
    </fetcher.Form>
    </div>
  )
}

function EmployeeList({ employees }:{ employees: Employee[] }) {
  return (
    <div>
      {employees.map((employee, index) => <div key={index}>{employee.name}</div>)}
    </div>
  )
}

ポイントとしては Index() の冒頭で、fetcher.data からのデータの取得を試み、それができなかった場合は useLoaderData() からデータを取得している部分です。これにより、action() からデータが返されていたなら(つまり条件が指定された検索が行われた場合であれば)そのデータを、そうでない場合は loader() から返されたデータを利用する、という動きが実現されます。


いいなと思ったら応援しよう!