- Arah masa depan Next.js terasa menarik
- Ada isu terkait Server Actions, tetapi terlihat ada potensi perbaikan dengan
useOptimistic dan useFormStatus di React 19
- Pendekatan
useFetcher milik Remix juga memberi DX yang baik
- PPR (Partial Pre-rendering) Next.js dan sistem cache granular yang baru sangat menonjol
- Secara keseluruhan, saya mendapat kesan yang sangat positif
The Big Picture
- Sistem cache baru dapat diaktifkan secara eksperimental di
next.config.js
- Anda bisa mendefinisikan profil cache untuk menetapkan berbagai waktu kedaluwarsa dan siklus revalidasi
// next.config.js
const config = {
experimental: {
// Aktifkan sistem caching baru. Kini `use cache` bisa digunakan di dalam kode
dynamicIO: true,
// Opsional: pengaturan profil cache
cacheLife: {
blog: {
stale: 3600, // Pertahankan cache klien: 1 jam
revalidate: 900, // Segarkan dari server: 15 menit
expire: 86400, // Masa hidup maksimum: 1 hari
},
},
},
};
Dasar penggunaan use cache
- Caching dapat diterapkan di level file, komponen, dan fungsi melalui deklarasi
"use cache"
- Pada contoh kode, cache bisa diterapkan dengan mudah hanya dengan menambahkan
use cache
- Dengan memanfaatkan
cacheTag, revalidateTag, dan lainnya, cache dapat diinvalidasi pada waktu yang diinginkan
// 1. Caching level file
"use cache";
export default function Page() {
return <div>Cached Page</div>;
}
// 2. Caching level komponen
export async function PriceDisplay() {
"use cache";
const price = await fetchPrice();
return <div>${price}</div>;
}
// 3. Caching level fungsi
export async function getData() {
"use cache";
return await db.query();
}
Caching berbasis tag
import { unstable_cacheTag as cacheTag, revalidateTag } from 'next/cache';
// Cache untuk grup data tertentu
export async function ProductList() {
'use cache';
cacheTag('products');
const products = await fetchProducts();
return <div>{products}</div>;
}
// Invalidasi cache saat data berubah
export async function addProduct() {
'use server';
await db.products.add(...);
revalidateTag('products');
}
Profil cache kustom
- Dengan
unstable_cacheLife, Anda bisa memanggil profil cache yang didefinisikan di next.config.js
- Terapkan kebijakan cache dengan menggunakan nama profil yang dideklarasikan di dalam kode (misalnya
"blog")
import { unstable_cacheLife as cacheLife } from "next/cache";
export async function BlogPosts() {
"use cache";
cacheLife("blog"); // Gunakan profil cache blog yang telah didefinisikan sebelumnya
return await fetchPosts();
}
Hal penting yang mudah terlewat
Pembuatan kunci cache otomatis
props dan arguments komponen otomatis dimasukkan ke dalam kunci cache
- Nilai yang tidak bisa diserialisasi (seperti fungsi) diperlakukan sebagai "referensi yang tidak dapat dimodifikasi"
export async function UserCard({ id, onDelete }) {
"use cache";
// id dimasukkan ke kunci cache
// onDelete tetap diteruskan, tetapi tidak memengaruhi caching
const user = await fetchUser(id);
return <div onClick={onDelete}>{user.name}</div>;
}
Mencampur konten dinamis dan konten cache
- Konten dinamis dapat dicampurkan dengan meneruskannya sebagai child di dalam konten yang di-cache
- Dengan menetapkan array
cacheTag, beberapa tag bisa diterapkan dan diinvalidasi sekaligus
export async function CachedWrapper({ children }) {
"use cache";
const header = await fetchHeader();
return (
<div>
<h1>{header}</h1>
{children} {/* Konten dinamis tetap dipertahankan */}
</div>
);
}
export async function ProductPage({ id }) {
"use cache";
cacheTag(["products", `product-${id}`, "featured"]);
// Cache bisa diinvalidasi dengan salah satu tag ini
}
Hierarki caching
- Jika
"use cache" dideklarasikan di level paling atas, seluruh area tersebut akan di-cache
- Bagian tertentu (misalnya seksi dinamis yang menggunakan Suspense) dapat dikecualikan dari area caching
"use cache";
export default async function Page() {
return (
<div>
<CachedHeader />
<div>
<Suspense fallback={<Loading />}>
<DynamicFeed /> {/* Konten dinamis */}
</Suspense>
</div>
</div>
);
}
Keamanan tipe
- String seperti kunci cache dan profil cache dapat dikelola sebagai konstanta untuk mengurangi penggunaan magic string
- Nyaman juga bila memakai pendekatan generator tag seperti pola di React Query
// Kelola kunci profil cache sebagai konstanta
export const CACHE_LIFE_KEYS = {
blog: "blog",
} as const;
const config = {
experimental: {
cacheLife: {
[CACHE_LIFE_KEYS.blog]: {
stale: 3600,
revalidate: 900,
expire: 86400,
},
},
},
};
Cara mengelola tag cache secara efisien
- Terapkan pola tag factory ala React Query
export const CACHE_TAGS = {
blog: {
all: ["blog"] as const,
list: () => [...CACHE_TAGS.blog.all, "list"] as const,
post: (id: string) => [...CACHE_TAGS.blog.all, "post", id] as const,
comments: (postId: string) =>
[...CACHE_TAGS.blog.all, "post", postId, "comments"] as const,
},
} as const;
// Atur tag caching
function tagCache(tags: string[]) {
cacheTag(...tags);
}
// Contoh penggunaan
export async function BlogList() {
"use cache";
tagCache(CACHE_TAGS.blog.list());
}
3 komentar
Menurut saya, sebaiknya menggunakan framework seperti Next.js atau Remix hanya ketika SEO penting dan memang membutuhkan SSR.
Khususnya untuk layanan seperti produk bisnis B2B atau back office, di mana SEO tidak terlalu penting, saya rasa perlu berhati-hati dalam mengadopsi Next.js. Alasannya, antarmuka dan kompleksitas yang dipaksakan oleh Next.js bisa menurunkan produktivitas pengembangan.
Secara pribadi, jika SEO tidak diperlukan, saya merasa Vite + React jauh lebih baik dari sisi produktivitas pengembangan dan fleksibilitas.
Next.js sejak versi 13 jadi benar-benar makin layak dipakai, dan belakangan ini saya sangat-sangat menyukainya. Sepertinya ini akan menjadi standar de facto untuk tech stack pengembangan web full-stack.