ReactJS

Belum masuk. Progresmu tidak tersimpan.

Masuk
  • Bab 9 dari 10
  • 30 menit
  • 1 latihan kode
  • 4 soal kuis

React Router, State Aplikasi, dan Styling

Di bab ini (6 bagian)
  1. Tujuan
  2. React Router: banyak halaman dalam satu aplikasi
  3. Manajemen state aplikasi
  4. Cara styling
  5. Kesalahan umum
  6. Rangkuman

Tujuan bab ini

  • Membuat navigasi antarhalaman dengan React Router: route, link, parameter URL, navigasi lewat kode, dan halaman 404.
  • Memilih tempat state yang tepat: state lokal, Context + useReducer, atau pustaka seperti Zustand dan Redux Toolkit.
  • Membandingkan empat cara styling (CSS biasa, CSS Modules, Tailwind CSS, inline style) dan tahu kapan memakai masing-masing.

React Router: banyak halaman dalam satu aplikasi

Aplikasi React buatan Vite adalah SPA (Single Page Application). Browser hanya memuat satu index.html, lalu JavaScript yang menentukan komponen apa yang tampil. Kalau kamu ingin alamat /tentang dan /produk/7 menampilkan halaman berbeda, kamu butuh router. Pilihan paling populer adalah React Router (versi 7).

npm install react-router

Di versi 7, semua impor cukup dari react-router. Paket lama react-router-dom masih ada dan isinya sama, jadi tutorial lama yang memakai nama itu tetap jalan.

import { BrowserRouter, Routes, Route, Link } from 'react-router';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Beranda</Link> | <Link to="/tentang">Tentang</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Beranda />} />
        <Route path="/tentang" element={<Tentang />} />
        <Route path="/produk/:id" element={<DetailProduk />} />
        <Route path="*" element={<TidakDitemukan />} />
      </Routes>
    </BrowserRouter>
  );
}

Cara bacanya:

BagianTugasnya
BrowserRouterMembungkus aplikasi dan membaca URL dari address bar
RoutesMemilih satu Route yang paling cocok dengan URL
Route path elementPasangan alamat dan komponen halamannya
:idBagian dinamis, bisa berisi apa saja
path="*"Penangkap semua alamat yang tidak cocok, cocok untuk 404

Link, bukan <a>

Tag <a href="/tentang"> membuat browser meminta dokumen baru ke server: halaman berkedip, state hilang, semua JavaScript dimuat ulang. <Link to="/tentang"> menghasilkan <a> juga, tetapi klik-nya dicegat: URL diganti lewat History API dan React hanya menukar komponen. Hasilnya cepat dan state aplikasi tetap hidup.

useParams dan useNavigate

import { useParams, useNavigate } from 'react-router';

function DetailProduk() {
  const { id } = useParams();       // URL /produk/7 → id = '7' (string!)
  const navigate = useNavigate();

  function hapus() {
    // ... hapus produk di server
    navigate('/');                  // pindah halaman lewat kode
  }

  return (
    <>
      <h1>Produk #{id}</h1>
      <button onClick={hapus}>Hapus</button>
      <button onClick={() => navigate(-1)}>Kembali</button>
    </>
  );
}

useParams selalu mengembalikan string. Kalau ID-mu angka, ubah dengan Number(id) sebelum membandingkan. useNavigate dipakai untuk pindah halaman setelah suatu aksi, misalnya selesai login atau menyimpan form. navigate(-1) sama dengan tombol back browser.

Mode data: createBrowserRouter

React Router juga punya gaya konfigurasi berbentuk objek, yang membuka fitur seperti loader (mengambil data sebelum halaman tampil):

import { createBrowserRouter, RouterProvider } from 'react-router';

const router = createBrowserRouter([
  { path: '/', element: <Beranda /> },
  { path: '/produk/:id', element: <DetailProduk /> },
  { path: '*', element: <TidakDitemukan /> },
]);

createRoot(document.getElementById('root')).render(<RouterProvider router={router} />);

Untuk belajar, gaya BrowserRouter lebih mudah dibaca. Keduanya memakai Link, useParams, dan useNavigate yang sama. Satu catatan saat deploy: server hosting perlu diatur agar semua alamat dikembalikan ke index.html, kalau tidak, membuka /tentang langsung bisa menghasilkan 404 dari server.

Manajemen state aplikasi

Pertanyaan "state ini taruh di mana?" muncul di setiap proyek. Aturan praktisnya: mulai dari yang paling dekat, naik hanya kalau perlu.

Pilihan manajemen state

  1. State lokal (useState). Isi input, modal terbuka atau tidak, tab aktif. Hanya satu komponen yang peduli.
  2. Diangkat ke induk (lifting state up). Dua atau tiga komponen berdekatan butuh data yang sama.
  3. Context. Data dibutuhkan banyak komponen di berbagai kedalaman: tema, bahasa, user yang login. Context menghapus prop drilling.
  4. Pustaka state. State global besar yang sering berubah dan disentuh banyak fitur.

Context + useReducer

Kalau logika update state mulai rumit (tambah, hapus, ubah jumlah), pindahkan ke reducer: fungsi murni (state, action) => stateBaru. Gabungkan dengan Context supaya bisa diakses dari mana saja.

import { createContext, useContext, useReducer } from 'react';

function keranjangReducer(state, action) {
  switch (action.type) {
    case 'tambah':
      return [...state, action.item];
    case 'hapus':
      return state.filter((i) => i.id !== action.id);
    default:
      return state;
  }
}

const KeranjangContext = createContext(null);

export function KeranjangProvider({ children }) {
  const [items, dispatch] = useReducer(keranjangReducer, []);
  return <KeranjangContext value={{ items, dispatch }}>{children}</KeranjangContext>;
}

export const useKeranjang = () => useContext(KeranjangContext);

// di komponen mana pun di dalam provider
const { items, dispatch } = useKeranjang();
dispatch({ type: 'tambah', item: { id: 1, nama: 'Kaos' } });

Di React 19, <KeranjangContext value={...}> bisa langsung dipakai sebagai provider (sebelumnya <KeranjangContext.Provider>). Reducer tidak boleh mengubah state lama; selalu kembalikan array atau objek baru.

Kapan Zustand atau Redux Toolkit?

Context punya kelemahan: setiap kali nilainya berubah, semua komponen yang memakainya ikut render ulang. Untuk state yang sering berubah dan dipakai di banyak tempat, pustaka khusus lebih efisien.

  • Zustand: kecil, tanpa provider, komponen hanya render ulang kalau bagian yang ia pilih berubah.
import { create } from 'zustand';

const useKeranjang = create((set) => ({
  items: [],
  tambah: (item) => set((s) => ({ items: [...s.items, item] })),
}));

const jumlah = useKeranjang((s) => s.items.length);
  • Redux Toolkit: lebih terstruktur, cocok untuk aplikasi besar dan tim besar, dengan Redux DevTools yang bisa memutar ulang riwayat aksi.

Untuk data yang berasal dari server (daftar produk, profil), jangan masukkan ke Redux. TanStack Query lebih cocok karena sudah mengurus cache dan refetch.

Cara styling

React tidak memaksa satu cara styling. Ini empat yang paling sering kamu temui:

// 1. CSS biasa: berlaku global
import './App.css';
<div className="kartu">...</div>

// 2. CSS Modules: file Kartu.module.css
import s from './Kartu.module.css';
<div className={s.kartu}>...</div>   // jadi class unik, misalnya _kartu_x8f2a

// 3. Tailwind CSS: utility class langsung di JSX
<div className="p-4 rounded-xl bg-white shadow">...</div>

// 4. Inline style: objek JavaScript, nama camelCase
<p style={{ fontSize: 18, color: 'tomato' }}>Halo</p>  // font-size:18px
CaraKelebihanPerhatikan
CSS biasaPaling familiar, tanpa setupNama class bisa bentrok antar komponen
CSS ModulesNama class otomatis unik, didukung Vite tanpa konfigurasiPindah-pindah file JSX dan CSS
Tailwind CSSCepat, konsisten, tidak perlu menamai classClass panjang di JSX, perlu hafal utility
Inline styleCocok untuk nilai dinamis (lebar progress bar)Tanpa :hover, media query, atau animasi

Perhatikan: di JSX atributnya className, bukan class, dan style menerima objek, bukan string. Angka tanpa satuan di properti seperti fontSize otomatis dianggap px. Selain itu ada component library (MUI, shadcn/ui) yang menyediakan tombol dan dialog siap pakai. Yang terpenting: satu proyek konsisten memakai satu pendekatan utama.

Kesalahan umum

  • Memakai <a href> untuk navigasi internal. Halaman reload dan state hilang; pakai <Link>.
  • Membandingkan hasil useParams dengan angka. '7' === 7 bernilai false.
  • Lupa route *. Alamat yang salah menampilkan halaman kosong tanpa penjelasan.
  • Langsung memasang Redux di proyek kecil. Mulai dari state lokal dan Context dulu.
  • Menulis style="color: red". Di JSX tulis style={{ color: 'red' }}.

Rangkuman

  • React Router memetakan URL ke komponen lewat Routes dan Route; Link berpindah halaman tanpa reload.
  • useParams membaca bagian dinamis URL sebagai string, useNavigate berpindah halaman lewat kode, path="*" menangani 404.
  • Taruh state sedekat mungkin dengan pemakainya; naik ke induk, lalu Context + useReducer, baru pustaka seperti Zustand atau Redux Toolkit.
  • CSS biasa, CSS Modules, Tailwind, dan inline style punya kelebihan masing-masing; pilih satu yang utama dan konsisten.

Selesaikan bab ini

  1. 1. Baca materi
  2. 2. Latihan kode (1)
  3. 3. Kuis (4 soal)

Latihan kode: Reducer keranjang dan pencocok rute

Dua fungsi murni di balik bab ini.

1. keranjangReducer(state, action) dipakai dengan useReducer(keranjangReducer, []) di dalam KeranjangProvider. state adalah array item { id, nama, harga, qty }. Jangan pernah mengubah array atau objek lama.

  • { type: 'tambah', item }: bila id sudah ada, qty item itu bertambah 1; bila belum, tambahkan item di akhir dengan qty: 1.
  • { type: 'hapus', id }: buang item dengan id tersebut.
  • { type: 'ubahJumlah', id, qty }: ganti qty; bila qty <= 0, item dibuang.
  • Aksi lain: kembalikan state yang sama persis.

2. cocokkanRute(pola, path) meniru cara React Router membaca useParams. Pecah keduanya dengan '/'. Bila jumlah bagian berbeda, atau bagian biasa tidak sama, kembalikan null. Bagian pola yang diawali : mengisi objek params. Contoh: cocokkanRute('/produk/:id', '/produk/42') menghasilkan { id: '42' }, dan cocokkanRute('/tentang', '/tentang') menghasilkan {}.

Cek pemahaman

  1. 1Hook React Router mana yang membaca nilai :id dari alamat /produk/:id?

  2. 2Apa output kode berikut?

    function reducer(state, action) {
      switch (action.type) {
        case 'tambah':
          return { ...state, jumlah: state.jumlah + action.n };
        case 'reset':
          return { ...state, jumlah: 0 };
        default:
          return state;
      }
    }
    
    let s = { jumlah: 1, nama: 'A' };
    s = reducer(s, { type: 'tambah', n: 2 });
    s = reducer(s, { type: 'kurang', n: 5 });
    s = reducer(s, { type: 'tambah', n: 3 });
    console.log(s.jumlah, s.nama);
  3. 3Nama class di file .module.css diubah otomatis menjadi unik sehingga tidak bentrok dengan komponen lain.

  4. 4Mode gelap/terang dibutuhkan header, sidebar, dan puluhan tombol. Pilihan paling sederhana tanpa pustaka tambahan adalah...