Data Loading
Iya, di Next.js namanya data fetching, tapi di React Router nyebutnya data loading. Data loading diperluin kalo lo butuh ambil data saat halaman di-render, misalnya buat nge-fetch data dari API atau database.
React Router punya dua fungsi buat data loading: loader dan clientLoader.
Seperti yang gue bahas sebelumnya, loader dieksekusi di server saat halaman di-render, sedangkan clientLoader dieksekusi di client/browser.
Contohnya gini:
import type { Route } from './+types/product';
import { db } from '~/libs/db';
export async function loader({ params }: Route.LoaderArgs) {
const product = await db.getProduct(params.pid);
return product;
}
export default function Product({ loaderData }: Route.ComponentProps) {
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}loaderData bakal otomatis di-infer tipe datanya dari return value loader.
Tapi, kalo lo pake fetch, loaderData bakal jadi any.
Ini karena data yang lo fetch bisa punya tipe yang beda-beda, tergantung dari API yang lo panggil.
Contohnya:
import type { Route } from './+types/product';
export async function loader({ params }: Route.LoaderArgs) {
const response = await fetch(`https://api.example.com/products/${params.pid}`);
const product = await response.json();
return product;
}
export default function Product({ loaderData }: Route.ComponentProps) {
// loaderData di sini punya tipe any
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}Di kasus kayak gini, lo bisa bikin type guard manual atau pake library kayak zod:
import type { Route } from './+types/product';
import { z } from 'zod';
const productSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string(),
});
export async function loader({ params }: Route.LoaderArgs) {
const response = await fetch(`https://api.example.com/products/${params.pid}`);
const product = await response.json();
const parsedProduct = productSchema.safeParse(product);
if (!parsedProduct.success) {
throw new Response('Invalid product data', { status: 500 });
}
return parsedProduct.data;
}
export default function Product({ loaderData }: Route.ComponentProps) {
// loaderData di sini punya tipe yang udah di-parse
const { name, description } = loaderData;
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}Kalo misalnya lo pake ORM kayak Drizzle, loader juga bisa langsung nge-return model yang udah di-infer tipe datanya. Contohnya:
import type { Route } from './+types/product';
import { db } from '~/libs/db';
import { products } from '~/libs/db/schema';
export async function loader({ params }: Route.LoaderArgs) {
const product = await db.query.products.findFirst({
where: (products, { eq }) => eq(products.id, params.pid),
});
return product;
}
// kode yang sebelumnya tetap samaDengan cara ini, loaderData di dalam component Product bakal otomatis punya tipe yang sesuai dengan model products.
Fungsi loader ini juga bakal dihapus dari client bundle, jadi lo aman buat pake API yang cuman boleh jalan di server.
https://pastebin.com/2Vkt94Gi
bang ini kenapa error products di productsPage nya masih any, sama loaderData nya undefined waktu di hover tapi di browser tetap tampil