Bikin fitur keranjang belanja untuk Toko Kue-mu! Pelajari cara mengelola state keranjang, menambah/menghapus item, dan menyimpan data keranjang di localStorage agar tidak hilang. Mungkin juga pengenalan Context API untuk state global.
17m bacaanKuis WajibMicro Badge Lesson
Isi Troli Belanjaanmu! Bikin Keranjang Belanja di Toko Kue Pake React & TypeScript
Udah bisa nampilin daftar kue dan detailnya? Keren! Sekarang, fitur paling penting di toko online apa coba? Ya, Keranjang Belanja! Pengguna harus bisa "masukin" kue yang mereka mau ke keranjang, liat apa aja isinya, dan nanti lanjut ke checkout.
Di bagian ini, kita bakal ngebangun fungsionalitas dasar keranjang belanja:
Nambahin kue ke keranjang dari halaman detail produk (atau halaman daftar).
Nampilin isi keranjang di halaman khusus.
(Opsional dasar) Ngubah jumlah atau ngapus item dari keranjang.
Yang penting, data keranjang ini gak ilang pas halaman di-refresh atau browser ditutup! Kita bakal pake localStorage buat nyimpennya.
Kita juga bakal ngintip gimana React Context API bisa ngebantu nge-share state keranjang ini ke berbagai komponen tanpa perlu prop drilling yang ribet.
Ini bakal jadi bagian yang lumayan banyak ngoprek state dan logika JavaScript (dengan TypeScript tentunya!).
Langkah 1: Mendefinisikan Tipe Data untuk Item Keranjang
Selain interface Kue yang udah kita punya, kita perlu interface buat ngewakilin satu item di dalam keranjang. Item keranjang mungkin butuh properti tambahan kayak jumlah.
Di file src/types/kue.ts (atau file tipe terpisah), tambahkan:
typescript
// src/types/kue.ts (atau src/types.ts)// ... (interface Kue yang sudah ada) ...export interface ItemKeranjang extends Kue { // Mewarisi semua properti dari Kue jumlah: number;}
Jadi, ItemKeranjang punya semua properti Kue plus properti jumlah.
Langkah 2: State Management untuk Keranjang - Pake useState dan useEffect di AppLayout (atau Provider Context)
Data keranjang ini idealnya jadi state global atau state yang ada di level atas aplikasi biar bisa diakses sama banyak komponen (misal, tombol "Tambah ke Keranjang" di halaman produk, ikon jumlah item di header, dan halaman keranjang itu sendiri).
Ada beberapa cara buat ngelola state global di React. Buat contoh ini, kita bisa:
Opsi A (Simpel dulu): Taruh state keranjang di src/app/layout.tsx (RootLayout) atau bikin komponen layout khusus (misal AppLayout.tsx) yang ngebungkus semua halaman dan ngelola state ini, terus dioper pake props. Tapi ini bisa jadi prop drilling kalau komponennya banyak.
Opsi B (Lebih Baik untuk State Global): Pake React Context API. Kita bikin "CartContext" buat nyimpen state keranjang dan fungsi-fungsi buat ngelolanya. Komponen mana aja yang butuh bisa "langganan" ke context ini. Ini pendekatan yang akan kita coba di sini.
Membuat CartContext
Bikin folder baru src/context/.
Di dalam src/context/, bikin file cart-context.tsx.
File src/context/cart-context.tsx:
tsx
// src/context/cart-context.tsx"use client"; // Context Provider yang punya state dan useEffect biasanya butuh "use client"import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';import { Kue, ItemKeranjang } from '@/types/kue'; // Impor tipe kita// Definisikan tipe untuk nilai contextinterface CartContextType { itemDiKeranjang: ItemKeranjang[]; tambahKeKeranjang: (kue: Kue) => void; hapusDariKeranjang:
Bedah cart-context.tsx:
"use client";: Karena Provider ini punya useState dan useEffect (yang berinteraksi dengan localStorage browser), dia harus jadi Client Component.
CartContextType: Definisikan "kontrak" data dan fungsi apa aja yang bakal disediain sama context ini.
createContext<CartContextType>(null!): Bikin context-nya. null! itu cara bilang ke TS, "Tenang, aku janji pas dipake nanti nilainya gak bakal null karena udah dibungkus Provider."
useCart(): Custom hook biar komponen lain tinggal manggil useCart() buat dapet semua dari context, gak perlu useContext(CartContext) lagi.
CartProvider: Ini komponen yang bakal ngebungkus aplikasi kita.
Dia punya state itemDiKeranjang (bertipe ItemKeranjang[]).
Dia pake useEffect buat sinkronisasi state ini sama localStorage (inget JSON.stringify dan JSON.parse!).
Punya fungsi-fungsi buat ngelola keranjang: tambahKeKeranjang, hapusDariKeranjang, updateJumlah, kosongkanKeranjang.
Ngitung totalItem dan totalHarga dari state itemDiKeranjang.
Semua state dan fungsi itu "dikirim" lewat value prop di <CartContext.Provider>.
Menggunakan CartProvider di Root Layout
Sekarang, kita bungkus aplikasi kita pake CartProvider ini biar semua komponen di dalamnya bisa ngakses context keranjang.
File src/app/layout.tsx (RootLayout):
tsx
// src/app/layout.tsximport React from 'react';import './globals.css'; // Atau App.css jika itu style globalmuimport { CartProvider } from '@/context/cart-context'; // Impor Provider kita// import Navbar from '@/components/Navbar'; // Jika kamu punya Navbarexport default function RootLayout({ children,}: { children: React.ReactNode;}) { return ( <html lang="id"> <body> <CartProvider> {/* Bungkus semua dengan CartProvider */} {/* <Navbar /> */} {/* Navbar bisa ditaruh di sini biar bisa akses info keranjang */} <main className="main-content-area"> {/* Kasih class buat styling main content */} {children} </main> {/* Footer bisa di sini juga */} </CartProvider> </body> </html> );}
Dengan begini, semua children (termasuk page.tsx dan komponen-komponen di dalamnya) bisa pake useCart().
Langkah 3: Komponen Tombol "Tambah ke Keranjang"
Kita bikin komponen tombol ini biar bisa dipake di halaman daftar produk dan halaman detail.
File src/components/tombol-tambah-keranjang.tsx:
tsx
// src/components/tombol-tambah-keranjang.tsx"use client"; // Karena dia manggil fungsi dari context dan berinteraksiimport React from 'react';import { useCart } from '@/context/cart-context';import { Kue } from '@/types/kue';interface TombolTambahKeranjangProps { kue: Kue; // Nerima objek kue yang mau ditambah}export default function TombolTambahKeranjang({ kue }: TombolTambahKeranjangProps) { const { tambahKeKeranjang, itemDiKeranjang } = useCart(); const itemSudahDiKeranjang = itemDiKeranjang.find(item => item.id === kue.id); const jumlahDiKeranjang = itemSudahDiKeranjang ? itemSudahDiKeranjang.jumlah : 0; return ( <button onClick={() => tambahKeKeranjang(kue)} className="w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-lg transition-colors text-sm md:text-base" > Tambah ke Keranjang {jumlahDiKeranjang > 0 && ` (${jumlahDiKeranjang})`} </button> );}
Komponen ini pake useCart() buat dapet fungsi tambahKeKeranjang.
Dia juga bisa nampilin jumlah item itu yang udah ada di keranjang.
Gunakan di kartu-kue.tsx dan HalamanDetailKue:
Sekarang, ganti tombol placeholder di kartu-kue.tsx dan src/app/kue/[idKue]/page.tsx dengan komponen ini:
tsx
// Di kartu-kue.tsx (bagian tombolnya)// import TombolTambahKeranjang from './tombol-tambah-keranjang'; // Sesuaikan path// ...// <TombolTambahKeranjang kue={kue} />// Di src/app/kue/[idKue]/page.tsx (bagian tombolnya)// import TombolTambahKeranjang from '@/components/tombol-tambah-keranjang'; // Sesuaikan path// ...// <TombolTambahKeranjang kue={kue} />
Bikin folder src/app/keranjang/ dan file page.tsx di dalamnya.
File src/app/keranjang/page.tsx:
tsx
// src/app/keranjang/page.tsx"use client"; // Karena butuh akses context dan interaksiimport React from 'react';import Link from 'next/link';import Image from 'next/image';import { useCart } from '@/context/cart-context';export default function HalamanKeranjang() { const { itemDiKeranjang, updateJumlah, hapusDariKeranjang, totalItem
Bedah HalamanKeranjang:
Pake useCart() buat dapet semua data dan fungsi keranjang.
Nampilin pesan kalau keranjang kosong.
Kalau ada item, nge-map itemDiKeranjang buat nampilin tiap item: gambar, nama, harga, input buat ngubah jumlah (manggil updateJumlah), dan tombol hapus (manggil hapusDariKeranjang).
Nampilin total harga dan link ke halaman checkout.
Langkah 5: Styling Tambahan (di globals.css atau App.css)
Kamu mungkin perlu nambahin beberapa style global atau spesifik buat halaman keranjang dan komponennya biar tampilannya makin oke. Misalnya:
css
/* src/app/globals.css atau App.css *//* ... (style yang udah ada) ... */.main-content-area { /* class buat <main> di layout.tsx */ padding-top: 20px; /* Kasih jarak dari header/nav (kalau ada) */ padding-bottom: 40px;}/* Bisa tambahin style lain sesuai kebutuhan */
Tes Semuanya!
Jalanin npm run dev dan coba semua fiturnya:
Buka halaman utama, klik tombol "Tambah ke Keranjang" di beberapa kue.
Klik tombol "Tambah ke Keranjang" lagi di kue yang sama, liat jumlahnya nambah di tombol itu.
Pindah ke halaman detail produk, klik tombol "Tambah ke Keranjang".
Buka halaman /keranjang. Harusnya kamu liat semua item yang udah kamu tambahin.
Coba ubah jumlah item di keranjang. Coba set jadi 0, itemnya harusnya ilang.
Coba hapus item dari keranjang.
Refresh halaman keranjang, datanya harusnya masih ada (karena localStorage).
Tutup browser, buka lagi, buka halaman keranjang. Datanya juga harusnya masih ada.
Ini dia implementasi dasar keranjang belanja! Kita udah pake useState buat state lokal (di TodoForm misalnya), useState + useEffect + localStorage buat state keranjang yang persisten di CartProvider, dan useContext (lewat custom hook useCart) buat nge-share state keranjang itu ke komponen-komponen yang butuh.
Ini adalah pola yang sangat umum di aplikasi React. Dengan Context API, kita bisa ngindarin prop drilling buat state yang dipake di banyak tempat.
Di bagian studi kasus terakhir, kita bakal bikin form checkout sederhana dan halaman "terima kasih".
Checkpoint KuisTarget: 70%
Kuis Keranjang Belanja Toko Kue (Next.js+TS)
Kuis ini wajib untuk sertifikat. Kamu tetap bisa lanjut ke pelajaran berikutnya dan mengerjakannya nanti.