Перейти к содержимому

4. Loaders: загрузка данных

loader — это серверная функция в Remix, которая выполняется перед рендерингом страницы. Она загружает данные из БД, API или любого другого источника, и передаёт их компоненту через useLoaderData.

import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
// Серверная функция — выполняется ТОЛЬКО на сервере
export async function loader() {
const users = await db.user.findMany();
return json(users);
}
// Клиентский компонент — получает данные из loader
export default function Users() {
const users = useLoaderData<typeof loader>();
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
export async function loader({ request, params, context }) {
// params — динамические сегменты URL (:id, :slug)
const { id } = params;
// request — полный Request объект (URL, headers, cookies)
const url = new URL(request.url);
const search = url.searchParams.get("q");
// context — данные из адаптера (env переменные и т.д.)
const user = await db.user.findUnique({ where: { id } });
if (!user) {
throw new Response("Not Found", { status: 404 });
}
return json({ user, search });
}
import type { LoaderFunctionArgs } from "@remix-run/node";
export async function loader({ params }: LoaderFunctionArgs) {
const post = await getPost(params.slug!);
return json({ post });
}
// Автовывод типов — TypeScript знает структуру данных
export default function Post() {
const { post } = useLoaderData<typeof loader>();
// post имеет правильный тип!
return <h1>{post.title}</h1>;
}
export async function loader({ params }) {
const user = await db.user.findUnique({
where: { id: params.id }
});
// 404 — бросаем Response
if (!user) {
throw new Response("Пользователь не найден", { status: 404 });
}
return json(user);
}
export async function loader() {
const data = await getStaticData();
return json(data, {
headers: {
"Cache-Control": "max-age=3600, stale-while-revalidate=86400",
},
});
}
import { defer } from "@remix-run/node";
import { Await } from "@remix-run/react";
import { Suspense } from "react";
export async function loader() {
// Быстрые данные — ждём
const user = await getUser();
// Медленные данные — стримим
const posts = getSlowPosts(); // Promise, без await!
return defer({ user, posts });
}
export default function Page() {
const { user, posts } = useLoaderData<typeof loader>();
return (
<div>
<h1>{user.name}</h1>
<Suspense fallback={<p>Загрузка постов...</p>}>
<Await resolve={posts}>
{(data) => <PostList posts={data} />}
</Await>
</Suspense>
</div>
);
}