Bab 1: Pengantar React JS dan Revolusi Web Modern
Di era digital yang bergerak super cepat ini, kamu pasti sadar banget kalau website itu sudah bukan sekadar kumpulan teks dan gambar statis lagi. Sekarang, kita ngomongin tentang pengalaman yang interaktif, responsif, dan dinamis, mulai dari aplikasi e-commerce yang mulus banget sampai dashboard yang real-time. Nah, di sinilah React JS muncul sebagai bintang utama yang berjasa banget bikin revolusi di dunia pengembangan web.
Memahami Peran React JS dalam Pengembangan Web

Coba bayangin kamu lagi main game atau pakai aplikasi di ponsel. Pasti pengennya semua tombol bisa diklik, tampilan langsung berubah, dan semuanya terasa "hidup" kan? Nah, di sinilah peran besar React JS. React itu bukan cuma sekadar tool biasa, tapi semacam "otak" di balik antarmuka pengguna (UI) yang kita lihat di berbagai aplikasi web modern. Fungsinya adalah membantu para developer membangun bagian frontend sebuah website atau aplikasi, yaitu semua yang kita lihat dan berinteraksi dengannya di layar.
React memungkinkan kita membangun komponen-komponen UI yang kecil, independen, dan bisa dipakai ulang. Ibaratnya, kalau kamu mau bikin rumah, React ini seperti menyediakan LEGO blocks yang sudah jadi dan bisa kamu susun dengan berbagai cara untuk membentuk bangunan yang kompleks. Jadi, daripada bikin semuanya dari awal terus-menerus, developer bisa fokus merakit komponen yang sudah ada, menghemat waktu, dan membuat proses pengembangan jadi jauh lebih efisien. Ini bikin tampilan web jadi lebih konsisten, lebih cepat dibangun, dan pastinya lebih mudah dipelihara.
Evolusi React: Dari Konsep Awal hingga React 18
Perjalanan React ini menarik banget, lho. Awalnya, React tuh cuma sebuah library kecil buatan Facebook (sekarang Meta) yang dirancang untuk mengatasi kompleksitas tampilan di Facebook yang makin ruwet. Ide dasarnya sederhana tapi brilian: kalau ada perubahan data, React cuma akan update bagian tertentu dari UI yang benar-benar perlu diubah, bukan me-render ulang seluruh halaman. Konsep ini yang kita kenal dengan Virtual DOM, sebuah representasi ringan dari DOM asli yang bikin proses update UI jadi super cepat.
Dari sana, React terus berkembang pesat. Ingat era Class Components yang dulu jadi primadona? Itu adalah cara kita membangun komponen di React di masa lalu. Tapi, seiring berjalannya waktu dan kebutuhan yang makin kompleks, lahirlah React Hooks di versi 16.8. Ini adalah terobosan besar yang mengubah cara kita menulis kode React. Dengan Hooks, kita bisa menggunakan state dan lifecycle features di functional components tanpa perlu pakai class. Kode jadi lebih ringkas, mudah dibaca, dan jauh lebih bersih. Lalu, muncullah React 18 yang membawa fitur keren seperti Concurrent Mode dan Suspense for Data Fetching, yang bikin aplikasi jadi lebih responsif dan pengalaman pengguna makin mulus, terutama saat memuat data atau melakukan tugas berat. Semua evolusi ini menunjukkan bagaimana React selalu berinovasi untuk menjawab tantangan dunia web yang terus berubah.
Mengapa Web Modern Membutuhkan Solusi yang Dinamis dan Efisien

Coba deh pikirkan, pengguna internet zaman sekarang maunya apa? Mereka pasti pengen yang instan, mulus, dan intuitif. Tidak ada yang mau menunggu halaman loading terlalu lama atau melihat tampilan yang stuck saat mereka berinteraksi. Inilah tantangan terbesar web modern. Aplikasi web sekarang harus bisa:
- Berinteraksi Secara Real-time: Pengguna ingin melihat pembaruan data secara langsung, tanpa perlu me-refresh halaman. Contohnya, notifikasi media sosial atau chat online.
- Memberikan Pengalaman Pengguna yang Mulus (Seamless UX): Transisi antar halaman atau antar bagian aplikasi harus terasa halus, seperti aplikasi native di ponsel.
- Responsif dan Cepat: Aplikasi harus bekerja dengan baik di berbagai ukuran layar (ponsel, tablet, desktop) dan memuat dengan cepat, bahkan di koneksi internet yang kurang stabil.
- Skalabel: Aplikasi harus bisa tumbuh dan menangani banyak pengguna serta fitur baru tanpa jadi lemot atau error.
Untuk memenuhi semua tuntutan ini, kita butuh teknologi frontend yang enggak cuma kuat tapi juga cerdas dalam mengelola tampilan dan interaksi pengguna. Teknologi tradisional yang cenderung me-render ulang seluruh halaman setiap kali ada perubahan sudah tidak relevan lagi. Kita butuh solusi yang bisa mengelola state dengan efisien, mengoptimalkan proses rendering, dan memungkinkan developer membangun fitur-fitur kompleks dengan cepat.
Di sinilah peran React JS jadi sangat krusial. Dengan arsitekturnya yang berorientasi komponen, efisiensi rendering melalui Virtual DOM, dan ekosistem yang kaya, React JS menjadi fondasi kokoh untuk membangun aplikasi web modern yang bisa memberikan pengalaman pengguna terbaik. Dan, dengan kedatangan React 19 yang sebentar lagi akan kita bahas, janji untuk pengalaman web yang lebih dinamis dan efisien ini akan semakin terpenuhi!
Bab 2: Memperkenalkan React 19: Lompatan Besar ke Depan

Setelah kita bahas bagaimana React sudah jadi tulang punggung aplikasi web modern, sekarang saatnya kita intip apa yang bikin React 19 ini begitu istimewa dan kenapa dia disebut sebagai "lompatan besar ke depan." Ini bukan cuma update biasa, tapi sebuah evolusi yang bakal mengubah cara kita membangun web.
Apa Saja yang Baru di React 19? Ikhtisar Fitur Utama
React 19 datang dengan segudang fitur baru yang bukan cuma keren di atas kertas, tapi juga benar-benar bisa kita rasakan manfaatnya langsung di proyek. Beberapa fitur utamanya antara lain:
1. React Server Components (RSC)

Ini adalah game-changer paling besar! Kalau biasanya semua rendering UI itu terjadi di browser (sisi klien), RSC memungkinkan kita me-render komponen di sisi server. Manfaatnya? Loading halaman bisa lebih cepat karena yang dikirim ke browser cuma HTML yang sudah jadi, plus bundle size JavaScript jadi lebih kecil. Ini juga membuka pintu buat SEO yang lebih baik karena konten sudah ada saat halaman pertama kali dimuat.
Bayangkan kita ingin membuat halaman profil pengguna di mana kita menampilkan data pengguna dari database. Tanpa RSC, biasanya kita akan membuat komponen React di sisi klien, lalu di dalamnya kita akan fetch data dari API saat komponen dimuat.
⭐ Contoh Kode Tanpa RSC (Client Component Biasa)
// components/UserProfileClient.js
"use client"; // Ini menandakan ini adalah Client Component
import { useState, useEffect } from 'react';
export default function UserProfileClient({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
// Data diambil di sisi klien setelah komponen dirender
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
setError(e.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <p>Memuat profil...</p>;
if (error) return <p className="error">Terjadi kesalahan: {error}</p>;
if (!user) return <p>Pengguna tidak ditemukan.</p>;
return (
<div className="user-profile">
<h1>Profil Pengguna</h1>
<img src={user.avatar} alt={user.name} width={100} height={100} />
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Bergabung sejak: {new Date(user.joinedDate).toLocaleDateString()}</p>
</div>
);
}
Dalam contoh di atas:
"use client";menandakan bahwa ini adalah Client Component. Kode JavaScript-nya akan dikirim ke browser pengguna.- Data
userdiambil (difetch) setelah komponen dimuat di browser menggunakanuseEffect. - Selama data dimuat, ada state loading dan error yang perlu diurus di sisi klien.
- Browser harus mengunduh JavaScript untuk komponen ini, menjalankan React, dan baru kemudian melakukan fetch data. Ini bisa membuat initial load lebih lambat dan kurang bagus untuk SEO.
⭐ Contoh Kode dengan RSC (Server Component)
Sekarang, mari kita ubah komponen UserProfile menjadi Server Component dengan React 19 (atau kerangka kerja seperti Next.js 13+ yang mendukung RSC).
// components/UserProfileServer.js
// Secara default, komponen ini adalah Server Component karena tidak ada "use client";
// Fungsi untuk mengambil data dari "database" (simulasi API call di server)
async function getUserData(userId) {
// Dalam skenario nyata, ini akan menjadi panggilan ke database
// atau API internal yang dijalankan di server.
// Tidak ada fetch() ke URL eksternal yang diunduh oleh browser.
await new Promise(resolve => setTimeout(resolve, 500)); // Simulasi penundaan jaringan/DB
if (userId === 'user123') {
return {
id: 'user123',
name: 'Budi Santoso',
email: '[email protected]',
avatar: '<https://via.placeholder.com/100/FF5733/FFFFFF?text=BS>',
joinedDate: '2023-01-15T10:00:00Z',
bio: 'Seorang pengembang web yang antusias.'
};
}
return null;
}
export default async function UserProfileServer({ userId }) {
// Data diambil di sisi server SEBELUM komponen dirender
const user = await getUserData(userId);
if (!user) {
return <p>Pengguna tidak ditemukan.</p>;
}
return (
<div className="user-profile">
<h1>Profil Pengguna</h1>
<img src={user.avatar} alt={user.name} width={100} height={100} />
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Bergabung sejak: {new Date(user.joinedDate).toLocaleDateString()}</p>
<p>Bio: {user.bio}</p>
{/* Bisa mengimpor Client Component lain di sini jika diperlukan interaktivitas */}
{/* <FollowButton userId={userId} /> */}
</div>
);
}
Dalam contoh dengan RSC:
- Tidak ada
"use client";di bagian atas, yang berarti ini adalah Server Component secara default. - Fungsi
getUserData(yang mensimulasikan fetch data dari database atau API internal) dipanggil langsung di Server Component. Ini berarti data diambil di sisi server sebelum HTML dikirim ke browser. - Komponen ini bisa berupa
asyncdan menggunakanawaituntuk menunggu data. - Yang dikirim ke browser adalah HTML yang sudah terisi data, bukan JavaScript untuk mengambil data. Ini membuat initial page load sangat cepat dan konten langsung tersedia untuk web crawler (baik untuk SEO).
- Tidak ada state loading atau
useEffectuntuk data fetching di komponen ini, karena data sudah tersedia saat komponen dirender. - Bundle size JavaScript yang dikirim ke browser akan lebih kecil karena logika data fetching dan rendering dilakukan di server.
Ini adalah inti dari kekuatan RSC: menggeser data fetching dan rendering awal ke sisi server, sehingga browser hanya perlu mengunduh JavaScript yang benar-benar diperlukan untuk interaktivitas, bukan untuk menampilkan konten awal.
2. Actions

Fitur ini bikin interaksi dengan form atau data mutation jadi jauh lebih gampang dan otomatis. Dengan Actions, kamu bisa langsung mengirim data dari form ke server tanpa perlu repot bikin handler manual yang panjang. React akan otomatis mengelola pending state dan optimistic updates, bikin pengalaman pengguna jadi lebih mulus.
Bayangkan kita punya form sederhana untuk menambahkan sebuah item baru ke dalam daftar. Tanpa Actions, kita biasanya akan mengelola state loading, state error, dan secara manual memperbarui UI setelah data berhasil terkirim. Dengan Actions, banyak dari pekerjaan itu bisa otomatis diurus oleh React.
⭐ Contoh Kode Tanpa Actions (Cara Klasik)
// components/AddItemFormClassic.js
"use client";
import { useState } from 'react';
export default function AddItemFormClassic() {
const [itemName, setItemName] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [items, setItems] = useState([]); // Simulasi daftar item
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
setError(null);
try {
// Simulasi pengiriman data ke server
const response = await fetch('/api/add-item', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: itemName }),
});
if (!response.ok) {
throw new Error('Gagal menambahkan item.');
}
const newItem = await response.json();
setItems((prevItems) => [...prevItems, newItem]); // Perbarui UI secara manual
setItemName(''); // Kosongkan input
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
return (
<div style={{ border: '1px solid #ccc', padding: '20px', borderRadius: '8px' }}>
<h2>Tambah Item Baru (Cara Klasik)</h2>
<form onSubmit={handleSubmit}>
<input
type="text"
value={itemName}
onChange={(e) => setItemName(e.target.value)}
placeholder="Nama Item"
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Menambahkan...' : 'Tambah Item'}
</button>
</form>
{error && <p style={{ color: 'red' }}>{error}</p>}
<h3>Daftar Item:</h3>
<ul>
{items.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
</div>
);
}
Dalam contoh di atas:
- Kita harus secara manual mengelola
isLoadinguntuk menonaktifkan tombol dan input. - Kita juga harus secara manual mengelola
error. - Setelah berhasil, kita memperbarui daftar
itemssecara manual menggunakansetItems. - Semua logika data fetching, state loading, dan error handling ada di dalam
handleSubmit.
⭐ Contoh Kode dengan Actions (React 19)
Sekarang, mari kita ubah komponen yang sama menggunakan Actions di React 19. Untuk contoh ini, kita akan membuat Server Action (fungsi yang berjalan di server saat dipanggil dari klien).
Pertama, kita butuh file untuk Server Action itu sendiri (misalnya, app/actions.js di Next.js atau setup serupa di kerangka kerja lain yang mendukung RSC).
// app/actions.js (Ini akan berjalan di sisi server)
"use server"; // WAJIB untuk Server Action
// Kita bisa langsung berinteraksi dengan database di sini
// atau memanggil API internal yang berjalan di server.
import { revalidatePath } from 'next/cache'; // Contoh dari Next.js untuk revalidasi cache
export async function addItem(formData) {
const name = formData.get('itemName'); // Ambil data dari FormData
if (!name) {
return { success: false, message: 'Nama item tidak boleh kosong!' };
}
try {
// Simulasi penundaan jaringan / database di server
await new Promise(resolve => setTimeout(resolve, 1000));
// Simulasi penyimpanan ke database dan mengembalikan item baru
const newItem = {
id: Date.now().toString(),
name: name,
createdAt: new Date().toISOString(),
};
console.log('Item baru ditambahkan di server:', newItem);
// Contoh: Revalidasi cache atau data yang relevan di server (opsional, tergantung kebutuhan)
// revalidatePath('/items');
return { success: true, item: newItem }; // Mengembalikan data atau status sukses
} catch (error) {
console.error('Error menambahkan item di server:', error);
return { success: false, message: 'Gagal menambahkan item: ' + error.message };
}
}
// Kita juga bisa punya server-side data fetching di sini
export async function getItems() {
"use server";
await new Promise(resolve => setTimeout(resolve, 500));
return [
{ id: 'a', name: 'Item A', createdAt: '2024-01-01' },
{ id: 'b', name: 'Item B', createdAt: '2024-01-02' },
];
}
Kemudian, di komponen Client Component kita:
// components/AddItemFormActions.js
"use client";
import { useRef } from 'react';
import { useFormStatus, useOptimistic } from 'react'; // Hooks baru di React 19
import { addItem } from '@/app/actions'; // Mengimpor Server Action kita
// Komponen tombol terpisah untuk menunjukkan useFormStatus
function SubmitButton() {
const { pending } = useFormStatus(); // Otomatis tahu status pending dari form induk
return (
<button type="submit" disabled={pending}>
{pending ? 'Menambahkan...' : 'Tambah Item'}
</button>
);
}
export default function AddItemFormActions({ initialItems }) {
const formRef = useRef(); // Untuk mereset form setelah submit
// useOptimistic untuk UI yang responsif (seolah-olah sudah terupdate)
const [optimisticItems, addOptimisticItem] = useOptimistic(
initialItems, // State awal
(currentItems, newItemName) => [
...currentItems,
{ id: 'temp-' + Date.now(), name: newItemName + ' (menunggu...)', createdAt: new Date().toISOString() } // Tampilan sementara
]
);
const formAction = async (formData) => {
const itemName = formData.get('itemName');
// Tampilkan perubahan optimistik dulu
addOptimisticItem(itemName);
formRef.current.reset(); // Reset form segera
// Panggil Server Action
const result = await addItem(formData); // React otomatis mengelola pending state
if (!result.success) {
alert(`Error: ${result.message}`);
// Di sini, optimistic update akan otomatis di-rollback jika ada error
// atau kamu bisa mengimplementasikan logika rollback manual jika perlu
} else {
console.log('Item berhasil ditambahkan:', result.item);
// Jika berhasil, useOptimistic akan otomatis memperbarui dengan data asli
// atau kamu bisa memicu re-render data asli jika tidak menggunakan revalidatePath
}
};
return (
<div style={{ border: '1px solid #007bff', padding: '20px', borderRadius: '8px' }}>
<h2>Tambah Item Baru (dengan Actions & React 19)</h2>
<form ref={formRef} action={formAction}> {/* 'action' langsung ke Server Action */}
<input
type="text"
name="itemName" // Nama input penting untuk FormData
placeholder="Nama Item"
required
/>
<SubmitButton />
</form>
<h3>Daftar Item:</h3>
<ul>
{optimisticItems.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
// Parent Component (di Server Component) yang memuat initialItems
// app/page.js atau serupa
/*
import AddItemFormActions from '@/components/AddItemFormActions';
import { getItems } from '@/app/actions'; // Ambil data awal dari Server Action
export default async function HomePage() {
const initialItems = await getItems(); // Ambil data awal di server
return (
<div>
<h1>Aplikasi Item</h1>
<AddItemFormActions initialItems={initialItems} />
</div>
);
}
*/
Perbandingan dan Manfaat dengan Actions:
actionLangsung di<form>: Daripada menggunakanonSubmityang memanggil fungsi JavaScript manual, kita bisa langsung menunjuk Server Action (yaituformAction) di atributactionpada tag<form>. React akan mengelola submission form secara otomatis.useFormStatusOtomatis: Kita tidak perlu lagi mengelola stateisLoadingsecara manual.useFormStatusadalah Hook yang disediakan React untuk membaca status pending dari form terdekat. Ini sangat rapi dan mengurangi boilerplate.useOptimisticuntuk UI Mulus: Ini adalah game-changer untuk user experience. Saat pengguna mengklik "Tambah Item",useOptimisticlangsung memperbarui UI seolah-olah item sudah ditambahkan (misalnya, dengan label "menunggu..."). Jika Server Action berhasil, UI akan diperbarui dengan data asli. Jika gagal, UI akan otomatis di-rollback ke kondisi sebelum optimistic update. Ini membuat aplikasi terasa sangat responsif.- Kode Lebih Ringkas: Logika data fetching, loading state, dan error handling (sebagian besar) dipindahkan ke Server Action, atau diurus secara otomatis oleh React di sisi klien. Ini membuat komponen UI kita jadi jauh lebih bersih dan fokus pada rendering semata.
- Data Langsung dari Server: Karena
addItemadalah Server Action, ia bisa langsung berinteraksi dengan database atau business logic di server tanpa perlu API endpoint terpisah yang diakses oleh browser.
Fitur Actions, terutama bila dikombinasikan dengan useFormStatus dan useOptimistic, benar-benar menyederhanakan proses interaksi dengan server dan menciptakan pengalaman pengguna yang jauh lebih halus dan responsif di aplikasi React modern.
3. useFormStatus dan useOptimistic

useFormStatus and useOptimisticDua Hook baru ini bekerja barengan dengan Actions untuk memberikan feedback instan ke pengguna. useFormStatus bisa kita pakai buat tahu apakah sebuah form sedang dalam proses pengiriman data (misalnya, menampilkan loading spinner). Sementara useOptimistic memungkinkan kita menampilkan perubahan UI yang seolah-olah sudah terjadi (misalnya, menambahkan komentar ke daftar sebelum benar-benar tersimpan di server) untuk pengalaman yang lebih responsif. Kalau ada error, UI-nya bisa balik lagi ke kondisi sebelumnya secara otomatis. Keren, kan?
Kita akan membuat komponen komentar sederhana di mana pengguna bisa menambahkan komentar. Saat komentar dikirim, kita ingin:
- Menampilkan status loading pada tombol kirim.
- Menampilkan komentar baru secara optimistik di daftar sebelum data benar-benar tersimpan di server.
- Jika ada error saat menyimpan, komentar optimistik akan otomatis di-rollback atau dihapus.
⭐ Langkah 1: Buat Server Action (app/actions.js)
Ini adalah fungsi yang akan berjalan di sisi server untuk "menyimpan" komentar.
// app/actions.js
"use server"; // Ini WAJIB untuk Server Action
export async function addComment(formData) {
const commentText = formData.get('comment');
const authorName = formData.get('author');
if (!commentText || !authorName) {
return { success: false, message: 'Nama dan komentar tidak boleh kosong!' };
}
try {
// Simulasi penundaan jaringan / database (2 detik)
await new Promise(resolve => setTimeout(resolve, 2000));
// Simulasi kondisi error 30% dari waktu
if (Math.random() < 0.3) {
throw new Error('Gagal menyimpan komentar. Coba lagi!');
}
const newComment = {
id: Date.now().toString(),
text: commentText,
author: authorName,
timestamp: new Date().toISOString(),
};
console.log('Komentar baru disimpan di server:', newComment);
return { success: true, comment: newComment };
} catch (error) {
console.error('Error saat menyimpan komentar di server:', error);
return { success: false, message: error.message || 'Terjadi kesalahan tidak dikenal.' };
}
}
// Fungsi untuk mendapatkan komentar awal (juga di server)
export async function getInitialComments() {
"use server";
// Simulasi fetch komentar awal dari DB
await new Promise(resolve => setTimeout(resolve, 500));
return [
{ id: '1', text: 'Komentar pertama saya!', author: 'Admin', timestamp: '2024-07-01T10:00:00Z' },
{ id: '2', text: 'Sangat informatif!', author: 'Pembaca Setia', timestamp: '2024-07-02T11:30:00Z' },
];
}
⭐ Langkah 2: Buat Komponen Client (components/CommentSection.js)
Di sinilah kita akan menggunakan useFormStatus dan useOptimistic.
// components/CommentSection.js
"use client"; // Menandakan ini adalah Client Component
import { useRef } from 'react';
import { useFormStatus, useOptimistic } from 'react'; // Impor Hooks baru
import { addComment } from '@/app/actions'; // Impor Server Action kita
// Sub-komponen untuk tombol kirim, memanfaatkan useFormStatus
function SubmitButton() {
const { pending } = useFormStatus(); // Mengambil status 'pending' dari form induk
return (
<button type="submit" disabled={pending}>
{pending ? 'Mengirim Komentar...' : 'Kirim Komentar'}
</button>
);
}
export default function CommentSection({ initialComments }) {
const formRef = useRef(); // Untuk mereset form setelah pengiriman
// useOptimistic: Mengelola daftar komentar dengan pembaruan sementara
const [optimisticComments, addOptimisticComment] = useOptimistic(
initialComments, // Initial state (komentar yang sudah ada)
(currentComments, newCommentInfo) => {
// Fungsi updater: Menambahkan komentar "sementara" ke daftar
return [
...currentComments,
{
id: 'temp-' + Date.now(), // ID sementara
text: newCommentInfo.text,
author: newCommentInfo.author,
timestamp: new Date().toISOString(),
isOptimistic: true // Tandai sebagai komentar optimistik
}
];
}
);
const handleFormAction = async (formData) => {
const commentText = formData.get('comment');
const authorName = formData.get('author');
// 1. Tampilkan komentar optimistik terlebih dahulu
addOptimisticComment({ text: commentText, author: authorName });
// Reset form segera setelah optimistik update
formRef.current.reset();
// 2. Panggil Server Action kita
const result = await addComment(formData); // React akan mengelola 'pending' secara otomatis
// 3. Tangani hasil dari Server Action
if (!result.success) {
alert(`Gagal mengirim komentar: ${result.message}`);
// Jika ada error, useOptimistic akan secara otomatis "menggulirkan kembali"
// (rollback) perubahan optimistik ke state sebelumnya.
// Anda tidak perlu melakukan rollback manual di sini.
} else {
console.log('Komentar berhasil disimpan:', result.comment);
// Jika berhasil, useOptimistic akan memastikan state akhir sesuai
// dengan data yang sebenarnya dari server (jika ada pembaruan).
// Dalam kasus ini, karena kita hanya menambahkan, tidak ada rollback/update lebih lanjut.
}
};
return (
<div style={{ border: '1px solid #0056b3', padding: '25px', borderRadius: '10px', maxWidth: '600px', margin: '30px auto', boxShadow: '0 4px 8px rgba(0,0,0,0.1)' }}>
<h2>Kolom Komentar</h2>
{/* Daftar Komentar */}
<div style={{ maxHeight: '300px', overflowY: 'auto', marginBottom: '20px', paddingRight: '10px' }}>
{optimisticComments.length === 0 ? (
<p>Belum ada komentar.</p>
) : (
<ul style={{ listStyle: 'none', padding: 0 }}>
{optimisticComments.map((comment) => (
<li key={comment.id} style={{
marginBottom: '10px',
padding: '10px',
border: `1px solid ${comment.isOptimistic ? '#ffc107' : '#eee'}`, // Kuning untuk optimistik
borderRadius: '5px',
backgroundColor: comment.isOptimistic ? '#fffbe6' : '#f9f9f9',
opacity: comment.isOptimistic ? 0.7 : 1 // Sedikit transparan untuk optimistik
}}>
<strong>{comment.author}</strong> ({new Date(comment.timestamp).toLocaleTimeString()}):<br />
{comment.text}
{comment.isOptimistic && <span style={{ fontStyle: 'italic', color: '#555', marginLeft: '5px' }}> (sedang dikirim...)</span>}
</li>
))}
</ul>
)}
</div>
{/* Form Komentar */}
<form ref={formRef} action={handleFormAction} style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<input
type="text"
name="author"
placeholder="Nama Anda"
required
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ddd' }}
/>
<textarea
name="comment"
placeholder="Tulis komentar Anda..."
rows="4"
required
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ddd' }}
></textarea>
<SubmitButton /> {/* Menggunakan komponen tombol terpisah */}
</form>
</div>
);
}
// Contoh bagaimana ini akan dipanggil di halaman utama (Server Component)
/*
// app/page.js
import CommentSection from '@/components/CommentSection';
import { getInitialComments } from '@/app/actions';
export default async function HomePage() {
const comments = await getInitialComments(); // Ambil komentar awal di server
return (
<main>
<h1>Diskusi Artikel React 19</h1>
<CommentSection initialComments={comments} />
</main>
);
}
*/
⭐ Bagaimana useFormStatus dan useOptimistic Bekerja di Sini:
<form action={handleFormAction}>: Ini adalah fitur Actions. Ketika form disubmit, React akan menjalankanhandleFormAction.SubmitButtondenganuseFormStatus():- Begitu
handleFormActiondipanggil dan sedang menunggu respons dariaddComment(Server Action), statuspendingyang dikembalikan olehuseFormStatus()akan otomatis menjaditrue. - Ini akan menonaktifkan tombol dan mengubah teksnya menjadi "Mengirim Komentar...", memberikan feedback visual langsung kepada pengguna tanpa perlu state loading manual di
CommentSection. - Setelah
addCommentselesai (baik sukses maupun gagal),pendingakan kembali menjadifalse.
- Begitu
useOptimistic(initialComments, (currentComments, newCommentInfo) => { ... }):- Ketika pengguna menekan tombol kirim dan
handleFormActiondipanggil, barisaddOptimisticComment({ text: commentText, author: authorName });akan segera dipanggil. - Ini memicu fungsi updater kedua dari
useOptimistic, yang segera menambahkan komentar "sementara" (denganisOptimistic: true) ke daftaroptimisticComments. DaftaroptimisticCommentsinilah yang di-render di UI. - Pengguna langsung melihat komentar mereka muncul di daftar, memberikan ilusi bahwa komentar sudah terkirim, bahkan sebelum Server Action selesai.
- Jika
addCommentberhasil,useOptimisticakan secara internal merekonsiliasi dengan data sebenarnya (yang dalam kasus ini, tidak perlu pembaruan lebih lanjut karena kita hanya menambahkan item baru). - Yang paling keren: Jika
addCommentGAGAL (karenaMath.random() < 0.3di Server Action kita),useOptimisticakan secara otomatis menggulirkan kembali (rollback) state keinitialComments(atau state terakhir yang berhasil), menghapus komentar optimistik yang gagal itu dari UI. Anda tidak perlu menulis logika rollback manual!
- Ketika pengguna menekan tombol kirim dan
Dengan kombinasi ini, kita mendapatkan pengalaman pengguna yang sangat responsif dan mulus, sambil mengurangi kompleksitas state management di sisi klien secara signifikan. Ini benar-benar contoh nyata bagaimana React 19 membuat pengembangan web modern jadi lebih "magis" dan efisien.
4. Reactivity Otomatis

React 19 membawa penyempurnaan dalam hal bagaimana React melacak perubahan data dan me-render ulang komponen. Tujuannya adalah mengurangi kebutuhan untuk memoization manual atau Hooks seperti useMemo dan useCallback di banyak kasus, membuat kode jadi lebih sederhana dan efisien secara default.
Penyempurnaan reaktivitas di React 19 ini belum sepenuhnya dirilis secara publik sebagai fitur yang langsung bisa kita "pakai" dengan sintaks baru di luar eksperimen atau kerangka kerja spesifik (seperti compiler React Forget yang sedang dikembangkan). Namun, inti dari "Reaktivitas Otomatis" ini adalah bagaimana React berupaya mengurangi kebutuhan kita untuk melakukan memoization manual (menggunakan useMemo, useCallback, React.memo) untuk mencegah re-render yang tidak perlu.
Intinya adalah, React ingin jadi lebih pintar dalam memahami kapan sebuah komponen atau prop benar-benar berubah, sehingga ia bisa mengoptimalkan re-render secara otomatis. Ini akan membuat kode kita lebih bersih dan tidak perlu terlalu banyak "hint" manual kepada React.
⭐ Konsep di Balik Reactivity Otomatis (dengan Analogi)
Bayangkan React sebagai koki yang sedang membuat kue.
- Tanpa Reactivity Otomatis (sekarang ini): Koki akan selalu mengecek ulang semua bahan (prop) satu per satu setiap kali ada pesanan baru, bahkan kalau bahan itu belum habis atau tidak perlu dicek lagi. Kita sebagai asisten harus bilang, "Koki, gula ini tidak perlu dicek lagi ya, ini masih sama dengan pesanan sebelumnya!" (ini analogi
useMemo/useCallback). - Dengan Reactivity Otomatis (React 19 yang dituju): Koki akan jadi lebih pintar. Dia tahu secara otomatis bahan mana yang benar-benar baru atau berubah, jadi dia hanya fokus mengecek bahan yang relevan. Kita tidak perlu lagi teriak-teriak mengingatkannya.
⭐ Contoh Kode Kasus (Simulasi Konsep Reactivity Otomatis)
Karena fitur ini bekerja di level compiler atau internal React, kita tidak akan melihat perubahan sintaks yang drastis seperti useFormStatus. Namun, kita bisa melihat perbedaannya dari sudut pandang developer dan bagaimana kode kita menjadi lebih bersih karena tidak perlu memoization yang berlebihan.
Mari kita bandingkan skenario di mana kita memiliki komponen daftar item yang di-filter.
⭐ Contoh Klasik (dengan memo dan useCallback/useMemo untuk Optimasi)
// components/ItemListItem.js
// Ini adalah komponen item tunggal. Dioptimasi dengan React.memo
import React from 'react';
function ItemListItem({ item, onClick }) {
console.log(`Rendering Item: ${item.name}`);
return (
<li onClick={() => onClick(item.id)} style={{ cursor: 'pointer', padding: '5px', borderBottom: '1px solid #eee' }}>
{item.name}
</li>
);
}
// Diperlukan React.memo untuk mencegah re-render jika prop item dan onClick tidak berubah
export default React.memo(ItemListItem);
// components/ItemListWithMemo.js
import React, { useState, useMemo, useCallback } from 'react';
import ItemListItem from './ItemListItem';
export default function ItemListWithMemo({ initialItems }) {
const [filterText, setFilterText] = useState('');
const [count, setCount] = useState(0); // State tambahan yang memicu re-render
// Gunakan useMemo untuk memfilter item HANYA KETIKA initialItems atau filterText berubah
const filteredItems = useMemo(() => {
console.log('Filtering items (dengan useMemo)...');
return initialItems.filter(item =>
item.name.toLowerCase().includes(filterText.toLowerCase())
);
}, [initialItems, filterText]); // Dependensi yang ketat
// Gunakan useCallback untuk memastikan fungsi ini tidak dibuat ulang setiap render
const handleItemClick = useCallback((id) => {
console.log(`Item with ID ${id} clicked!`);
}, []); // Dependensi kosong karena tidak ada variabel luar yang berubah
return (
<div style={{ border: '1px solid #ccc', padding: '20px', borderRadius: '8px' }}>
<h2>Daftar Item (Dengan Memoization Manual)</h2>
<input
type="text"
placeholder="Filter item..."
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
style={{ marginBottom: '10px' }}
/>
<button onClick={() => setCount(c => c + 1)}>
Update Count: {count} {/* Tombol ini akan memicu re-render ItemListWithMemo */}
</button>
<ul>
{filteredItems.map(item => (
<ItemListItem key={item.id} item={item} onClick={handleItemClick} />
))}
</ul>
</div>
);
}
Analisis Contoh Klasik:
- Ketika
countberubah,ItemListWithMemoakan re-render. - Tanpa
useMemo,filteredItemsakan selalu dihitung ulang. DenganuseMemo, dia hanya dihitung ulang jikainitialItemsataufilterTextberubah. - Tanpa
useCallback,handleItemClickakan dibuat ulang setiap render, yang akan menyebabkanItemListItemjuga re-render meskipunitemnya tidak berubah (karenaonClickadalah prop baru).React.memopadaItemListItemmemerlukannya.
Ini adalah boilerplate yang harus kita tambahkan untuk performa, seringkali membuat kode kurang mudah dibaca.
⭐ Contoh Ideal dengan Reactivity Otomatis di React 19 (Skenario Target)
Dengan React 19 dan compiler yang lebih cerdas (seperti React Forget), tujuannya adalah agar kita bisa menulis kode lebih sederhana, dan React yang akan menangani optimasi re-render secara otomatis di belakang layar.
// components/ItemListItem.js (Tidak perlu React.memo di sini jika compiler React Forget aktif)
// Jika React 19 dengan React Forget Compiler sudah matang, kita tidak perlu React.memo lagi.
// React akan cukup pintar untuk tidak merender ulang jika prop tidak berubah.
function ItemListItem({ item, onClick }) {
console.log(`Rendering Item: ${item.name}`); // Akan log hanya ketika item atau onClick benar-benar berubah
return (
<li onClick={() => onClick(item.id)} style={{ cursor: 'pointer', padding: '5px', borderBottom: '1px solid #eee' }}>
{item.name}
</li>
);
}
export default ItemListItem; // Tanpa React.memo
// components/ItemListWithAutoReactivity.js
import { useState } from 'react';
import ItemListItem from './ItemListItem'; // Tidak perlu React.memo di sini
export default function ItemListWithAutoReactivity({ initialItems }) {
const [filterText, setFilterText] = useState('');
const [count, setCount] = useState(0); // State tambahan yang memicu re-render
// Tidak perlu useMemo, React Compiler akan otomatis mem-memoize hasil filtering
// jika initialItems atau filterText tidak berubah
const filteredItems = initialItems.filter(item => {
console.log('Filtering items (otomatis oleh React 19)...'); // Hanya akan log ketika dibutuhkan
return item.name.toLowerCase().includes(filterText.toLowerCase());
});
// Tidak perlu useCallback, React Compiler akan otomatis mem-memoize fungsi ini
// sehingga ItemListItem tidak re-render karena prop onClick yang "baru"
const handleItemClick = (id) => {
console.log(`Item with ID ${id} clicked!`);
};
return (
<div style={{ border: '1px solid #007bff', padding: '20px', borderRadius: '8px' }}>
<h2>Daftar Item (Dengan Reactivity Otomatis - Target React 19)</h2>
<input
type="text"
placeholder="Filter item..."
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
style={{ marginBottom: '10px' }}
/>
<button onClick={() => setCount(c => c + 1)}>
Update Count: {count}
</button>
<ul>
{filteredItems.map(item => (
<ItemListItem key={item.id} item={item} onClick={handleItemClick} />
))}
</ul>
</div>
);
}
Analisis Contoh Ideal (Target React 19):
- Kode jauh lebih bersih dan lebih mudah dibaca.
- Kita tidak perlu lagi menulis
useMemo,useCallback, atauReact.memosecara eksplisit di banyak kasus. - Ketika
countberubah,ItemListWithAutoReactivityakan re-render, tapi React Compiler akan memastikan bahwa:filteredItemstidak akan dihitung ulang jikainitialItemsdanfilterTexttidak berubah.handleItemClicktidak akan dibuat ulang (atau jika dibuat ulang, React akan menyadari bahwa itu adalah fungsi yang secara fungsional sama dengan versi sebelumnya), sehinggaItemListItemtidak akan re-render secara tidak perlu.
- Console log "Filtering items (otomatis oleh React 19)..." dan "Rendering Item: ..." hanya akan muncul ketika ada perubahan yang benar-benar memengaruhinya, bukan setiap kali parent re-render.
Penting untuk diingat: "Reactivity Otomatis" ini adalah tujuan dari tim React, yang sedang diwujudkan melalui proyek seperti React Forget Compiler. Pada saat ini (Juli 2025), ini adalah visi untuk bagaimana React 19 dan versi selanjutnya akan bekerja secara default, bukan sesuatu yang kita aktifkan dengan sintaks baru di setiap komponen. Ini akan menjadi penyempurnaan di balik layar yang membuat kinerja aplikasi React menjadi lebih baik "secara gratis" tanpa developer perlu pusing memikirkan memoization manual yang seringkali menjadi sumber bug atau kompleksitas.
Intinya, semua fitur ini dirancang untuk membuat aplikasi web jadi lebih cepat, lebih interaktif, dan lebih mudah dikembangkan.
Filosofi di Balik Pengembangan React 19: Kinerja dan Pengalaman Pengembang
Setiap rilis React itu punya filosofi yang kuat di baliknya, dan untuk React 19, fokusnya jelas: memaksimalkan kinerja aplikasi dan meningkatkan pengalaman pengembang (Developer Experience/DX).
Dari sisi kinerja, React tim benar-benar ingin agar aplikasi yang dibangun dengan React bisa bersaing bahkan melampaui performa aplikasi native atau yang dirender sepenuhnya di server. Dengan Server Components dan optimasi rendering lainnya, mereka berusaha meminimalisir client-side JavaScript dan memaksimalkan first load performance. Pengguna akan merasakan aplikasi lebih cepat dan responsif, bahkan di koneksi internet yang lambat.
Untuk pengalaman pengembang, tujuannya adalah menyederhanakan kode dan menghilangkan boilerplate yang sering bikin frustrasi. Hooks seperti useFormStatus dan useOptimistic serta Actions adalah contoh sempurna bagaimana React berusaha membuat hal-hal yang tadinya kompleks (seperti form handling dan optimistic UI) jadi jauh lebih intuitif dan mudah diimplementasikan. Bayangin, kamu bisa fokus ke logika bisnis aplikasi, bukan lagi pusing mikirin manajemen state atau rendering yang rumit. React ingin kita sebagai developer bisa lebih produktif dan senang saat koding.
Kompatibilitas Mundur dan Proses Migrasi
Mungkin kamu bertanya-tanya, "Apakah project React lama saya bakal langsung rusak kalau update ke React 19?" Jangan khawatir! Tim React selalu berusaha keras untuk memastikan kompatibilitas mundur semaksimal mungkin. Artinya, sebagian besar kode yang sudah kamu tulis di React 18 (atau bahkan versi sebelumnya) seharusnya masih bisa berjalan di React 19 tanpa banyak perubahan drastis.
Namun, seperti setiap update besar lainnya, pasti ada beberapa penyesuaian atau breaking changes minor, terutama jika kamu menggunakan fitur-fitur yang sudah lama atau praktik yang tidak disarankan. React menyediakan panduan migrasi yang detail dan tool bantu (seperti mode Strict yang lebih ketat atau linters) untuk membantu kita mengidentifikasi dan memperbaiki potensi masalah.
Proses migrasi biasanya melibatkan:
- Meng-update paket
reactdanreact-domke versi 19. - Meninjau warning atau error di konsol yang mungkin muncul akibat perubahan perilaku tertentu.
- Memanfaatkan Strict Mode untuk menemukan praktik yang tidak disarankan dan mempersiapkan kode untuk fitur konkurensi penuh.
- Mempertimbangkan untuk mengadopsi fitur baru seperti Server Components dan Actions secara bertahap, jika relevan dengan kebutuhan proyek.
Intinya, React 19 dirancang untuk menjadi peningkatan yang mulus bagi sebagian besar proyek, dengan panduan yang jelas untuk memastikan transisi yang lancar. Ini bukan re-write total, tapi sebuah evolusi yang membawa kita ke masa depan pengembangan web yang lebih cepat dan efisien.
Bab 3: Kekuatan Server Components: Mengubah Paradigma Rendering

Setelah kita melihat sekilas fitur-fitur baru di React 19, mari kita selami lebih dalam salah satu yang paling revolusioner: React Server Components (RSC). Fitur ini bukan cuma update biasa; ini adalah perubahan besar dalam cara kita berpikir tentang rendering aplikasi web. Bersiaplah, karena RSC mengubah permainan!
1. Dari Client-Side Rendering ke Hibrida: Memahami Konsep Server Components
Selama bertahun-tahun, sebagian besar aplikasi web modern yang dibangun dengan React menganut pendekatan Client-Side Rendering (CSR). Artinya, ketika kamu mengunjungi sebuah website, browser akan mengunduh file HTML yang relatif kosong, lalu mengunduh semua JavaScript React. Setelah JavaScript ini dijalankan di browser kamu, barulah konten dan UI aplikasi dirender dan ditampilkan. Ibaratnya, browser kamu itu seperti koki yang harus menyiapkan semua bahan dan memasak makanannya sendiri dari nol.
Masalahnya, pendekatan CSR ini punya beberapa kelemahan, terutama untuk initial load dan SEO (Search Engine Optimization). Browser harus menunggu semua JavaScript diunduh dan dieksekusi sebelum konten utama terlihat, yang bisa bikin loading terasa lambat. Buat mesin pencari, ini juga jadi tantangan karena mereka mungkin kesulitan mengindeks konten yang baru muncul setelah JavaScript jalan.
Nah, di sinilah React Server Components (RSC) hadir sebagai solusi hibrida yang cerdas. Dengan RSC, beberapa komponen React bisa dirender di sisi server, bukan di browser klien. Bayangin, sebagian "masakan" sudah disiapkan matang-matang di dapur (server) sebelum dikirim ke meja (browser) kamu.
Apa artinya ini?
- HTML yang Sudah Terisi: Ketika browser menerima respons dari server, ia sudah mendapatkan HTML yang berisi sebagian besar konten aplikasi, lengkap dengan data. Jadi, pengguna bisa melihat sesuatu yang berarti lebih cepat.
- JavaScript yang Lebih Kecil: Karena banyak rendering sudah terjadi di server, browser tidak perlu lagi mengunduh JavaScript yang sebanyak itu untuk menampilkan konten awal. JavaScript yang diunduh hanya yang benar-benar dibutuhkan untuk interaktivitas (komponen yang memerlukan state, event listener, dll. – yang kita sebut Client Components).
Jadi, kita punya dua jenis komponen:
- Server Components: Komponen yang dirender di server. Cocok untuk mengambil data, berinteraksi langsung dengan backend atau database, dan merender UI statis atau dinamis yang tidak memerlukan interaktivitas di browser. Mereka tidak punya state atau lifecycle seperti
useStateatauuseEffect. - Client Components: Komponen yang dirender di browser klien. Ini adalah komponen React "tradisional" yang sudah kamu kenal, yang punya state, effect, dan event listener. Mereka ditandai dengan
'use client';di bagian atas file.
Pendekatan hibrida ini memungkinkan kita memanfaatkan kekuatan server untuk performa awal dan kekuatan klien untuk interaktivitas yang kaya.
2. Manfaat Kinerja dan SEO yang Dibawa oleh Server Components
Adopsi Server Components membawa banyak keuntungan, terutama dalam dua area krusial: kinerja dan SEO.
⭐ Peningkatan Kinerja Aplikasi
- Waktu Muat Awal yang Lebih Cepat (Faster Initial Load): Karena server mengirimkan HTML yang sudah dirender dan berisi konten, browser bisa langsung menampilkan sesuatu ke pengguna. Ini mengurangi waktu First Contentful Paint (FCP) dan Largest Contentful Paint (LCP), yang merupakan metrik penting untuk user experience. Pengguna tidak perlu menunggu JavaScript diunduh dan dieksekusi untuk melihat konten utama.
- Ukuran Bundle JavaScript yang Lebih Kecil: Logika data fetching dan rendering komponen di server berarti kode JavaScript untuk bagian-bagian tersebut tidak perlu dikirim ke browser. Ini secara signifikan mengurangi ukuran bundle JavaScript yang harus diunduh oleh klien, mempercepat waktu eksekusi skrip, dan membuat aplikasi lebih ringan.
- Mengurangi Perjalanan Bolak-balik (Network Roundtrips): Data bisa diambil langsung di server saat komponen dirender, seringkali langsung dari database atau API internal di datacenter yang sama. Ini jauh lebih cepat daripada browser harus melakukan panggilan API terpisah ke server setelah JavaScript dimuat.
⭐ Manfaat untuk SEO (Search Engine Optimization)
- Konten Langsung Tersedia untuk Crawler: Mesin pencari seperti Google mengandalkan web crawler untuk membaca dan mengindeks konten di website kamu. Dengan RSC, konten HTML yang lengkap sudah tersedia saat halaman pertama kali dimuat. Ini memastikan crawler dapat melihat dan mengindeks semua informasi relevan di halaman, yang sangat penting untuk peringkat SEO yang baik. Berbeda dengan CSR murni di mana crawler harus menunggu JavaScript dieksekusi, RSC memberikan konten instan.
- Pengalaman Pengguna yang Lebih Baik = Peringkat SEO yang Lebih Baik: Google dan mesin pencari lainnya semakin memprioritaskan pengalaman pengguna. Website yang cepat dimuat dan responsif cenderung mendapatkan peringkat yang lebih baik. Dengan peningkatan kinerja yang dibawa oleh RSC, website kamu akan menjadi lebih ramah pengguna dan, pada gilirannya, lebih disukai oleh mesin pencari.
3. Implementasi dan Contoh Kasus Penggunaan Server Components di React 19
Implementasi Server Components di React 19 sangatlah mudah, terutama jika kamu menggunakan kerangka kerja seperti Next.js 13+ yang sudah mendukungnya secara native. Kunci utamanya adalah bagaimana kamu menandai komponenmu.
- Default = Server Component: Secara default, di lingkungan yang mendukung RSC, setiap komponen React yang kamu buat akan dianggap sebagai Server Component kecuali kamu secara eksplisit memberitahu React sebaliknya.
'use client';untuk Client Component: Untuk mengubah sebuah komponen menjadi Client Component (yang akan dirender di browser dan bisa punya state serta effects), kamu cukup menambahkan direktif'use client';di bagian paling atas file komponen tersebut.
4. Contoh Kasus Penggunaan:
Mari kita lihat beberapa skenario di mana RSC bersinar:
⭐ Halaman Produk E-commerce:
- Server Component: Komponen utama
ProductPagebisa menjadi Server Component yang bertanggung jawab untuk mengambil detail produk (nama, harga, deskripsi, gambar) langsung dari database di server. Ini memastikan detail produk yang kaya SEO langsung termuat saat halaman pertama kali dibuka. - Client Component: Komponen
AddToCartButton(yang memerlukan state untuk jumlah item di keranjang) atauImageCarousel(yang interaktif) bisa menjadi Client Component yang diimpor ke dalamProductPage. Hanya JavaScript untuk interaktivitas ini yang akan dikirim ke browser.
⭐ Blog Post / Artikel:
- Server Component: Komponen
BlogPostbisa mengambil konten artikel, judul, penulis, dan tanggal dari CMS (Content Management System) atau database. Karena ini sebagian besar konten statis, merendernya di server akan membuat artikel termuat super cepat dan bagus untuk SEO. - Client Component: Komponen
CommentSection(yang memungkinkan pengguna mengirim komentar secara interaktif) atauShareButtons(dengan event listener) akan menjadi Client Component.
⭐ Dashboard / Halaman Admin (Data Read-only):
- Server Component: Bagian dashboard yang menampilkan grafik atau tabel data summary yang hanya bersifat read-only bisa dirender sebagai Server Component. Data diambil langsung dari server dan HTML yang sudah terisi dikirim.
- Client Component: Jika ada tombol untuk mengubah filter tanggal, mengurutkan tabel, atau widget yang interaktif, komponen-komponen tersebut bisa menjadi Client Component.
Dengan RSC, kita tidak perlu lagi kompromi antara performa initial load yang cepat dan interaktivitas yang kaya. Kita bisa punya keduanya dengan menggabungkan Server Components dan Client Components secara strategis, membangun aplikasi web yang lebih cepat, efisien, dan ramah SEO.
Bab 4: Interaktivitas yang Lebih Cerdas dengan Fitur Baru

Setelah kita bicara tentang revolusi rendering berkat Server Components di Bab 3, sekarang mari kita fokus ke bagaimana React 19 membuat aplikasi kita terasa lebih "hidup" dan responsif di mata pengguna. Fitur-fitur baru ini, terutama Actions, useFormStatus, dan useOptimistic, adalah kunci untuk membangun antarmuka yang tidak hanya efisien, tapi juga sangat menyenangkan saat digunakan.
1. Actions: Menyederhanakan Penanganan Interaksi Form dan Data
Pernah merasa ribet saat harus mengelola pengiriman form atau data mutation di React? Mulai dari membuat event handler, mengurus state loading, menangani error, sampai memperbarui data di UI setelah berhasil? Itu semua adalah bagian yang cukup memakan waktu. Nah, Actions hadir untuk menyederhanakan semua itu secara drastis!
Secara sederhana, Actions memungkinkan kamu memanggil fungsi yang berjalan di server langsung dari komponen klienmu, baik itu dari form HTML standar atau dari event handler lainnya. React akan mengurus jembatan komunikasi antara klien dan server untukmu.
Bayangkan kamu punya form untuk menambahkan postingan blog. Dulu, kamu mungkin akan punya kode seperti ini:
// Sebelum Actions:
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
try {
const response = await fetch('/api/add-post', { /* ... */ });
// Update UI
} catch (error) {
// Handle error
} finally {
setIsLoading(false);
}
};
<form onSubmit={handleSubmit}>
{/* ... */}
</form>
Dengan Actions di React 19, prosesnya jauh lebih bersih:
// Dengan Actions (Server Action):
// app/actions.js
"use server";
export async function createPost(formData) {
// Langsung berinteraksi dengan database atau logika bisnis di server
const title = formData.get('title');
const content = formData.get('content');
// ... simpan ke DB ...
console.log('Postingan baru dibuat:', title);
return { success: true };
}
// Di komponen React:
import { createPost } from './app/actions';
<form action={createPost}> {/* Langsung menunjuk ke Server Action! */}
<input type="text" name="title" />
<textarea name="content"></textarea>
<button type="submit">Kirim Postingan</button>
</form>
Lihat bedanya? Kamu tidak perlu lagi menulis handler onSubmit yang panjang, mengelola state loading secara manual di komponen ini, atau pusing memikirkan fetch API. Cukup arahkan atribut action dari <form>-mu ke fungsi Server Action yang kamu buat. React akan otomatis mengurus pengiriman data (FormData), pending state, dan bahkan optimistic updates (yang akan kita bahas berikutnya) di belakang layar. Ini benar-benar membuat interaksi form jadi lebih ringkas dan intuitif.
2. useFormStatus dan useOptimistic: Membangun UI yang Responsif dan Menyenangkan
Actions memang sudah sangat membantu, tapi bagaimana kita memberikan feedback instan kepada pengguna? Di sinilah dua Hook baru, useFormStatus dan useOptimistic, bersinar dan bekerja sangat apik dengan Actions. Mereka adalah kunci untuk menciptakan pengalaman pengguna yang seamless dan responsif.
useFormStatus: Tahu Kapan Form Lagi Sibuk
Pernah melihat tombol "Kirim" yang berubah jadi "Mengirim..." atau jadi disabled saat kamu klik? Itu adalah feedback penting yang memberitahu pengguna bahwa ada proses yang sedang berjalan. Dulu, kita harus mengelola state isLoading di komponen induk dan meneruskannya ke tombol.
Dengan useFormStatus, kamu bisa mendapatkan status pending dari form terdekat secara otomatis. Kamu tinggal mengimpornya dan menggunakannya di dalam komponen tombolmu:
// components/SubmitButton.js
"use client"; // Ini Client Component karena ada Hook
import { useFormStatus } from 'react-dom'; // Perhatikan: ini dari 'react-dom'
export function SubmitButton() {
const { pending } = useFormStatus(); // Otomatis tahu apakah form induk sedang submit
return (
<button type="submit" disabled={pending}>
{pending ? 'Mengirim...' : 'Kirim Sekarang'}
</button>
);
}
Sekarang, setiap kali form yang berisi SubmitButton ini melakukan submission (misalnya lewat Action), pending akan otomatis jadi true dan tombol akan nonaktif. Ketika submission selesai, pending kembali false. Super mudah dan bersih!
useOptimistic: "Seolah-olah Sudah Terjadi"
Ini adalah Hook yang paling "ajaib" dan paling banyak mengubah pengalaman pengguna. useOptimistic memungkinkan kamu untuk memperbarui UI secara "optimistik" (seolah-olah perubahan sudah berhasil) bahkan sebelum respons dari server diterima. Jika server mengembalikan sukses, UI tetap di kondisi optimistik. Tapi, jika ada error, UI akan otomatis "di-rollback" kembali ke kondisi sebelumnya.
Bayangkan kamu mengirim komentar. Dengan useOptimistic, komentarmu bisa langsung muncul di daftar, seolah-olah sudah terkirim, lengkap dengan tanda "sedang dikirim..." di sampingnya. Ini memberikan kesan instan. Jika nanti ternyata ada error dari server, komentar itu akan otomatis hilang dari daftar atau berubah status menjadi "gagal".
// components/CommentForm.js
"use client";
import { useRef } from 'react';
import { useFormStatus, useOptimistic } from 'react-dom'; // Dari 'react-dom'
import { addComment } from '@/app/actions'; // Server Action kita
// ... SubmitButton komponen seperti di atas ...
export default function CommentForm({ initialComments }) {
const formRef = useRef();
// useOptimistic(state_saat_ini, (state_saat_ini, payload_optimistik) => new_state_optimistik)
const [optimisticComments, addOptimisticComment] = useOptimistic(
initialComments,
(currentComments, newCommentInfo) => [
...currentComments,
{ id: 'temp-' + Date.now(), text: newCommentInfo.text, author: newCommentInfo.author, pending: true }
]
);
const handleFormAction = async (formData) => {
const commentText = formData.get('comment');
const authorName = formData.get('author');
// 1. Tampilkan komentar secara optimistik
addOptimisticComment({ text: commentText, author: authorName });
formRef.current.reset(); // Kosongkan form segera
// 2. Panggil Server Action
const result = await addComment(formData);
// 3. Tangani hasil server (jika ada error, useOptimistic akan otomatis rollback)
if (!result.success) {
alert(`Gagal mengirim: ${result.message}`);
// Tidak perlu rollback manual, useOptimistic akan mengurusnya
}
// Jika sukses, useOptimistic akan tetap mempertahankan state terbaru
};
return (
<div>
{/* Daftar komentar dengan yang optimistik */}
<ul>
{optimisticComments.map((comment) => (
<li key={comment.id} style={{ opacity: comment.pending ? 0.7 : 1 }}>
{comment.text} - {comment.author} {comment.pending && <em>(sedang dikirim...)</em>}
</li>
))}
</ul>
{/* Form untuk mengirim komentar */}
<form ref={formRef} action={handleFormAction}>
<input type="text" name="author" placeholder="Nama Anda" />
<textarea name="comment" placeholder="Komentar Anda"></textarea>
<SubmitButton />
</form>
</div>
);
}
Kombinasi Actions, useFormStatus, dan useOptimistic adalah trio yang sangat kuat. Mereka bekerja sama untuk mengurangi boilerplate kode, memindahkan logika yang relevan ke server (dengan Actions), dan memberikan feedback visual yang instan dan handal kepada pengguna, bahkan di tengah kondisi jaringan yang tidak stabil.
3. Peningkatan Pengalaman Pengguna Melalui Pembaruan Konkuren
Fitur-fitur yang kita bahas di atas, ditambah dengan penyempurnaan di balik layar seperti Concurrent Features yang sudah ada sejak React 18 (dan terus ditingkatkan di React 19), secara fundamental mengubah bagaimana React mengelola rendering dan pembaruan UI.
Konkurensi adalah kemampuan React untuk bekerja pada beberapa tugas secara bersamaan, bahkan memprioritaskan tugas-tugas yang lebih penting (seperti input pengguna) di atas tugas yang kurang mendesak (seperti fetch data di latar belakang).
Manfaatnya bagi pengalaman pengguna adalah:
- UI yang Tetap Responsif Selama Pembaruan Berat: Aplikasi tidak akan "macet" atau freeze saat melakukan kalkulasi data yang besar atau memuat data baru. React bisa menjaga UI tetap interaktif sambil memproses pembaruan di belakang layar.
- Transisi yang Lebih Mulus: Saat beralih antara halaman atau tampilan yang berbeda, React bisa mulai me-render tampilan baru tanpa langsung memblokir tampilan lama. Ini menciptakan transisi yang lebih halus, mirip dengan pengalaman di aplikasi native.
- Optimistic UI yang Lebih Andal: Seperti yang kita lihat dengan
useOptimistic, kemampuan React untuk mengelola state sementara dan melakukan rollback jika ada error adalah bagian dari kekuatan konkurensi. Ini membuat pengalaman "seolah-olah sudah terjadi" menjadi sangat bisa diandalkan.
Singkatnya, React 19 tidak hanya memberi kita tool baru untuk menulis kode yang lebih baik, tapi juga meningkatkan inti mesin rendering-nya untuk memastikan aplikasi kita selalu terasa cepat, responsif, dan menyenangkan bagi siapa pun yang menggunakannya. Ini adalah alasan mengapa React JS 19 benar-benar pilihan tepat untuk web modern yang menuntut performa dan pengalaman pengguna prima.
Bab 5: Studi Kasus dari BuildWithAngga

Bila kamu mau belajar lebih dalam dan lebih lanjut lagi aku saranin mengikuti kelas dari BuildWithAngga. BuildWithAngga punya pendekatan pembelajaran yang cukup beda dibanding platform belajar coding lainnya, terutama buat pemula dan orang yang suka belajar visual. Berikut beberapa hal yang bikin mereka menonjol:
⭐ Belajar dari Real Project, Bukan Teori Doang
Di BuildWithAngga, kamu gak cuma belajar sintaks, tapi langsung bikin project nyata kayak:
- Website portfolio modern
- Aplikasi booking seperti Tokopedia Travel
- Landing page profesional
- UI design pakai Figma
➡️ Jadi kamu langsung lihat hasilnya, bukan cuma hafalin kode.
⭐ Visual Interaktif & UI Keren
Mereka bener-bener fokus ke desain antarmuka yang kece:
- Kelas banyak yang berfokus pada Frontend (HTML, CSS, Tailwind, React)
- Desainnya modern dan sesuai tren industri
- Belajar coding sekaligus belajar taste desain
Cocok banget buat kamu yang pengin kerja sebagai UI/UX Developer.
⭐ Materi Up-to-date & Fokus ke Industri
BuildWithAngga ngikutin tren teknologi terbaru:
- Tailwind CSS, Next.js, Laravel 10
- Vercel deployment, GitHub versioning
- Animasi interaktif, Responsive Web Design
➡️ Gak belajar hal yang "jadul" dan kurang relevan.
⭐ Penjelasan Simpel, Bahasa Indonesia
Semua materi pakai bahasa Indonesia dengan gaya yang santai, receh tapi dalam, cocok banget buat kamu yang bosen dengan pembelajaran kaku dan textbook.
➡️ Gak bikin stres dan lebih mudah dipahami, terutama buat pemula.
⭐ Mentoring & Sertifikat Karier
- Bisa dapet sertifikat resmi
- Ada career mentoring buat bantu kamu masuk dunia kerja
- Kelas premium biasanya dilengkapi dengan review dari mentor
➡️ Jadi bukan sekadar belajar, tapi juga disiapkan untuk kerja.
Kalau kamu suka gaya belajar yang langsung "terjun ke lapangan", visual menarik, dan gampang dipahami, BuildWithAngga jelas beda dan cocok untuk kamu yang mau serius masuk dunia kreatif & digital.
Bab 6: Kesimpulan

Kita sudah menjelajahi perjalanan React JS, mulai dari akarnya sebagai library UI hingga evolusi terbarunya yang paling menarik dengan React 19. Dari setiap bab yang kita bahas, satu hal jadi sangat jelas: React 19 bukan cuma update tambahan, melainkan sebuah loncatan fundamental yang secara signifikan membentuk ulang masa depan pengembangan web.
jika kamu sedang membangun aplikasi web baru atau mempertimbangkan untuk memodernisasi yang sudah ada, React 19 menawarkan kombinasi kekuatan, efisiensi, dan kemudahan pengembangan yang sulit ditandingi. Ini adalah fondasi kokoh yang akan membantu Anda menciptakan pengalaman digital yang luar biasa di era web modern.