Belum masuk. Progresmu tidak tersimpan.

Masuk
Ruang Materi
Daftar

Logika Keranjang Belanja

Bangun sendiri dari brief di bawah. Semua pemeriksaan lolos berarti proyekmu selesai, salah satu syarat sertifikat ReactJS.

Brief

Bangun lapisan logika keranjang belanja untuk toko merchandise kecil berbasis React. Tampilannya (daftar produk, tombol tambah, ringkasan total) nanti dibuat dengan komponen, tapi semua aturan keranjang kamu tulis di sini sebagai fungsi JavaScript murni: reducer untuk useReducer, fungsi turunan state (selector), format rupiah, dan pemuat data produk asynchronous.

Kenapa dipisah? Fungsi murni gampang diuji tanpa browser, dan komponen jadi tipis: cuma memanggil dispatch dan menampilkan hasil selector.

Bentuk state

const initialState = { items: [] };
// setelah beberapa aksi:
{ items: [{ id: "kaos", jumlah: 2 }, { id: "stiker", jumlah: 3 }] }

Keranjang hanya menyimpan id dan jumlah. Harga dan nama tetap di data produk, jadi total dihitung, bukan disimpan.

Fungsi wajib

FungsiTugas
keranjangReducer(state, action)Mengembalikan state baru, tidak pernah mengubah state lama atau object item di dalamnya.
{ type: "tambah", id, jumlah }jumlah opsional, default 1. Item baru ditaruh di akhir items; bila id sudah ada, jumlahnya ditambah.
{ type: "kurangi", id }Jumlah item dikurangi 1. Bila jadi 0, item dibuang dari items.
{ type: "hapus", id }Item dengan id itu dibuang.
{ type: "kosongkan" }items menjadi array kosong.
action lainKembalikan state yang sama persis (referensi yang sama), supaya React tidak render ulang.
totalItem(state)Jumlah seluruh barang, misalnya 2 kaos + 3 stiker = 5.
totalHarga(state, produk, diskon = 0)Jumlah harga x jumlah setiap item. Item yang id-nya tidak ada di produk dilewati. diskon dalam persen, hasil akhir dibulatkan dengan Math.round.
formatRupiah(angka)Pakai Intl.NumberFormat("id-ID", { style: "currency", currency: "IDR", minimumFractionDigits: 0, maximumFractionDigits: 0 }).
async muatProduk(fetcher)await fetcher(). Bila hasilnya array: { status: "sukses", data }. Bila bukan array: { status: "gagal", pesan: "Format data tidak valid" }. Bila fetcher melempar error: { status: "gagal", pesan: error.message }. Tidak boleh melempar error keluar.

Contoh hasil

let s = initialState;
s = keranjangReducer(s, { type: "tambah", id: "kaos" });
s = keranjangReducer(s, { type: "tambah", id: "kaos" });
s = keranjangReducer(s, { type: "tambah", id: "stiker", jumlah: 3 });
// { items: [{ id: "kaos", jumlah: 2 }, { id: "stiker", jumlah: 3 }] }

totalItem(s);                 // 5
totalHarga(s, PRODUK);        // 215000  (2 x 85000 + 3 x 15000)
totalHarga(s, PRODUK, 10);    // 193500
formatRupiah(193500);         // "Rp 193.500" (spasinya non-breaking space dari Intl)

s = keranjangReducer(s, { type: "kurangi", id: "kaos" });  // kaos tinggal 1
s = keranjangReducer(s, { type: "hapus", id: "stiker" });  // stiker hilang
keranjangReducer(s, { type: "lompat" }) === s;             // true

await muatProduk(fakeFetch);   // { status: "sukses", data: [ ...3 produk ] }
await muatProduk(fetchGagal);  // { status: "gagal", pesan: "Jaringan putus" }

Cara komponen memakainya

import { useEffect, useReducer, useState } from "react";

function Toko() {
  const [keranjang, dispatch] = useReducer(keranjangReducer, initialState);
  const [produk, setProduk] = useState({ status: "loading" });

  useEffect(() => {
    muatProduk(() => fetch("/api/produk").then((r) => r.json())).then(setProduk);
  }, []);

  if (produk.status === "loading") return <p>Memuat produk...</p>;
  if (produk.status === "gagal") return <p>Gagal: {produk.pesan}</p>;

  return (
    <>
      {produk.data.map((p) => (
        <button key={p.id} onClick={() => dispatch({ type: "tambah", id: p.id })}>
          + {p.nama}
        </button>
      ))}
      <p>{totalItem(keranjang)} barang, {formatRupiah(totalHarga(keranjang, produk.data))}</p>
    </>
  );
}

Pemeriksa sudah menyiapkan PRODUK (kaos 85000, totebag 45000, stiker 15000), fakeFetch() (API palsu yang berhasil), fetchGagal() (API palsu yang melempar Error("Jaringan putus")), dan contohKeranjang() (2 kaos dan 3 stiker).

Latihan kode: Logika Keranjang Belanja

Tulis lapisan logika keranjang belanja untuk aplikasi React: keranjangReducer yang menangani aksi tambah, kurangi, hapus, dan kosongkan secara immutable (dipakai komponen lewat useReducer dan dispatch), initialState, selector totalItem dan totalHarga dengan diskon persen, formatRupiah dengan Intl.NumberFormat("id-ID"), serta muatProduk yang menunggu fetcher asynchronous dan selalu mengembalikan status sukses atau gagal tanpa melempar error, sehingga komponen tinggal menampilkan loading, pesan error, atau daftar produk.