Bab 4: JavaScript dalam JSX dengan Kurung Kurawal
⏱ 4 menit bacaJendela ke Dunia JavaScript
Di bab sebelumnya, kamu udah belajar nulis JSX. Sekarang pertanyaannya: gimana caranya bikin JSX itu dinamis? Masa iya semua teks di-hardcode?
Bayangin JSX itu kayak template undangan nikahan. Strukturnya sama (ada nama mempelai, tanggal, tempat), tapi isinya beda-beda tergantung siapa yang nikah. Nah, kurung kurawal {} itu kayak "kolom kosong" di template yang bisa diisi data berbeda.
// Tanpa kurung kurawal - statis, membosankan
function Undangan() {
return <h1>Selamat Datang, Tamu</h1>;
}
// Dengan kurung kurawal - dinamis!
function Undangan() {
const nama = "Pak Budi";
return <h1>Selamat Datang, {nama}</h1>;
}Kurung kurawal {} adalah jendela dari dunia JSX ke dunia JavaScript. Apapun yang kamu taruh di dalam {}, React akan menjalankannya sebagai kode JavaScript dan menampilkan hasilnya.
Di Mana Kurung Kurawal Bisa Dipakai?
Kurung kurawal bisa dipakai di dua tempat dalam JSX:
1. Sebagai Konten (Isi Teks)
function Sapaan() {
const nama = "Siti";
const umur = 25;
return (
<div>
{/* Sebagai teks di antara tag pembuka dan penutup */}
<h1>Halo, {nama}!</h1>
<p>Umur: {umur} tahun</p>
<p>Tahun lahir: {2024 - umur}</p>
</div>
);
}2. Sebagai Nilai Atribut
function Avatar() {
const urlFoto = "https://example.com/foto-siti.jpg";
const altText = "Foto profil Siti";
const ukuran = 150;
return (
<img
src={urlFoto} // Atribut src pakai variabel
alt={altText} // Atribut alt pakai variabel
width={ukuran} // Atribut width pakai variabel
height={ukuran} // Bisa pakai variabel yang sama
className="avatar" // String biasa nggak perlu {}
/>
);
}Perhatiin: Kalau nilai atribut itu string yang udah fix (nggak dinamis), kamu tetap pakai tanda kutip biasa tanpa kurung kurawal. className="avatar" itu oke, nggak perlu className={"avatar"} (meskipun keduanya valid).
Di Mana Kurung Kurawal NGGAK Bisa Dipakai?
// ❌ SALAH - nggak bisa pakai {} untuk nama tag
const tag = "h1";
return <{tag}>Halo</{tag}>; // Error!
// ❌ SALAH - nggak bisa pakai {} untuk nama atribut
const atribut = "className";
return <div {atribut}="box">Halo</div>; // Error!Kurung kurawal cuma bisa di posisi konten (antara tag pembuka dan penutup) dan posisi nilai atribut (setelah tanda =).
Apa yang Bisa Dimasukkan ke Kurung Kurawal?
Aturan emas: Apapun yang merupakan ekspresi JavaScript bisa masuk ke {}.
Apa Itu Ekspresi?
Ekspresi adalah potongan kode yang menghasilkan nilai. Gampangnya: kalau bisa ditaruh di sebelah kanan tanda =, itu ekspresi.
// Semua ini EKSPRESI (menghasilkan nilai):
const a = 5 + 3; // 8
const b = nama.toUpperCase(); // "SITI"
const c = umur >= 18; // true
const d = items.length; // angka
const e = kondisi ? "ya" : "tidak"; // stringContoh Ekspresi yang Valid di JSX:
function ContohEkspresi() {
const nama = "Ahmad";
const items = ["Nasi", "Ayam", "Teh"];
const harga = 25000;
const diskon = 0.1;
return (
<div>
{/* Variabel */}
<p>{nama}</p>
{/* Operasi matematika */}
<p>Total: Rp {harga - (harga * diskon)}</p>
{/* Pemanggilan method */}
<p>{nama.toLowerCase()}</p>
<p>Jumlah item: {items.length}</p>
{/* Ternary operator */}
<p>{harga > 20000 ? 'Mahal' : 'Murah'}</p>
{/* Template literal */}
<p>{`Halo, ${nama}! Kamu punya ${items.length} item.`}</p>
{/* Pemanggilan fungsi */}
<p>{Math.round(harga * diskon)}</p>
<p>{new Date().getFullYear()}</p>
</div>
);
}Apa yang BUKAN Ekspresi (Nggak Bisa Masuk {})?
Statement (pernyataan) nggak bisa masuk ke kurung kurawal. Statement itu instruksi yang nggak menghasilkan nilai.
// ❌ SALAH - ini semua statement, bukan ekspresi
function Salah() {
return (
<div>
{/* if/else BUKAN ekspresi */}
{if (true) { return "halo" }} // Error!
{/* for loop BUKAN ekspresi */}
{for (let i = 0; i < 5; i++) { }} // Error!
{/* deklarasi variabel BUKAN ekspresi */}
{const x = 5} // Error!
{/* switch BUKAN ekspresi */}
{switch(nilai) { case 1: break; }} // Error!
</div>
);
}Cara gampang bedain: Coba taruh di console.log(...). Kalau masuk akal, itu ekspresi. Kalau aneh, itu statement.
console.log(5 + 3); // ✅ Masuk akal → ekspresi
console.log(nama.length); // ✅ Masuk akal → ekspresi
console.log(if (true) {}); // ❌ Aneh → statement
console.log(for (;;) {}); // ❌ Aneh → statementObjek dalam JSX: Kurung Kurawal Ganda {{}}
Ini yang sering bikin bingung pemula. Kadang kamu lihat {{}} di JSX dan mikir "apa ini sintaks khusus?" Bukan. Itu cuma objek JavaScript {} di dalam kurung kurawal JSX {}.
Bayangin kurung kurawal JSX {} itu kayak jendela rumah. Kamu bisa lempar apapun lewat jendela itu. Nah, kalau yang kamu lempar itu sebuah kotak (objek {}), ya jadinya keliatan kayak jendela dengan kotak di dalamnya: {{}}.
// Ini bukan sintaks khusus!
// Ini: { (buka jendela JSX) { color: 'red' } (objek JS) } (tutup jendela JSX)
<div style={{ color: 'red', fontSize: '20px' }}>
Teks merah
</div>Mari kita breakdown:
// Langkah 1: Bikin objek style
const gayaSaya = { color: 'red', fontSize: '20px' };
// Langkah 2: Masukkan ke JSX pakai kurung kurawal
<div style={gayaSaya}>Teks merah</div>
// Atau langsung inline (jadinya kurung kurawal ganda):
<div style={{ color: 'red', fontSize: '20px' }}>Teks merah</div>Inline Style di React
Di HTML biasa, style ditulis sebagai string: style="color: red; font-size: 20px". Di React/JSX, style itu objek JavaScript:
function KotakWarna() {
return (
<div style={{
backgroundColor: 'lightblue', // background-color → backgroundColor
padding: '20px', // nilai string dengan unit
borderRadius: '8px', // border-radius → borderRadius
fontSize: '16px', // font-size → fontSize
fontWeight: 'bold', // font-weight → fontWeight
textAlign: 'center', // text-align → textAlign
width: 200, // angka tanpa unit = pixel
height: 100, // angka tanpa unit = pixel
}}>
Kotak Biru Muda
</div>
);
}Aturan style di JSX:
- Property CSS yang pakai dash (
-) diubah ke camelCase - Nilai biasanya string (dengan unit):
'20px','1rem','50%' - Kalau nilainya pixel, boleh pakai angka tanpa unit:
width: 200=width: '200px'
Style Dinamis
Kekuatan inline style di React: bisa dinamis berdasarkan data!
function BarProgress({ persen }) {
return (
<div style={{
width: '100%',
backgroundColor: '#eee',
borderRadius: '4px'
}}>
<div style={{
width: `${persen}%`, // Dinamis!
backgroundColor: persen > 70 ? 'green' : 'orange', // Dinamis!
height: '20px',
borderRadius: '4px',
transition: 'width 0.3s ease'
}}>
{persen}%
</div>
</div>
);
}Memasukkan Variabel dan Kalkulasi
Variabel Sederhana
function ProfilToko() {
const namaToko = "Warung Makan Bu Sari";
const rating = 4.8;
const jumlahReview = 1250;
const buka = true;
return (
<div className="profil-toko">
<h1>{namaToko}</h1>
<p>⭐ {rating} ({jumlahReview} review)</p>
<p>Status: {buka ? '🟢 Buka' : '🔴 Tutup'}</p>
</div>
);
}Kalkulasi dan Operasi
function RingkasanBelanja() {
const items = [
{ nama: "Nasi Goreng", harga: 15000, qty: 2 },
{ nama: "Es Teh", harga: 5000, qty: 3 },
{ nama: "Kerupuk", harga: 3000, qty: 1 },
];
// Kalkulasi di luar JSX
const subtotal = items.reduce((acc, item) => acc + (item.harga * item.qty), 0);
const pajak = subtotal * 0.1;
const total = subtotal + pajak;
return (
<div className="ringkasan">
<h2>Ringkasan Belanja</h2>
{/* Kalkulasi langsung di JSX juga bisa */}
<p>Jumlah item: {items.length}</p>
<p>Subtotal: Rp {subtotal.toLocaleString('id-ID')}</p>
<p>Pajak (10%): Rp {pajak.toLocaleString('id-ID')}</p>
<hr />
<p><strong>Total: Rp {total.toLocaleString('id-ID')}</strong></p>
{/* Operasi string */}
<p className="catatan">
{items.length > 2
? `Wah, kamu pesan ${items.length} jenis makanan!`
: 'Mau tambah pesanan lagi?'}
</p>
</div>
);
}Pemanggilan Fungsi
// Fungsi helper
function formatRupiah(angka) {
return `Rp ${angka.toLocaleString('id-ID')}`;
}
function formatTanggal(tanggal) {
return tanggal.toLocaleDateString('id-ID', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
function Invoice() {
const tanggalPesan = new Date();
const totalBayar = 157500;
return (
<div className="invoice">
<h2>Invoice Pembayaran</h2>
{/* Panggil fungsi di dalam kurung kurawal */}
<p>Tanggal: {formatTanggal(tanggalPesan)}</p>
<p>Total: {formatRupiah(totalBayar)}</p>
<p>Status: Lunas ✅</p>
</div>
);
}Objek dan Array di JSX
Menampilkan Properti Objek
function KartuMahasiswa() {
const mahasiswa = {
nama: "Rina Wulandari",
nim: "2024001234",
jurusan: "Teknik Informatika",
semester: 5,
ipk: 3.75,
foto: "https://example.com/rina.jpg"
};
return (
<div className="kartu-mahasiswa">
<img src={mahasiswa.foto} alt={`Foto ${mahasiswa.nama}`} />
<h2>{mahasiswa.nama}</h2>
<table>
<tbody>
<tr>
<td>NIM</td>
<td>{mahasiswa.nim}</td>
</tr>
<tr>
<td>Jurusan</td>
<td>{mahasiswa.jurusan}</td>
</tr>
<tr>
<td>Semester</td>
<td>{mahasiswa.semester}</td>
</tr>
<tr>
<td>IPK</td>
<td>{mahasiswa.ipk.toFixed(2)}</td>
</tr>
</tbody>
</table>
</div>
);
}Yang NGGAK Bisa Ditampilkan Langsung
React bisa menampilkan string, number, dan boolean (sebagai string). Tapi objek dan array nggak bisa ditampilkan langsung sebagai konten:
// ❌ Error: Objects are not valid as a React child
function Salah() {
const user = { nama: "Budi", umur: 30 };
return <p>{user}</p>; // Error!
}
// ✅ Benar: akses properti spesifik
function Benar() {
const user = { nama: "Budi", umur: 30 };
return <p>{user.nama}, {user.umur} tahun</p>;
}// Array bisa ditampilkan (React akan join elemennya)
function ContohArray() {
const buah = ["Apel", "Mangga", "Jeruk"];
return (
<div>
{/* Array of strings - React gabung jadi satu */}
<p>{buah}</p> {/* Output: ApelManggaJeruk (tanpa separator) */}
{/* Lebih baik: join dengan separator */}
<p>{buah.join(", ")}</p> {/* Output: Apel, Mangga, Jeruk */}
</div>
);
}Kurung Kurawal Bersarang (Nested)
Kadang kamu perlu kurung kurawal di dalam kurung kurawal. Ini valid dan sering terjadi:
function ProfilLengkap() {
const data = {
nama: "Joko Widodo",
jabatan: "Presiden ke-7",
periode: { mulai: 2014, selesai: 2024 }
};
return (
<div style={{
padding: '20px',
border: `2px solid ${data.periode.selesai <= 2024 ? 'gray' : 'green'}`,
borderRadius: '8px'
}}>
<h2>{data.nama}</h2>
<p>{data.jabatan}</p>
<p>Periode: {data.periode.mulai} - {data.periode.selesai}</p>
<p>Durasi: {data.periode.selesai - data.periode.mulai} tahun</p>
</div>
);
}Pola Umum: Variabel JSX
Kamu bisa menyimpan JSX di variabel, lalu pakai variabel itu di dalam JSX lain:
function HalamanProduk() {
const sedangDiskon = true;
const harga = 500000;
// Simpan JSX di variabel
let badgeDiskon;
if (sedangDiskon) {
badgeDiskon = <span className="badge">🔥 DISKON 20%!</span>;
}
// Konten yang berbeda berdasarkan kondisi
const infoHarga = sedangDiskon
? <p className="harga-diskon">Rp {(harga * 0.8).toLocaleString('id-ID')}</p>
: <p className="harga">Rp {harga.toLocaleString('id-ID')}</p>;
return (
<div className="halaman-produk">
<h1>Tas Ransel Premium</h1>
{badgeDiskon}
{infoHarga}
</div>
);
}Contoh Komprehensif: Dashboard Cuaca
function DashboardCuaca() {
// Data cuaca (biasanya dari API)
const cuaca = {
kota: "Jakarta",
suhu: 32,
kelembaban: 78,
kondisi: "Berawan",
angin: 15,
lastUpdate: new Date(),
};
// Fungsi helper
function getEmoji(kondisi) {
const emojiMap = {
'Cerah': '☀️',
'Berawan': '⛅',
'Hujan': '🌧️',
'Badai': '⛈️',
};
return emojiMap[kondisi] || '🌤️';
}
function getSaranAktivitas(suhu, kondisi) {
if (kondisi === 'Hujan' || kondisi === 'Badai') return 'Bawa payung!';
if (suhu > 35) return 'Minum yang banyak, panas banget!';
if (suhu > 30) return 'Pakai sunscreen kalau keluar rumah.';
return 'Cuaca enak buat jalan-jalan!';
}
return (
<div style={{
padding: '24px',
borderRadius: '12px',
backgroundColor: cuaca.suhu > 30 ? '#fff3e0' : '#e3f2fd',
maxWidth: '400px',
fontFamily: 'sans-serif'
}}>
{/* Header */}
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<h2 style={{ margin: 0 }}>📍 {cuaca.kota}</h2>
<span style={{ fontSize: '2rem' }}>{getEmoji(cuaca.kondisi)}</span>
</div>
{/* Suhu utama */}
<p style={{ fontSize: '3rem', margin: '10px 0', fontWeight: 'bold' }}>
{cuaca.suhu}°C
</p>
{/* Detail */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
<p>💨 Angin: {cuaca.angin} km/h</p>
<p>💧 Kelembaban: {cuaca.kelembaban}%</p>
<p>🌡️ Kondisi: {cuaca.kondisi}</p>
<p>🕐 Update: {cuaca.lastUpdate.toLocaleTimeString('id-ID')}</p>
</div>
{/* Saran */}
<p style={{
marginTop: '16px',
padding: '12px',
backgroundColor: 'rgba(255,255,255,0.7)',
borderRadius: '8px',
fontStyle: 'italic'
}}>
💡 {getSaranAktivitas(cuaca.suhu, cuaca.kondisi)}
</p>
</div>
);
}⚠️ Jebakan
Jebakan 1: Lupa Kurung Kurawal
// ❌ Ini menampilkan teks literal "nama", bukan isi variabel
const nama = "Budi";
<h1>Halo, nama</h1> // Output: "Halo, nama"
// ✅ Pakai kurung kurawal untuk akses variabel
<h1>Halo, {nama}</h1> // Output: "Halo, Budi"Jebakan 2: Pakai Kutip di Dalam Kurung Kurawal untuk Atribut
// ❌ Ini menampilkan string literal "{urlFoto}", bukan isi variabel
const urlFoto = "foto.jpg";
<img src="{urlFoto}" /> // src akan jadi string "{urlFoto}" 😱
// ✅ Pilih salah satu: kutip ATAU kurung kurawal
<img src={urlFoto} /> // Dinamis - pakai variabel
<img src="foto.jpg" /> // Statis - langsung stringJebakan 3: Menampilkan Objek Langsung
// ❌ Error: Objects are not valid as a React child
const user = { nama: "Siti", kota: "Bandung" };
<p>{user}</p>
// ✅ Akses properti spesifik
<p>{user.nama} dari {user.kota}</p>
// ✅ Atau convert ke string dulu (untuk debugging)
<p>{JSON.stringify(user)}</p>Jebakan 4: Statement di Dalam Kurung Kurawal
// ❌ if/else bukan ekspresi!
<p>{if (skor > 80) { "Bagus" } else { "Kurang" }}</p>
// ✅ Pakai ternary operator (ini ekspresi)
<p>{skor > 80 ? "Bagus" : "Kurang"}</p>
// ✅ Atau hitung di luar JSX
const penilaian = skor > 80 ? "Bagus" : "Kurang";
<p>{penilaian}</p>Jebakan 5: Bingung {} vs {{}}
// ❌ Salah - style butuh objek, bukan string
<div style="color: red">Halo</div>
// ❌ Salah - cuma satu kurung kurawal, isinya bukan objek
<div style={color: 'red'}>Halo</div>
// ✅ Benar - kurung kurawal JSX + objek JavaScript
<div style={{ color: 'red' }}>Halo</div>
// Breakdown:
// { → buka jendela JSX
// { → buka objek JavaScript
// color: 'red' → properti objek
// } → tutup objek JavaScript
// } → tutup jendela JSXJebakan 6: Boolean, Null, dan Undefined
function ContohBoolean() {
const tampilkan = true;
const kosong = null;
const belumAda = undefined;
return (
<div>
{/* Boolean, null, undefined TIDAK ditampilkan (invisible) */}
<p>{tampilkan}</p> {/* Nggak ada output */}
<p>{kosong}</p> {/* Nggak ada output */}
<p>{belumAda}</p> {/* Nggak ada output */}
{/* Kalau mau tampilkan sebagai teks, convert ke string */}
<p>{String(tampilkan)}</p> {/* Output: "true" */}
<p>{`${kosong}`}</p> {/* Output: "null" */}
{/* TAPI angka 0 DITAMPILKAN! Ini sering jadi bug */}
<p>{0}</p> {/* Output: "0" */}
</div>
);
}Ringkasan
| Konsep | Contoh | Penjelasan |
|---|---|---|
| Variabel | {nama} | Tampilkan isi variabel |
| Kalkulasi | {harga * 1.1} | Hitung dan tampilkan hasil |
| Method | {teks.toUpperCase()} | Panggil method, tampilkan hasil |
| Fungsi | {formatRupiah(harga)} | Panggil fungsi, tampilkan hasil |
| Ternary | {x ? 'ya' : 'tidak'} | Kondisional inline |
| Objek (style) | style={{ color: 'red' }} | Objek di dalam kurung kurawal |
| Template literal | {`Halo ${nama}`} | String interpolation |
🏋️ Challenge
Challenge 1: Kartu Cuaca Dinamis
Buat komponen KartuCuaca yang menampilkan:
- Nama kota dari variabel
- Suhu dalam Celsius DAN Fahrenheit (hitung konversinya: F = C × 9/5 + 32)
- Emoji yang berubah berdasarkan suhu (< 20: 🥶, 20-30: 😊, > 30: 🥵)
- Background color yang berubah berdasarkan suhu (dingin: biru, sedang: hijau, panas: merah)
💡 Hint
- Rumus Fahrenheit:
(celsius * 9/5) + 32 - Pakai ternary bertingkat atau fungsi helper untuk emoji
- Style dinamis:
style={{ backgroundColor: suhu > 30 ? '#ffcdd2' : '#c8e6c9' }} toFixed(1)untuk membatasi desimal
✅ Solusi
function KartuCuaca() {
const kota = "Surabaya";
const suhuCelsius = 33;
// Kalkulasi
const suhuFahrenheit = (suhuCelsius * 9/5) + 32;
// Fungsi helper
function getEmoji(suhu) {
if (suhu < 20) return '🥶';
if (suhu <= 30) return '😊';
return '🥵';
}
function getWarna(suhu) {
if (suhu < 20) return '#bbdefb';
if (suhu <= 30) return '#c8e6c9';
return '#ffcdd2';
}
return (
<div style={{
padding: '20px',
borderRadius: '12px',
backgroundColor: getWarna(suhuCelsius),
textAlign: 'center',
maxWidth: '300px'
}}>
<h2>📍 {kota}</h2>
<p style={{ fontSize: '3rem', margin: '10px 0' }}>
{getEmoji(suhuCelsius)}
</p>
<p style={{ fontSize: '2rem', fontWeight: 'bold' }}>
{suhuCelsius}°C
</p>
<p style={{ color: '#666' }}>
({suhuFahrenheit.toFixed(1)}°F)
</p>
<p>
{suhuCelsius > 30
? 'Panas banget! Minum yang banyak ya.'
: suhuCelsius < 20
? 'Dingin! Pakai jaket.'
: 'Cuaca enak nih!'}
</p>
</div>
);
}Challenge 2: Kalkulator Diskon
Buat komponen KalkulatorDiskon yang:
- Punya variabel
hargaAslidanpersenDiskon - Menampilkan harga asli (dicoret), harga setelah diskon, dan berapa yang dihemat
- Format semua harga dalam Rupiah
- Tampilkan badge "HEMAT!" kalau diskon lebih dari 20%
- Warna harga diskon: hijau kalau diskon > 30%, oranye kalau 10-30%, hitam kalau < 10%
💡 Hint
- Harga diskon:
hargaAsli - (hargaAsli * persenDiskon / 100) - Hemat:
hargaAsli * persenDiskon / 100 toLocaleString('id-ID')untuk format RupiahtextDecoration: 'line-through'untuk coret harga- Ternary bertingkat untuk warna
✅ Solusi
function KalkulatorDiskon() {
const hargaAsli = 750000;
const persenDiskon = 25;
// Kalkulasi
const potongan = hargaAsli * persenDiskon / 100;
const hargaFinal = hargaAsli - potongan;
// Fungsi helper format Rupiah
function formatRp(angka) {
return `Rp ${angka.toLocaleString('id-ID')}`;
}
// Tentukan warna berdasarkan diskon
function getWarnaDiskon(persen) {
if (persen > 30) return 'green';
if (persen >= 10) return 'orange';
return 'black';
}
return (
<div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
<h2>Kalkulator Diskon</h2>
{/* Harga asli dicoret */}
<p style={{ textDecoration: 'line-through', color: '#999' }}>
{formatRp(hargaAsli)}
</p>
{/* Harga setelah diskon */}
<p style={{
fontSize: '1.5rem',
fontWeight: 'bold',
color: getWarnaDiskon(persenDiskon)
}}>
{formatRp(hargaFinal)}
</p>
{/* Info hemat */}
<p>Kamu hemat: {formatRp(potongan)} ({persenDiskon}%)</p>
{/* Badge kalau diskon > 20% */}
{persenDiskon > 20 ? (
<span style={{
backgroundColor: 'red',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '0.8rem'
}}>
🔥 HEMAT!
</span>
) : null}
</div>
);
}Challenge 3: Jam Digital
Buat komponen JamDigital yang menampilkan:
- Waktu sekarang (jam:menit:detik) dari
new Date() - Tanggal lengkap dalam Bahasa Indonesia
- Sapaan yang berubah berdasarkan jam (Pagi/Siang/Sore/Malam)
- Background gelap kalau malam (jam 18-06), terang kalau siang
💡 Hint
new Date().getHours(),.getMinutes(),.getSeconds().toLocaleDateString('id-ID', { weekday: 'long', ... })untuk tanggal Indonesia.toString().padStart(2, '0')untuk format 2 digit (01, 02, dst)- Pagi: 5-11, Siang: 11-15, Sore: 15-18, Malam: 18-5
✅ Solusi
function JamDigital() {
const sekarang = new Date();
const jam = sekarang.getHours();
const menit = sekarang.getMinutes();
const detik = sekarang.getSeconds();
// Format 2 digit
const jamStr = jam.toString().padStart(2, '0');
const menitStr = menit.toString().padStart(2, '0');
const detikStr = detik.toString().padStart(2, '0');
// Tanggal lengkap
const tanggal = sekarang.toLocaleDateString('id-ID', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
// Sapaan berdasarkan jam
function getSapaan(j) {
if (j >= 5 && j < 11) return 'Selamat Pagi 🌅';
if (j >= 11 && j < 15) return 'Selamat Siang ☀️';
if (j >= 15 && j < 18) return 'Selamat Sore 🌇';
return 'Selamat Malam 🌙';
}
// Cek apakah malam (untuk tema gelap)
const isMalam = jam >= 18 || jam < 6;
return (
<div style={{
padding: '30px',
borderRadius: '16px',
backgroundColor: isMalam ? '#1a1a2e' : '#f0f8ff',
color: isMalam ? '#eee' : '#333',
textAlign: 'center',
fontFamily: 'monospace',
maxWidth: '350px'
}}>
<p style={{ fontSize: '1.2rem', marginBottom: '8px' }}>
{getSapaan(jam)}
</p>
<p style={{ fontSize: '3rem', fontWeight: 'bold', margin: '10px 0' }}>
{jamStr}:{menitStr}:{detikStr}
</p>
<p style={{ fontSize: '1rem', color: isMalam ? '#aaa' : '#666' }}>
{tanggal}
</p>
</div>
);
}Sudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.