Remixで親のloaderデータを子のmetaでいい感じに取得する
RemixでNested Layoutを使っていると起きる実装の悩みの一つ。
親のloaderのデータを子で使いたい場合です。
今回のパターンは親のloaderで取得したデータを子のmeta内で使うにはどうすればいいのか。
Remixでアプリケーションを実装しているとかなり需要があるケースながらこれまでしっくりくるコードをかけていませんでしたが、今回割といい感じに汎用化できたので紹介していきたいと思います。
Remixで親loaderのデータを子のmetaで使うには?
親のloaderデータを子のmetaで使うには公式からコードサンプルが提供されています。
下記は公式で公開されているコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import type { loader as projectDetailsLoader } from "./project.$pid"; export async function loader({ params, }: LoaderFunctionArgs) { return json({ task: await getTask(params.tid) }); } export const meta: MetaFunction< typeof loader, { "routes/project.$pid": typeof projectDetailsLoader } > = ({ data, matches }) => { const project = matches.find( (match) => match.id === "routes/project.$pid" ).data.project; const task = data.task; return [{ title: `${project.name}: ${task.name}` }]; };
https://remix.run/docs/ru/main/route/meta#accessing-data-from-parent-route-loaders
MetaFunctionのジェネリクスの第2引数に参照したい親のルートとloaderを指定することで型を守りつつ、データにアクセスすることができます。
公式から公開されているコードでももちろん良いのですが各ページに書いていくとかなり冗長になってしまうので、うまいことラップした関数がかけないかと思い考えてみました。
親loaderのデータを取得する汎用関数
まずは汎用関数と使用例です。
今回紹介しているケースではroutes/_front.tsxのloaderで取得したsettingを子のmetaで使うことを想定でしています。
汎用関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
import { type MetaFunction, MetaArgs } from '@remix-run/node' import { SettingDTO } from '~/domain/setting/dtos/SettingDTO' import { loader as frontLoader } from '~/routes/_front' // 取得したい親のloaderの情報 export type FrontRoute = { 'routes/_front': typeof frontLoader } export function withFrontData<T>( callback: ( args: MetaArgs<T, FrontRoute>, frontData: { setting: SettingDTO }, ) => ReturnType<MetaFunction>, ) { return (args: MetaArgs<T, FrontRoute>) => { const frontMeta = args.matches.find((match) => match.id === 'routes/_front') // 必要であれば適切なハンドリングを行う if (!frontMeta) { throw new Response(null, { status: 500, }) } const data = frontMeta.data return callback(args, { setting: data.setting }) } }
使用例
1 2 3 4 5
// 子のmeta export const meta: MetaFunction = withFrontData<typeof loader>((args, { setting }) => { // 親のloaderで取得したsettingを参照できる return [] })
まずはroutes/_front.tsxのデータを参照したいので型定義をしました。
ベタ書きする場合は別に別途定義はしなくてよいかなと思います。
1
export type FrontRoute = { 'routes/_front': typeof frontLoader }
メインとなる関数はwith◯◯Dataとしてどこのデータを一緒に参照できるようにしました。
metaに指定する関数 を内部でreturnして、さらに子から渡されるmetaに該当する関数に取得した親データを渡してreturnするようにしました。
withFrontDataのジェネリクスは必須ではありません。
ですが子のloaderのデータをmetaで参照したい場合には指定をしないとwithFrontData内のargs.dataで子のloaderで取得したデータの型が参照できない型エラーが発生するので、子loaderのデータを参照する場合にはつけるようにすると良いです。