KembaliBelajar Full-stack Dengan React Router v7
Pembahasan

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:

app/routes/product.tsx
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:

app/routes/product.tsx
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:

app/routes/product.tsx
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:

app/routes/product.tsx
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 sama

Dengan 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.

Progres kursus

Sudah paham pelajaran ini?

Tandai selesai untuk menyimpan progresmu di kursus ini.

Diskusi

2 komentar
AzrilFitrayana3/11/2025

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