Dasar-dasar React JS 19: Mengenal Component sebagai Pondasi Utama

Daftar Isi:

  • Bab 1: Pengantar React JS 19 dan Pentingnya Komponen
    • Apa itu React JS 19
    • Mengapa React JS Begitu Populer
    • Konsep "Component-Based Architecture" Dalam Pengembangan Web
    • Memulai Proyek React JS 19 Pertama Anda
  • Bab 2: Memahami Component: Blok Bangunan Utama React
    • Definisi dan Fungsi Component
    • Perbedaan antara Class Component dan Functional Component (dengan Hooks)
    • Class Component
    • Functional Component (dengan Hooks)
    • Kapan Menggunakan Class Component vs. Functional Component
    • Struktur Dasar Component: JSX dan Rendering
    • JSX (JavaScript XML)
    • Rendering Component
  • Bab 3: Jenis-jenis Component dalam React
    • Functional Component: Penggunaan dan Keunggulan
    • Mengenal Hooks: useState, useEffect, dan Lainnya
    • Studi Kasus: Membuat Component Sederhana dengan Functional Component
    • Class Component: Konsep Dasar dan Lifecycle Methods
    • Lifecycle Methods (Metode Siklus Hidup)
      • ⭐ Mounting (Saat Komponen Dibuat/Muncul)
      • ⭐ Updating (Saat Komponen Diperbarui)
      • ⭐ Unmounting (Saat Komponen Dihapus)
      • Studi Kasus: Membuat Component Sederhana dengan Class Component
  • Bab 4: Props Mengirim Data ke Komponen
    • Apa Itu Props dan Bagaimana Cara Kerja?
    • Meneruskan Props ke Komponen
    • Validasi Props dengan PropTypes
    • Children Props: Mengirim Konten Tambahan
  • Bab 5: State: Mengelola Data Internal Component
    • Apa Itu State dan Perbedaannya dengan Props?
    • Menggunakan State dalam Functional Component (dengan useState)
    • Menggunakan State dalam Class Component (this.state dan this.setState)
    • Pentingnya Immutability saat Mengupdate State
  • Bab 6: Lifecycle Component (untuk Class Component) dan Efek Samping (untuk Functional Component)
    • Class Component Lifecycle
      1. Mounting (Saat Komponen Dibuat dan Muncul di DOM)
    • Functional Component dengan Hooks Mengenai useEffect untuk Efek Samping
    • Konsep useEffect
    • Mengenal useEffect untuk Efek Samping
    • Membersihkan Efek Samping (cleanup function)
  • Bab 7: Styling Component di React
    • Inline Styling
    • CSS Modules
    • CSS-in-JS (Contoh: Styled Components)
    • Menggunakan Preprocessor CSS (Sass/Less)
  • Bab 8: Praktik Terbaik dalam Menggunakan Component
    • Membuat Component yang Reusable dan Modular
    • Prinsip Single Responsibility (SRP) untuk Component
    • Folder Structure untuk Proyek React Skala Besar
    • Debugging Component di React Developer Tools
  • Bab 9: Studi Kasus: Membangun Aplikasi Sederhana dengan Komponen
    • Perencanaan Struktur Component untuk Aplikasi
    • Langkah-langkah Membangun Aplikasi Interaktif Sederhana
    • Penerapan Props dan State dalam Aplikasi Nyata
  • Bab 10: Studi Kasus dan BuildWithNgga
    • ⭐ Belajar dari Real Project, Bukan Teori Doang
    • ⭐ Visual Interaktif & UI Keren
    • ⭐ Materi Up-to-date & Fokus ke Industri
    • ⭐ Penjelasan Simpel, Bahasa Indonesia
    • ⭐ Mentoring & Sertifikat Karier
  • Bab 11: Kesimpulan dan Langkah Selanjutnya
    • Merangkum Konsep Kunci Component
    • Sumber Belajar Lanjutan untuk React JS

Bab 1: Pengantar React JS 19 dan Pentingnya Komponen

Halo, para pembangun web! Selamat datang di dunia React JS yang seru! Di bab pertama ini, kita akan ngobrol santai tentang apa itu React JS, kenapa banyak banget developer yang jatuh cinta sama framework ini, dan bagaimana konsep "komponen" ini menjadi jantungnya dalam pengembangan web modern. Siap? Yuk, kita mulai!

Apa itu React JS 19

Nah, mungkin kamu sering dengar nama "React" di mana-mana, kan? React (sering juga disebut React.js atau ReactJS) itu sebenarnya adalah library JavaScript open-source yang dikembangkan oleh Meta (dulu Facebook). Tujuannya sederhana tapi revolusioner: bikin UI (User Interface) aplikasi jadi lebih interaktif, responsif, dan gampang dikelola.

Bayangin kamu lagi bikin rumah, nah React ini semacam "kit bangunan" yang super canggih. Dia itu fokus banget di bagian tampilan depan aplikasi kamu, jadi kamu bisa menciptakan pengalaman pengguna yang mulus dan memukau. Dengan React 19, ada banyak pembaruan dan penyempurnaan yang bikin ngoding makin asyik dan performa aplikasi makin ngebut. Intinya, React itu tool sakti buat kamu yang pengen bikin UI keren tanpa ribet.

Mengapa React JS Begitu Populer

Ada banyak alasan kenapa React ini jadi bintang di kalangan developer. Yuk, kita bedah satu per satu:

  • Mudah Dipelajari dan Digunakan: Kalau kamu udah akrab sama JavaScript, HTML, dan CSS, belajar React itu ibarat "naik 1 kelas dekat dengan mereka" aja. Sintaksnya bersih dan konsepnya intuitif, jadi kamu bisa cepat bikin aplikasi fungsional dalam hitungan hari. Nggak perlu pusing mikirin konfigurasi yang rumit, React udah siapkan semuanya biar kamu bisa langsung fokus ngoding.
  • Performa Ngebut dengan Virtual DOM: Ini nih salah satu senjata rahasia React! Ketika ada perubahan di UI, React nggak langsung "rombak total" seluruh halaman web. Dia punya "Virtual DOM" (tiruan DOM asli di memori) yang super efisien. React cuma akan membandingkan perubahan di Virtual DOM dan hanya memperbarui bagian yang perlu diubah di DOM asli. Hasilnya? Aplikasi kamu jadi enteng dan responsif banget!
  • Pendekatan Deklaratif: Daripada "ngasih tau" komputer gimana cara mengubah sesuatu (imperatif), di React kamu cukup "ngasih tau" apa yang kamu mau. Kamu tinggal desain tampilan untuk setiap kondisi di aplikasi kamu, nanti React yang akan ngurusin gimana caranya dia update dan render komponennya secara efisien. Kode jadi lebih mudah diprediksi dan gampang dicari kalau ada bug.
  • Komunitas yang Solid dan Ekosistem Kaya: React punya komunitas developer yang GEDE banget di seluruh dunia. Artinya, kalau kamu nemu masalah, pasti ada banyak yang siap bantu. Plus, ekosistemnya juga kaya banget dengan berbagai library dan tool pendukung yang bisa kamu pakai untuk berbagai kebutuhan.

Konsep "Component-Based Architecture" dalam Pengembangan Web

Inilah inti dari React: Komponen! Bayangkan kamu lagi merakit mainan Lego. Setiap balok Lego adalah sebuah "komponen". Kamu bisa pakai balok-balok kecil itu (misalnya, balok satu tombol, balok gambar profil, atau balok input teks) untuk membangun struktur yang lebih besar dan kompleks (seperti formulir pendaftaran, header situs web, atau bahkan seluruh halaman).

Dalam React, component-based architecture artinya kita memecah UI aplikasi menjadi bagian-bagian kecil yang mandiri dan bisa digunakan berulang kali. Setiap komponen punya tanggung jawabnya masing-masing. Misalnya, ada komponen <Button>, komponen <UserProfileCard>, atau komponen <CommentSection>.

Manfaatnya apa? Banyak banget!

  • Reusability (Bisa Dipakai Berulang): Bikin satu komponen <Button>, lalu pakai di mana aja kamu butuh tombol. Hemat waktu dan tenaga kan?
  • Modularity (Lebih Terstruktur): Kode jadi lebih rapi dan gampang dipahami. Kalau ada masalah di satu bagian, kamu tahu harus cari di komponen mana.
  • Maintainability (Gampang Dirawat): Kalau ada perubahan di satu fitur, kamu cuma perlu fokus di komponen itu aja, nggak perlu utak-atik seluruh kode aplikasi.
  • Scalability (Mudah Dikembangkan): Aplikasi kamu bisa tumbuh besar tanpa jadi berantakan, karena semua udah terorganisir dalam komponen-komponen.

Memulai Proyek React JS 19 Pertama Anda

Oke, setelah kita tahu kenapa React itu keren dan konsep komponen itu penting, pasti kamu udah nggak sabar pengen langsung ngoding, kan? Untuk memulai proyek React JS 19, cara yang paling direkomendasikan adalah menggunakan framework berbasis React. Kenapa? Karena framework ini udah menyiapkan semua "perkakas" yang kamu butuhkan agar bisa langsung fokus bikin aplikasi.

Beberapa opsi populer untuk memulai proyek React adalah dengan menggunakan:

  • Next.js

Ini paling sering direkomendasikan untuk aplikasi web modern. Next.js menyediakan fitur-fitur seperti server-side rendering (SSR) dan static site generation (SSG) yang bikin aplikasi kamu makin performa dan SEO-friendly.

  • Vite

Kalau kamu mau yang super cepat untuk memulai proyek dan fokus ke client-side rendering, Vite adalah pilihan yang sangat bagus. Ringan dan ngebut!

  • Create React App (CRA)

Meskipun ada alternatif lain, CRA masih jadi pilihan solid untuk kamu yang baru mulai dan pengen setup project React yang simpel tanpa banyak konfigurasi.

Biasanya, kamu cuma perlu beberapa perintah di terminal untuk bikin proyek baru, lalu semua folder dan file dasar akan otomatis terbentuk. Dari situ, kamu bisa langsung mulai membuat komponen pertamamu dan melihat hasilnya di browser.

Bab 2: Memahami Component: Blok Bangunan Utama React

Oke, setelah di Bab 1 kita kenalan sama React dan kenapa komponen itu penting, sekarang saatnya kita selami lebih dalam tentang sang bintang utama: Component itu sendiri! Ini adalah jantungnya React, jadi pahami baik-baik, ya.

Definisi dan Fungsi Component

Jadi, apa sih sebenarnya "Component" itu? Di dunia React, komponen adalah potongan kode JavaScript yang mandiri (independent), bisa dipakai berulang (reusable), dan bertugas untuk me-render atau menampilkan bagian tertentu dari antarmuka pengguna (UI) aplikasi kita. Gampangnya, komponen itu kayak modul Lego yang udah kita bahas di bab sebelumnya. Setiap komponen bertanggung jawab atas tampilan dan logika dari bagian UI yang spesifik.

Fungsinya? Banyak banget!

  1. Membangun UI yang Kompleks: Kita bisa memecah UI yang besar dan kompleks menjadi komponen-komponen kecil yang lebih mudah dikelola. Misalnya, halaman Facebook bisa dipecah jadi komponen Header, Sidebar, NewsFeed, dan Post.
  2. Meningkatkan Reusability: Begitu kita bikin satu komponen, misalnya tombol (Button), kita bisa pakai tombol itu di mana saja dalam aplikasi kita tanpa harus menulis ulang kodenya. Ini bikin pengembangan jadi lebih cepat dan konsisten.
  3. Memudahkan Maintenance: Kalau ada bug atau perubahan yang perlu dilakukan, kita cuma perlu fokus pada komponen yang bersangkutan, bukan seluruh aplikasi. Jadi lebih gampang mencari dan memperbaiki masalah.
  4. Meningkatkan Keterbacaan Kode: Kode jadi lebih terstruktur dan mudah dipahami karena setiap komponen punya tugasnya masing-masing.

Perbedaan antara Class Component dan Functional Component (dengan Hooks)

Di awal kemunculannya, React hanya punya Class Component. Tapi seiring berjalannya waktu dan munculnya fitur Hooks, Functional Component jadi makin powerful dan populer. Yuk, kita lihat bedanya:

Class Component

Sebelum ada Hooks, Class Component adalah cara utama untuk membuat komponen di React yang punya state (data internal yang bisa berubah) dan lifecycle methods (fungsi yang dijalankan pada tahapan tertentu di kehidupan komponen, seperti saat komponen pertama kali muncul di layar).

  • Sintaks: Menggunakan sintaks kelas JavaScript (class MyComponent extends React.Component { ... }).
  • State: Mengelola state internal menggunakan this.state dan this.setState().
  • Lifecycle Methods: Menggunakan metode seperti componentDidMount(), componentDidUpdate(), dan componentWillUnmount() untuk menangani efek samping atau interaksi dengan siklus hidup komponen.
  • Contoh Sederhana:
import React from 'react';

class Greeting extends React.Component {
  render() {
    return <h1>Halo, {this.props.name}!</h1>;
  }
}
// Penggunaan: <Greeting name="Dunia" />

Functional Component (dengan Hooks)

Awalnya, Functional Component hanya bisa digunakan untuk komponen yang sederhana dan tidak membutuhkan state atau lifecycle methods (sering disebut stateless atau presentational components). Namun, sejak diperkenalkannya Hooks di React 16.8, Functional Component jadi jauh lebih kuat. Sekarang, mereka bisa punya state dan melakukan efek samping, sama seperti Class Component, tapi dengan sintaks yang lebih ringkas dan modern.

  • Sintaks: Hanya menggunakan fungsi JavaScript biasa (function MyComponent() { ... } atau const MyComponent = () => { ... }).
  • State: Mengelola state internal menggunakan Hook seperti useState().
  • Efek Samping (Lifecycle): Menangani efek samping menggunakan Hook useEffect(), yang bisa menggantikan banyak fungsi dari lifecycle methods di Class Component.
  • Contoh Sederhana (dan Modern):
import React from 'react';

function Greeting(props) { // Atau ({ name }) untuk destructuring
  return <h1>Halo, {props.name}!</h1>;
}
// Penggunaan: <Greeting name="Dunia" />

Dan untuk yang punya state dengan Hook useState:

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // count adalah state, setCount fungsinya

  return (
    <div>
      <p>Kamu klik sebanyak {count} kali</p>
      <button onClick={() => setCount(count + 1)}>
        Klik Saya
      </button>
    </div>
  );
}
// Penggunaan: <Counter />

Kapan Menggunakan Class Component vs. Functional Component

Dengan adanya Hooks, saat ini hampir selalu direkomendasikan untuk menggunakan Functional Component untuk pengembangan React yang baru. Mengapa?

  • Lebih Ringkas dan Mudah Dibaca: Kode Functional Component (terutama dengan Hooks) cenderung lebih pendek dan mudah dipahami.
  • Lebih Fleksibel: Hooks memungkinkan kita untuk menggunakan fitur state dan lifecycle dalam fungsi, yang membuat logika lebih mudah diorganisir dan di-reuse.
  • Performa Potensial: Meskipun perbedaannya minor, Functional Component secara internal bisa lebih dioptimalkan oleh React.
  • Tren Industri: Sebagian besar materi pembelajaran dan proyek baru di komunitas React saat ini sudah beralih ke Functional Component dengan Hooks.

Jadi, apakah Class Component sudah ketinggalan zaman? Tidak sepenuhnya. Kamu mungkin masih akan menemukannya di proyek lama atau legacy code. Penting untuk bisa membacanya, tapi untuk proyek baru, fokuslah pada Functional Component.

Struktur Dasar Component: JSX dan Rendering

Bagaimana sih bentuk dasar dari sebuah komponen di React? Jawabannya ada pada JSX dan bagaimana React melakukan rendering.

JSX (JavaScript XML)

Ini adalah sintaks ajaib yang bikin ngoding React jadi unik dan menyenangkan! JSX memungkinkan kita menulis kode yang terlihat seperti HTML di dalam file JavaScript. Sebenarnya, itu bukan HTML murni, tapi ekstensi sintaks JavaScript yang kemudian akan "ditranspilasi" (diubah) menjadi panggilan fungsi JavaScript oleh tool seperti Babel.

  • Kenapa pakai JSX?
    • Intuitif: Lebih mudah visualisasi struktur UI karena mirip HTML.
    • Deklaratif: Kamu bisa mendefinisikan tampilan UI dan bagaimana data mempengaruhi tampilan tersebut dalam satu tempat.
    • Power of JavaScript: Kamu bisa memasukkan ekspresi JavaScript langsung ke dalam JSX menggunakan kurung kurawal {}.
  • Contoh JSX:
function WelcomeMessage() {
  const username = "Tegar BuildWithAngga";
  return (
    <div>
      <h1>Selamat Datang, {username}!</h1> {/* JavaScript expression */}
      <p>Ini adalah komponen pertamamu di React!</p>
      <button>Klik Saya</button>
    </div>
  );
}

Perhatikan bahwa kita harus membungkus semua elemen JSX dalam satu elemen parent (dalam contoh ini <div>) karena sebuah komponen harus me-return hanya satu elemen root.

Rendering Component

Setelah kita punya komponen dengan JSX-nya, bagaimana caranya komponen itu bisa muncul di layar browser? Di sinilah proses rendering berperan.

Secara sederhana, rendering adalah proses React mengubah kode komponen (termasuk JSX) menjadi elemen-elemen DOM (Document Object Model) yang bisa ditampilkan oleh browser.

  • Langkah-langkah umumnya:
    1. Kode React: Kamu menulis komponen dengan JSX.
    2. Transpilasi (oleh Babel): JSX diubah menjadi panggilan React.createElement() JavaScript.
    3. Virtual DOM: React membuat representasi "virtual" dari UI di memori (Virtual DOM).
    4. Diffing Algorithm: Ketika ada perubahan state atau props pada komponen, React membandingkan Virtual DOM yang baru dengan Virtual DOM sebelumnya untuk mengetahui bagian mana yang berubah.
    5. Reconciliation: Hanya perubahan yang terdeteksi itu yang kemudian diaplikasikan ke DOM asli browser. Proses ini sangat efisien, makanya React terasa cepat!

Di aplikasi React, biasanya ada satu titik "root" di file HTML utama (index.html) tempat seluruh aplikasi React akan di-mount atau dirender.

<!DOCTYPE **html**>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Aplikasi React Pertamaku</title>
</head>
<body>
    <div id="root"></div> <script src="index.js"></script> </body>
</html>

Dan di file JavaScript utama (misalnya index.js), kita akan menggunakan ReactDOM.createRoot() untuk "me-render" komponen utama kita ke dalam elemen #root di HTML:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'; // Ini adalah komponen utama aplikasi kita

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Bagaimana, sudah mulai kebayang kan betapa powerful-nya komponen di React? Di bab selanjutnya, kita akan mulai praktek bikin komponen-komponen ini!

Bab 3: Jenis-jenis Component dalam React

Setelah kita memahami apa itu komponen dan mengapa mereka penting, sekarang saatnya kita kenalan lebih dekat dengan dua "ras" utama komponen di React: Functional Component dan Class Component. Kita akan lihat bagaimana mereka bekerja, apa keunggulan masing-masing, dan kapan menggunakan salah satunya, khususnya dengan kehadiran Hooks yang mengubah banyak hal!

Functional Component: Penggunaan dan Keunggulan

Ini adalah "bintang" utama di React modern. Functional Component adalah cara paling direkomendasikan untuk menulis komponen di React saat ini.

Penggunaan:

Functional Component, seperti namanya, hanyalah sebuah fungsi JavaScript biasa yang menerima props (properti) sebagai argumennya dan mengembalikan elemen React (biasanya JSX) yang menjelaskan apa yang harus ditampilkan di UI. Awalnya mereka "stateless" (tanpa state), tapi itu semua berubah total sejak adanya Hooks.

Keunggulan:

  • Sintaks Lebih Ringkas: Lebih sedikit boilerplate code dibandingkan Class Component.
  • Lebih Mudah Dibaca: Logika seringkali lebih mudah diikuti karena tidak terikat pada this atau banyak lifecycle methods.
  • Fleksibilitas Tinggi dengan Hooks: Bisa punya state dan menangani efek samping, membuat mereka sama powerful-nya dengan Class Component, bahkan lebih baik dalam beberapa kasus.
  • Performa Potensial: React bisa melakukan optimasi lebih baik pada Functional Component.

Mengenal Hooks: useState, useEffect, dan Lainnya

Inilah yang membuat Functional Component jadi super kuat! Hooks adalah fungsi spesial yang memungkinkan kita "mengaitkan" (hook into) fitur React seperti state dan lifecycle dari Functional Component. Intinya, Hooks membawa kekuatan Class Component ke dalam dunia fungsi.

  • useState (Untuk Mengelola State):
import React, { useState } from 'react';

function Counter() {
  // Deklarasi state "count" dengan nilai awal 0
  // `count` adalah nilai state saat ini
  // `setCount` adalah fungsi untuk mengubah nilai state `count`
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Kamu menekan tombol sebanyak {count} kali</p>
      {/* Saat tombol diklik, panggil setCount untuk memperbarui state */}
      <button onClick={() => setCount(count + 1)}>
        Tambah
      </button>
    </div>
  );
}

Ini adalah Hook paling dasar dan sering kamu gunakan. useState memungkinkan Functional Component memiliki state internal, yaitu data yang bisa berubah seiring waktu dan memicu render ulang komponen.

Setiap kali setCount dipanggil, komponen Counter akan di-render ulang dengan nilai count yang baru.

  • useEffect (Untuk Efek Samping/Lifecycle):

useEffect adalah Hook yang memungkinkan kita melakukan "efek samping" dalam Functional Component. Efek samping ini bisa berupa fetching data dari API, memanipulasi DOM secara langsung, setting up event listeners, atau operasi lain yang terjadi di luar proses rendering. useEffect bisa menggantikan banyak lifecycle methods dari Class Component.

import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  // Ini adalah fungsi efek samping
  useEffect(() => {
    // Efek samping: mengatur interval setiap 1 detik
    const intervalId = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

    // Fungsi cleanup: akan dijalankan saat komponen unmount
    // Penting untuk membersihkan efek samping (contoh: hapus interval)
    return () => clearInterval(intervalId);
  }, []); // Array dependensi kosong berarti efek hanya dijalankan sekali (mirip componentDidMount)

  return (
    <p>Waktu berjalan: {seconds} detik</p>
  );
}

Parameter kedua dari useEffect (array kosong []) sangat penting. Ini adalah array dependensi. Jika array-nya kosong, efek hanya akan dijalankan sekali setelah render pertama (mirip componentDidMount). Jika ada nilai di dalamnya (misal [someVariable]), efek akan dijalankan setiap kali someVariable berubah.

  • Hooks Lainnya (Sekilas):
    • useContext: Untuk mengakses data dari React Context API.
    • useRef: Untuk mengakses elemen DOM secara langsung atau nilai yang tidak memicu render ulang.
    • useReducer: Alternatif useState untuk state yang lebih kompleks.
    • useCallback, useMemo: Untuk optimasi performa.

Studi Kasus: Membuat Component Sederhana dengan Functional Component

Mari kita praktik membuat komponen ProductCard yang akan menampilkan informasi produk.

import React from 'react';

// Functional Component untuk menampilkan kartu produk
function ProductCard({ name, price, imageUrl, description }) {
  // Kita bisa menambahkan logika di sini jika diperlukan,
  // misalnya untuk memformat harga atau menampilkan stok.

  return (
    <div style={{
      border: '1px solid #ddd',
      borderRadius: '8px',
      padding: '15px',
      margin: '10px',
      maxWidth: '300px',
      boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
    }}>
      <img
        src={imageUrl}
        alt={name}
        style={{ width: '100%', height: '200px', objectFit: 'cover', borderRadius: '4px' }}
      />
      <h3>{name}</h3>
      <p>Harga: **Rp{price.toLocaleString('id-ID')}**</p>
      <p style={{ fontSize: '0.9em', color: '#555' }}>{description}</p>
      <button style={{
        backgroundColor: '#007bff',
        color: 'white',
        border: 'none',
        padding: '10px 15px',
        borderRadius: '5px',
        cursor: 'pointer'
      }}>
        Beli Sekarang
      </button>
    </div>
  );
}

// Cara penggunaan komponen ini di aplikasi utama (misalnya di App.js)
// import ProductCard from './ProductCard'; // Anggap file ini disimpan sebagai ProductCard.js

/*
function App() {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
      <ProductCard
        name="Smartphone X"
        price={7500000}
        imageUrl="<https://via.placeholder.com/300x200?text=Smartphone>"
        description="Smartphone canggih dengan kamera mutakhir dan performa kencang."
      />
      <ProductCard
        name="Laptop Pro"
        price={12000000}
        imageUrl="<https://via.placeholder.com/300x200?text=Laptop>"
        description="Laptop ideal untuk produktivitas dan multitasking sehari-hari."
      />
    </div>
  );
}

export default App;
*/

Dalam contoh ini, ProductCard adalah Functional Component yang menerima props name, price, imageUrl, dan description. Ia mengembalikan JSX yang membentuk tampilan kartu produk. Simpel dan elegan, kan?

Class Component: Konsep Dasar dan Lifecycle Methods

Meskipun Functional Component dengan Hooks adalah masa depan React, memahami Class Component itu penting karena kamu akan sering menemukannya di kode lama atau proyek legacy. Mereka juga punya konsep fundamental yang bagus untuk dipahami.

Konsep Dasar:

Class Component didefinisikan menggunakan sintaks class JavaScript. Mereka harus extend dari React.Component dan setidaknya harus memiliki metode render().

  • constructor

Ini adalah metode opsional yang pertama kali dijalankan saat sebuah instance dari komponen kelas dibuat. Di sinilah kamu biasanya menginisialisasi state awal komponen dan melakukan binding untuk event handlers jika diperlukan. Kamu harus memanggil super(props) di dalamnya.

class MyClassComponent extends React.Component {
  constructor(props) {
    super(props); // Selalu panggil super(props)!
    this.state = {
      count: 0 // Inisialisasi state
    };
    // this.handleClick = this.handleClick.bind(this); // Contoh binding method
  }
  // ...
}
  • render()

Ini adalah satu-satunya metode yang wajib ada di Class Component. Metode render() bertanggung jawab untuk mengembalikan elemen React (JSX) yang akan dirender ke DOM. Metode ini bersifat "pure", artinya ia harus selalu mengembalikan output yang sama untuk props dan state yang sama, dan tidak boleh menyebabkan efek samping.

class MyClassComponent extends React.Component {
  render() {
    return (
      <div>
        <h1>Halo dari Class Component!</h1>
        <p>Nilai count: {this.state.count}</p>
      </div>
    );
  }
}

Lifecycle Methods (Metode Siklus Hidup)

Class Component punya serangkaian metode khusus yang akan dipanggil React pada berbagai tahapan "kehidupan" sebuah komponen (saat dibuat, diperbarui, atau dihapus dari DOM). Ini sangat berguna untuk melakukan efek samping.

⭐ Mounting (Saat Komponen Dibuat/Muncul):

  • componentDidMount()

Metode ini dipanggil tepat setelah komponen pertama kali dirender dan dimasukkan ke dalam DOM. Ini adalah tempat yang ideal untuk:

  • Melakukan fetching data dari API.
  • Menyiapkan event listeners global.
  • Mengintegrasikan library pihak ketiga yang membutuhkan akses ke DOM.
class DataFetcher extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null };
  }

  componentDidMount() {
    console.log('Komponen DataFetcher sudah mount!');
    // Contoh: fetch data dari API
    fetch('<https://api.example.com/data>')
      .then(response => response.json())
      .then(data => this.setState({ data }));
  }

  render() {
    return (
      <div>
        <h2>Data:</h2>
        {this.state.data ? <pre>{JSON.stringify(this.state.data, null, 2)}</pre> : <p>Memuat data...</p>}
      </div>
    );
  }
}

⭐ Updating (Saat Komponen Diperbarui):

  • componentDidUpdate(prevProps, prevState)

Metode ini dipanggil setelah komponen diperbarui (yaitu, setelah props atau state berubah dan komponen di-render ulang). Di sini kamu bisa membandingkan props dan state yang baru dengan yang lama untuk melakukan efek samping tertentu.

class CountUpdater extends React.Component {
  // ... constructor dan state

  componentDidUpdate(prevProps, prevState) {
    // Cek apakah state 'count' berubah
    if (this.state.count !== prevState.count) {
      console.log(`Count berubah dari ${prevState.count} menjadi ${this.state.count}`);
      // Lakukan sesuatu berdasarkan perubahan count
    }
  }

  render() {
    // ...
  }
}

⭐ Unmounting (Saat Komponen Dihapus):

  • componentWillUnmount()

Metode ini dipanggil sesaat sebelum komponen dihapus dari DOM. Ini adalah tempat yang tepat untuk membersihkan semua efek samping yang telah kamu buat, seperti:

  • Menghapus event listeners.
  • Membatalkan network requests.
  • Membersihkan timers atau intervals.
class TimerCleaner extends React.Component {
  constructor(props) {
    super(props);
    this.state = { timer: 0 };
    this.interval = null;
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState(prevState => ({ timer: prevState.timer + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    console.log('Komponen TimerCleaner akan unmount, membersihkan interval...');
    clearInterval(this.interval); // Sangat penting untuk membersihkan!
  }

  render() {
    return <p>Timer: {this.state.timer} detik</p>;
  }
}

Studi Kasus: Membuat Component Sederhana dengan Class Component

Mari kita coba membuat komponen toggle sederhana menggunakan Class Component.

import React from 'react';

class ToggleButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isOn: false // State awal: tombol mati
    };
    // Mengikat (binding) method agar 'this' berfungsi dengan benar di dalam handleClick
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // Mengubah state 'isOn' menjadi kebalikannya
    this.setState(prevState => ({
      isOn: !prevState.isOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick} style={{
        padding: '10px 20px',
        fontSize: '1em',
        backgroundColor: this.state.isOn ? '#4CAF50' : '#f44336', // Warna berubah sesuai state
        color: 'white',
        border: 'none',
        borderRadius: '5px',
        cursor: 'pointer'
      }}>
        {this.state.isOn ? 'ON' : 'OFF'} {/* Teks berubah sesuai state */}
      </button>
    );
  }
}

// Cara penggunaan komponen ini di aplikasi utama (misalnya di App.js)

/*
function App() {
  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h1>Demo Toggle Button (Class Component)</h1>
      <ToggleButton />
    </div>
  );
}

export default App;
*/

Dalam contoh ToggleButton ini, kita melihat bagaimana Class Component mengelola state dengan this.state dan memperbaruinya dengan this.setState(). Metode handleClick terikat pada instance komponen, dan render() menampilkan UI yang bergantung pada state isOn.

Nah, itulah perkenalan kita dengan Functional Component (dan Hooks-nya yang powerful) serta Class Component (dengan lifecycle methods mereka). Ingat, untuk pengembangan modern, fokuslah pada Functional Component. Tapi, memahami Class Component akan sangat membantumu ketika berhadapan dengan kode yang sudah ada atau mencari solusi di dokumentasi lama.

Bab 4: Props: Mengirim Data ke Component

Oke, di bab sebelumnya kita sudah belajar tentang jenis-jenis komponen. Sekarang, bagaimana caranya komponen-komponen yang mandiri itu bisa "berkomunikasi" satu sama lain? Jawabannya ada pada Props! Anggap saja Props ini sebagai kurir data yang super efisien di React.

Apa itu Props dan Bagaimana Cara Kerjanya?

Props (singkatan dari "properties") adalah cara utama untuk mengirimkan data dari komponen induk (parent) ke komponen anak (child). Bayangkan kamu sedang merakit Lego, dan setiap balok Lego itu adalah sebuah komponen. Nah, props itu seperti instruksi atau cetakan yang kamu berikan ke setiap balok, agar balok itu tahu bagaimana ia harus terlihat atau berfungsi.

Cara Kerjanya:

  1. Satu Arah (Unidirectional Flow): Data melalui props selalu mengalir dari atas ke bawah, yaitu dari komponen induk ke komponen anak. Komponen anak tidak bisa langsung mengubah props yang diterimanya dari induk. Ini adalah konsep penting di React yang disebut "data mengalir ke bawah" (data flows down), yang membuat aplikasi lebih mudah diprediksi dan di-debug.
  2. Read-Only: Props bersifat read-only (hanya bisa dibaca) di dalam komponen anak. Kamu tidak boleh mencoba mengubah nilai props dari dalam komponen anak. Jika komponen anak perlu mengubah data, ia harus mengomunikasikannya kembali ke induk (biasanya melalui callback function yang dikirim sebagai prop) agar induk yang mengubah statenya, lalu data baru akan mengalir ke bawah sebagai props lagi.
  3. Mirip Atribut HTML: Saat kamu menggunakan komponen di JSX, props ditulis mirip seperti atribut HTML.

Contoh sederhana: Komponen <Greeting name="Dunia" /> memiliki prop bernama name dengan nilai "Dunia". Komponen Greeting akan menerima prop ini dan menggunakannya untuk menampilkan sapaan.

Meneruskan Props ke Component

Meneruskan props ke komponen itu mudah sekali! Kamu cukup menuliskannya sebagai atribut saat memanggil komponen di JSX.

Contoh: Mari kita gunakan komponen UserProfile yang menampilkan nama dan email pengguna.

import React from 'react';

// Komponen Functional yang menerima props
function UserProfile(props) {
  return (
    <div style={{ border: '1px solid #ccc', padding: '15px', margin: '10px', borderRadius: '5px' }}>
      <h2>Profil Pengguna</h2>
      <p>Nama: **{props.name}**</p>
      <p>Email: **{props.email}**</p>
    </div>
  );
}

// Komponen Induk (misalnya App.js) yang meneruskan props
function App() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center' }}>
      {/* Meneruskan props 'name' dan 'email' ke UserProfile */}
      <UserProfile name="Budi Santoso" email="[email protected]" />
      <UserProfile name="Citra Kirana" email="[email protected]" />
    </div>
  );
}

export default App;

Penjelasan:

  • Di UserProfile (komponen anak), kita menerima objek props sebagai argumen fungsi.
  • Kita bisa mengakses nilai prop dengan props.namaProp (misalnya props.name).
  • Di App (komponen induk), kita memanggil UserProfile dan meneruskan data sebagai atribut: <UserProfile name="Budi Santoso" email="[email protected]" />.

Destructuring Props (Cara yang Lebih Rapi):

Seringkali, kamu akan melihat props di-destructuring langsung di dalam definisi Functional Component. Ini membuat kode lebih ringkas dan mudah dibaca:

// Destructuring props langsung di argumen fungsi
function UserProfile({ name, email }) { // Langsung ambil 'name' dan 'email' dari props
  return (
    <div style={{ border: '1px solid #ccc', padding: '15px', margin: '10px', borderRadius: '5px' }}>
      <h2>Profil Pengguna</h2>
      <p>Nama: **{name}**</p> {/* Sekarang bisa langsung pakai 'name' */}
      <p>Email: **{email}**</p> {/* Dan langsung pakai 'email' */}
    </div>
  );
}

Ini adalah praktik yang sangat umum dan direkomendasikan karena membuat kode lebih bersih.

Validasi Props dengan PropTypes

Bagaimana kalau kita ingin memastikan bahwa props yang diterima komponen itu selalu sesuai dengan tipe data yang kita harapkan (misalnya, name harus string, age harus number)? Di sinilah PropTypes berperan! PropTypes adalah library dari React itu sendiri yang membantu kita memvalidasi tipe data props yang masuk ke komponen. Ini sangat berguna untuk mencegah bug dan memastikan komponen digunakan dengan benar, terutama di proyek besar atau tim.

Cara Penggunaan:

  1. Instalasi: Jika kamu menggunakan Create React App, PropTypes sudah terinstal. Jika tidak, kamu bisa menginstalnya secara manual: npm install prop-types atau yarn add prop-types
  2. Import: Import PropTypes di file komponen kamu.
  3. Definisikan propTypes: Tambahkan properti propTypes ke komponenmu, di mana kamu mendefinisikan tipe data yang diharapkan untuk setiap prop.

Contoh: Mari kita validasi UserProfile agar name dan email harus berupa string dan age (jika ada) harus number dan wajib.

import React from 'react';
import PropTypes from 'prop-types'; // Import PropTypes

function UserProfile({ name, email, age }) {
  return (
    <div style={{ border: '1px solid #ccc', padding: '15px', margin: '10px', borderRadius: '5px' }}>
      <h2>Profil Pengguna</h2>
      <p>Nama: **{name}**</p>
      <p>Email: **{email}**</p>
      {age && <p>Usia: **{age}** tahun</p>} {/* Hanya tampilkan usia jika ada */}
    </div>
  );
}

// Definisikan propTypes untuk UserProfile
UserProfile.propTypes = {
  name: PropTypes.string.isRequired, // 'name' harus string dan wajib ada
  email: PropTypes.string,          // 'email' harus string (opsional)
  age: PropTypes.number             // 'age' harus number (opsional)
};

// Kamu juga bisa mengatur nilai default untuk props opsional
UserProfile.defaultProps = {
  email: '[email protected]'
};

// Contoh penggunaan di App.js
/*
function App() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center' }}>
      <UserProfile name="Budi Santoso" email="[email protected]" age={30} />
      <UserProfile name="Citra Kirana" /> // Email akan pakai default, tidak ada age
      // <UserProfile email="[email protected]" /> // Ini akan memicu peringatan PropTypes karena 'name' tidak ada
      // <UserProfile name={123} /> // Ini akan memicu peringatan PropTypes karena 'name' bukan string
    </div>
  );
}

export default App;
*/

Jika kamu mengirim prop yang tidak sesuai dengan definisi propTypes (misalnya, name bukan string atau name tidak ada padahal isRequired), React akan menampilkan peringatan di console browser kamu saat mode pengembangan. Ini sangat membantu untuk debugging!

Children Props: Mengirim Konten Tambahan

Ada satu prop spesial di React yang seringkali tidak kita teruskan secara eksplisit, yaitu children. Prop ini memungkinkan kita untuk meneruskan konten (bisa berupa teks, elemen HTML, atau bahkan komponen lain) langsung di antara tag pembuka dan penutup sebuah komponen.

Bayangkan kamu membuat komponen Card yang bisa membungkus konten apapun di dalamnya. children prop adalah jawabannya!

Contoh:

import React from 'react';

// Komponen Card yang menggunakan children prop
function Card({ title, children }) { // Menerima 'title' dan 'children'
  return (
    <div style={{
      border: '1px solid #ddd',
      borderRadius: '8px',
      padding: '20px',
      margin: '15px',
      maxWidth: '400px',
      boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
      backgroundColor: 'white'
    }}>
      {title && <h3 style={{ borderBottom: '1px solid #eee', paddingBottom: '10px', marginBottom: '15px' }}>{title}</h3>}
      {children} {/* Di sinilah konten yang dikirim di antara tag Card akan dirender */}
    </div>
  );
}

// Cara penggunaan di komponen induk (misalnya App.js)
function App() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center', flexWrap: 'wrap' }}>
      <Card title="Selamat Datang!">
        <p>Ini adalah konten paragraf yang dikirim sebagai <strong>children</strong> ke komponen Card.</p>
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
        </ul>
        <button>Pelajari Lebih Lanjut</button>
      </Card>

      <Card>
        <h4>Konten Tanpa Judul</h4>
        <p>Anda bisa mengirim elemen apapun sebagai children.</p>
        <UserProfile name="Dev React" email="[email protected]" />
      </Card>
    </div>
  );
}

export default App;

Dalam contoh ini, apapun yang berada di antara <Card> dan </Card> akan secara otomatis tersedia sebagai props.children di dalam komponen Card. Ini sangat fleksibel dan sering digunakan untuk komponen tata letak atau wrapper.

Gimana, sudah mulai terbayang kan bagaimana props menjadi jembatan komunikasi antar komponen di React? Ingat, props itu ibarat instruksi yang kamu berikan ke komponen anak, dan komponen anak akan "patuh" pada instruksi itu tanpa mengubahnya. Di bab selanjutnya, kita akan belajar tentang State, yaitu data internal komponen yang bisa berubah.

Bab 5: State: Mengelola Data Internal Component

Setelah kita jago mengirim data down menggunakan Props, sekarang saatnya kita kenalan dengan konsep yang nggak kalah penting: State! Jika props adalah data yang diterima dari luar dan sifatnya read-only, state ini adalah "memori" internal sebuah komponen. Dialah yang bikin komponen kita jadi interaktif dan dinamis.

Apa itu State dan Perbedaannya dengan Props?

Mari kita bedakan keduanya biar makin jelas:

  • Props (Properties):
    • Sumber: Data yang diteruskan dari komponen induk ke komponen anak.
    • Arah Aliran: Selalu dari atas ke bawah (induk ke anak).
    • Mutabilitas: Tidak bisa diubah oleh komponen anak. Bersifat read-only.
    • Tujuan: Mengonfigurasi komponen anak atau meneruskan data yang tidak akan berubah di dalam komponen anak itu sendiri.
  • State:
    • Sumber: Data yang dimiliki dan dikelola secara internal oleh sebuah komponen.
    • Arah Aliran: Hanya relevan di dalam komponen itu sendiri.
    • Mutabilitas: Bisa diubah oleh komponen tempat state itu berada. Perubahan state akan memicu re-render komponen.
    • Tujuan: Mengelola data yang dapat berubah seiring waktu dan memengaruhi bagaimana komponen ditampilkan atau berinteraksi. Contohnya: nilai input form, status toggle (on/off), data yang di-fetch dari API, dll.

Singkatnya, kalau props itu seperti instruksi dari atasan yang harus dipatuhi dan nggak bisa diubah, state itu seperti catatan pribadimu yang bisa kamu ubah kapan saja dan akan memengaruhi apa yang kamu lakukan selanjutnya.

Menggunakan State dalam Functional Component (dengan useState)

Di era React modern, Functional Component adalah jagoannya dalam mengelola state berkat adanya Hooks, khususnya useState. Hook ini membuat state management jadi jauh lebih ringkas dan intuitif.

Konsep useState:

useState adalah sebuah fungsi yang mengembalikan sepasang nilai:

  1. Nilai state saat ini.
  2. Fungsi updater (sering disebut setter function) yang memungkinkan kita untuk memperbarui nilai state tersebut.

Ketika fungsi updater dipanggil, React akan memperbarui state dan me-re-render komponen dengan nilai state yang baru.

Contoh: Mari kita buat komponen LikeButton yang akan menghitung jumlah like yang diklik.

import React, { useState } from 'react';

function LikeButton() {
  // Deklarasi state 'likes' dengan nilai awal 0
  // 'likes' adalah nilai state saat ini
  // 'setLikes' adalah fungsi untuk memperbarui 'likes'
  const [likes, setLikes] = useState(0);

  const handleLikeClick = () => {
    // Memperbarui state 'likes' dengan menambahkan 1
    // React akan otomatis me-render ulang komponen ini
    setLikes(likes + 1);
  };

  return (
    <button
      onClick={handleLikeClick}
      style={{
        padding: '10px 15px',
        fontSize: '1.2em',
        backgroundColor: '#007bff',
        color: 'white',
        border: 'none',
        borderRadius: '5px',
        cursor: 'pointer'
      }}
    >
      ❤️ Suka ({likes})
    </button>
  );
}

// Penggunaan di komponen induk (misalnya App.js)
/*
function App() {
  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h2>Postingan Keren</h2>
      <p>Ayo berikan suka pada postingan ini!</p>
      <LikeButton />
    </div>
  );
}

export default App;
*/

Penting: Ketika kamu memanggil setLikes (atau fungsi updater apapun), kamu bisa langsung memberikan nilai baru, atau jika nilai barunya bergantung pada nilai state sebelumnya, lebih baik gunakan callback function di dalam setter (setLikes(prevLikes => prevLikes + 1)). Ini untuk memastikan kamu bekerja dengan nilai state yang paling mutakhir, terutama dalam situasi asinkron.

Menggunakan State dalam Class Component (this.state dan this.setState)

Meskipun Functional Component dengan Hooks lebih populer, Class Component juga bisa mengelola state. Konsepnya sedikit berbeda, tapi esensinya sama.

Konsep this.state dan this.setState:

Di Class Component, state disimpan dalam sebuah objek bernama this.state. Untuk mengubah state, kamu harus menggunakan metode this.setState(). Kamu tidak boleh mengubah this.state secara langsung! Mengapa? Karena this.setState() tidak hanya mengubah nilai state, tapi juga memberitahu React untuk me-re-render komponen. Jika kamu mengubahnya langsung, React tidak akan tahu ada perubahan dan komponen tidak akan di-render ulang.

Contoh: Mari kita buat komponen counter sederhana menggunakan Class Component.

import React from 'react';

class ClassCounter extends React.Component {
  constructor(props) {
    super(props); // Selalu panggil super(props) di constructor Class Component
    this.state = {
      count: 0 // Inisialisasi state awal sebagai sebuah objek
    };
    // Mengikat (binding) method handleIncrement agar 'this' berfungsi dengan benar
    this.handleIncrement = this.handleIncrement.bind(this);
  }

  handleIncrement() {
    // Memperbarui state 'count' menggunakan this.setState()
    // Penting: Gunakan bentuk fungsi jika state berikutnya bergantung pada state sebelumnya
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
    // JANGAN LAKUKAN INI: this.state.count = this.state.count + 1;
  }

  render() {
    return (
      <div style={{ textAlign: 'center', margin: '20px' }}>
        <h2>Class Component Counter</h2>
        <p>Hitungan: **{this.state.count}**</p>
        <button
          onClick={this.handleIncrement}
          style={{
            padding: '10px 20px',
            fontSize: '1em',
            backgroundColor: '#28a745',
            color: 'white',
            border: 'none',
            borderRadius: '5px',
            cursor: 'pointer'
          }}
        >
          Tambah Hitungan
        </button>
      </div>
    );
  }
}

// Penggunaan di komponen induk (misalnya App.js)
/*
function App() {
  return (
    <div>
      <ClassCounter />
    </div>
  );
}

export default App;
*/

Perhatikan: Metode this.setState() itu asinkron. Artinya, React mungkin akan mengelompokkan beberapa pembaruan state untuk alasan performa. Itulah mengapa, jika state yang baru bergantung pada state sebelumnya, sangat direkomendasikan untuk menggunakan bentuk fungsi dari this.setState() (this.setState(prevState => ({ ... }))).

Pentingnya Immutability saat Mengupdate State

Ini adalah konsep krusial di React! Immutability artinya kamu tidak boleh langsung memodifikasi objek atau array yang ada di dalam state. Sebaliknya, kamu harus membuat salinan baru dari objek atau array tersebut, lalu melakukan perubahan pada salinan baru itu, dan kemudian mengaturnya sebagai state yang baru.

Mengapa Immutability Penting?

  1. Deteksi Perubahan oleh React: React mendeteksi perubahan state dengan membandingkan referensi objek atau array lama dengan yang baru. Jika kamu langsung memodifikasi objek atau array yang sama, referensinya tidak berubah, dan React mungkin tidak mendeteksi adanya perubahan, sehingga komponen tidak di-re-render.
  2. Memudahkan Debugging: Dengan objek state yang tidak berubah, kamu bisa dengan mudah melacak bagaimana state berubah dari waktu ke waktu.
  3. Memungkinkan Fitur Optimasi: Fitur-fitur seperti PureComponent atau React.memo bergantung pada perbandingan props dan state secara dangkal (shallow comparison), yang hanya bekerja dengan baik jika data tidak dimutasi secara langsung.

Contoh Mutasi yang SALAH (Hindari Ini!):

// Diasumsikan state = { items: ['apel', 'pisang'] }

// SALAH: Memodifikasi array langsung
this.state.items.push('mangga'); // JANGAN LAKUKAN INI!
this.setState({ items: this.state.items }); // React mungkin tidak mendeteksi perubahan

Contoh Mutasi yang BENAR (Gunakan Ini!):

// Diasumsikan state = { items: ['apel', 'pisang'] }

// BENAR: Membuat salinan baru dan menambahkan item
this.setState(prevState => ({
  items: [...prevState.items, 'mangga'] // Menggunakan spread operator untuk membuat salinan baru
}));

// BENAR: Mengupdate objek nested
this.setState(prevState => ({
  user: {
    ...prevState.user, // Salin properti user yang lama
    age: 31             // Ubah properti 'age'
  }
}));

Gunakan operator spread (...), map(), filter(), slice(), atau Object.assign() (dengan hati-hati) untuk membuat salinan baru dari data saat kamu perlu mengubah array atau objek dalam state.

Gimana? Sekarang kamu sudah tahu perbedaan vital antara props dan state, serta bagaimana cara mengelolanya di Functional maupun Class Component. Ingat, state adalah kunci untuk membuat aplikasi React kita interaktif dan punya "ingatan" sendiri. Di bab selanjutnya, kita akan menyelami Lifecycle Component dan Efek Samping, yang akan membantumu mengelola kapan dan bagaimana efek tertentu terjadi di aplikasi React-mu.

Bab 6: Lifecycle Component (untuk Class Component) dan Efek Samping (untuk Functional Component)

Kita sudah bahas props dan state, yang jadi jantung komponen kita. Tapi, bagaimana kalau komponen kita perlu melakukan sesuatu setelah dia muncul di layar, atau setiap kali ada data yang berubah, atau bahkan sebelum dia menghilang dari layar? Di sinilah konsep Lifecycle (siklus hidup) komponen dan Efek Samping berperan. Mereka adalah cara kita "mengintervensi" di berbagai tahapan hidup sebuah komponen.

Class Component Lifecycle

Class Component punya serangkaian metode khusus yang disebut Lifecycle Methods (metode siklus hidup). Metode-metode ini secara otomatis dipanggil oleh React pada momen-momen tertentu dalam "kehidupan" sebuah komponen. Mari kita pahami fase utamanya:

1. Mounting (Saat Komponen Dibuat dan Muncul di DOM)

Ini adalah fase di mana komponen pertama kali dibuat, di-render ke layar, dan dimasukkan ke dalam DOM.

  • componentDidMount()
    • Kapan dipanggil? Setelah komponen pertama kali dirender dan "terpasang" (di-mount) ke DOM.
    • Apa gunanya? Ini adalah tempat yang paling umum untuk melakukan:
      • Pengambilan data (Data Fetching): Mengambil data dari API eksternal.
      • Setup Langganan (Subscriptions): Menyiapkan event listeners atau langganan ke service lain.
      • Interaksi DOM Langsung: Melakukan manipulasi DOM yang memerlukan elemen sudah ada di halaman (meskipun ini jarang direkomendasikan di React).
    • Ingat: Metode ini hanya dipanggil sekali selama masa hidup komponen.
import React from 'react';

class DataLoader extends React.Component {
  constructor(props) {
    super(props);
    this.state = { users: [], isLoading: true, error: null };
  }

  componentDidMount() {
    console.log('ComponentDidMount: Komponen sudah dirender dan siap!');
    // Contoh: Mengambil data pengguna dari API
    fetch('<https://jsonplaceholder.typicode.com/users>')
      .then(response => {
        if (!response.ok) {
          throw new Error('Gagal mengambil data');
        }
        return response.json();
      })
      .then(data => this.setState({ users: data, isLoading: false }))
      .catch(error => this.setState({ error: error.message, isLoading: false }));
  }

  render() {
    const { users, isLoading, error } = this.state;

    if (isLoading) return <p>Memuat data pengguna...</p>;
    if (error) return <p style={{ color: 'red' }}>Error: {error}</p>;

    return (
      <div>
        <h2>Daftar Pengguna (Class Component)</h2>
        <ul>
          {users.map(user => (
            <li key={user.id}>{user.name} ({user.email})</li>
          ))}
        </ul>
      </div>
    );
  }
}
// Penggunaan: <DataLoader />

2. Updating (Saat Komponen Diperbarui)

Ini adalah fase di mana props atau state sebuah komponen berubah, menyebabkan komponen di-render ulang.

  • componentDidUpdate(prevProps, prevState)
    • Kapan dipanggil? Setelah komponen selesai di-render ulang karena props atau statenya berubah.
    • Apa gunanya?
      • Melakukan operasi DOM atau network request sebagai respons terhadap perubahan props atau state.
      • Penting: Selalu bandingkan prevProps dan prevState dengan this.props dan this.state saat ini untuk menghindari loop tak terbatas (misalnya, jika kamu memanggil setState di sini tanpa kondisi).
import React from 'react';

class SearchResult extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null,
      isLoading: false
    };
  }

  fetchData = (query) => {
    this.setState({ isLoading: true, data: null });
    console.log(`Mencari data untuk: ${query}`);
    // Simulasi fetching data
    setTimeout(() => {
      const result = `Hasil untuk "${query}"`;
      this.setState({ data: result, isLoading: false });
    }, 1000);
  };

  componentDidMount() {
    // Panggil fetch pertama kali saat komponen mount
    this.fetchData(this.props.searchQuery);
  }

  componentDidUpdate(prevProps, prevState) {
    // Hanya fetch data jika searchQuery di props berubah
    if (this.props.searchQuery !== prevProps.searchQuery) {
      console.log('Props searchQuery berubah, fetch data baru!');
      this.fetchData(this.props.searchQuery);
    }
  }

  render() {
    const { searchQuery } = this.props;
    const { data, isLoading } = this.state;

    if (isLoading) return <p>Mencari hasil untuk "{searchQuery}"...</p>;
    return (
      <div>
        <h3>Hasil Pencarian untuk "{searchQuery}"</h3>
        <p>{data}</p>
      </div>
    );
  }
}
// Penggunaan di komponen lain: <SearchResult searchQuery="react hooks" />
// Atau <SearchResult searchQuery={this.state.currentQuery} />

3. Unmounting (Saat Komponen Dihapus dari DOM)

Ini adalah fase terakhir di mana komponen akan dihapus dari DOM.

  • componentWillUnmount()
    • Kapan dipanggil? Tepat sebelum komponen dihancurkan dan dihapus dari DOM.
    • Apa gunanya? Ini adalah tempat yang sangat penting untuk melakukan pembersihan (cleanup). Kamu harus:
      • Membatalkan network requests yang belum selesai.
      • Menghapus event listeners yang dibuat di componentDidMount.
      • Membersihkan timers (setTimeout, setInterval).
      • Membatalkan langganan apapun yang kamu siapkan.
    • Ingat: Jika tidak dibersihkan, ini bisa menyebabkan kebocoran memori (memory leaks) atau bug lainnya.
import React from 'react';

class CountdownTimer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: this.props.startFrom || 10
    };
    this.timerId = null; // Untuk menyimpan ID timer
  }

  componentDidMount() {
    console.log('Timer dimulai!');
    this.timerId = setInterval(() => {
      this.setState(prevState => ({
        count: prevState.count - 1
      }));
    }, 1000);
  }

  componentDidUpdate() {
    if (this.state.count === 0) {
      clearInterval(this.timerId);
      console.log('Timer selesai!');
    }
  }

  componentWillUnmount() {
    // Penting: Bersihkan interval saat komponen dihapus
    clearInterval(this.timerId);
    console.log('ComponentWillUnmount: Timer dibersihkan!');
  }

  render() {
    return (
      <div>
        <h2>Waktu Mundur (Class Component)</h2>
        <p style={{ fontSize: '2em', fontWeight: 'bold' }}>
          {this.state.count > 0 ? this.state.count : 'Waktu Habis!'}
        </p>
      </div>
    );
  }
}

// Untuk mencoba unmount: Render komponen ini, lalu hapus dari DOM.
// Contoh di App.js:
/*
function App() {
  const [showTimer, setShowTimer] = React.useState(true);
  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <button onClick={() => setShowTimer(!showTimer)}>
        {showTimer ? 'Sembunyikan Timer' : 'Tampilkan Timer'}
      </button>
      {showTimer && <CountdownTimer startFrom={5} />}
    </div>
  );
}
export default App;
*/

Functional Component dengan Hooks: Mengenal useEffect untuk Efek Samping

Di dunia Functional Component, tidak ada lifecycle methods seperti Class Component. Sebagai gantinya, kita punya satu Hook super serbaguna bernama useEffect. useEffect ini yang akan menangani semua efek samping yang tadinya diurus oleh componentDidMount, componentDidUpdate, dan componentWillUnmount.

Konsep useEffect

useEffect memungkinkan kita "mengaitkan" efek samping ke komponen fungsi. Sebuah "efek samping" adalah segala sesuatu yang terjadi di luar rendering normal React, seperti pengambilan data, langganan, atau memanipulasi DOM.

Sintaks Dasar:

useEffect(callbackFunction, [dependenciesArray])

  • callbackFunction: Fungsi yang berisi kode efek samping kamu.
  • dependenciesArray (opsional): Array berisi nilai-nilai (props atau state) yang jika berubah, akan memicu callbackFunction untuk dijalankan kembali. Ini sangat penting untuk mengontrol kapan efek dijalankan.

Mengenal useEffect untuk Efek Samping

Mari kita lihat bagaimana useEffect bisa menggantikan lifecycle methods:

  • Menggantikan componentDidMount (Efek Hanya Sekali Saat Mounting)

Untuk efek yang hanya perlu dijalankan sekali setelah komponen pertama kali di-render, berikan array dependensi kosong ([]) sebagai argumen kedua useEffect.

import React, { useState, useEffect } from 'react';

function FetchDataFunctional() {
  const [users, setUsers] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    console.log('useEffect: Komponen sudah dirender, fetch data!');
    // Fetch data (mirip componentDidMount)
    fetch('<https://jsonplaceholder.typicode.com/users>')
      .then(response => {
        if (!response.ok) {
          throw new Error('Gagal mengambil data');
        }
        return response.json();
      })
      .then(data => {
        setUsers(data);
        setIsLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setIsLoading(false);
      });
  }, []); // Array dependensi kosong: efek hanya berjalan sekali saat mount

  if (isLoading) return <p>Memuat data pengguna...</p>;
  if (error) return <p style={{ color: 'red' }}>Error: {error}</p>;

  return (
    <div>
      <h2>Daftar Pengguna (Functional Component)</h2>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name} ({user.email})</li>
        ))}
      </ul>
    </div>
  );
}
// Penggunaan: <FetchDataFunctional />
  • Menggantikan componentDidUpdate (Efek Saat Dependensi Berubah)

Untuk efek yang perlu dijalankan setiap kali props atau state tertentu berubah, sertakan nilai-nilai tersebut di dalam array dependensi.

import React, { useState, useEffect } from 'react';

function DynamicSearch({ query }) {
  const [results, setResults] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    // Jangan fetch jika query kosong (atau saat mount pertama kali tanpa query)
    if (!query) {
      setResults(null);
      return;
    }

    setIsLoading(true);
    setResults(null); // Reset hasil saat query berubah

    console.log(`useEffect: Mencari data untuk: ${query}`);
    // Simulasi fetching data berdasarkan 'query'
    const timer = setTimeout(() => {
      const fetchedResult = `Hasil detail untuk "${query}"`;
      setResults(fetchedResult);
      setIsLoading(false);
    }, 800);

    // Fungsi cleanup: akan dijalankan sebelum efek baru dijalankan atau komponen unmount
    return () => {
      console.log(`useEffect cleanup: Membatalkan pencarian lama untuk ${query}`);
      clearTimeout(timer);
    };
  }, [query]); // Efek ini akan dijalankan setiap kali 'query' berubah

  if (isLoading) return <p>Mencari "{query}"...</p>;
  if (!query) return <p>Silakan masukkan kata kunci pencarian.</p>;
  return (
    <div>
      <h3>Hasil Pencarian Dinamis</h3>
      <p>{results}</p>
    </div>
  );
}

// Penggunaan:
/*
function App() {
  const [searchTerm, setSearchTerm] = useState('');
  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <input
        type="text"
        placeholder="Cari sesuatu..."
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        style={{ padding: '8px', fontSize: '1em', marginBottom: '20px' }}
      />
      <DynamicSearch query={searchTerm} />
    </div>
  );
}
export default App;
*/

Membersihkan Efek Samping (cleanup function)

Ini adalah bagian terpenting dari useEffect untuk mencegah kebocoran memori atau perilaku tak terduga! Jika efek samping kamu memerlukan "pembersihan" (misalnya, menghapus event listener, membatalkan timer, menutup koneksi), kamu bisa mengembalikan sebuah fungsi dari useEffect kamu. Fungsi yang dikembalikan ini disebut cleanup function.

  • Kapan cleanup function dijalankan?
    1. Sebelum efek dijalankan lagi (jika dependensinya berubah).
    2. Saat komponen di-unmount (dihapus dari DOM).

Ini memungkinkan useEffect untuk menggantikan componentWillUnmount dan juga menangani pembersihan saat state atau props yang memicu efek berubah.

Contoh Cleanup: setInterval tanpa cleanup akan terus berjalan meskipun komponen sudah dihapus dari DOM, menyebabkan memory leak.

import React, { useState, useEffect } from 'react';

function SimpleTimer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // Efek samping: mengatur interval
    console.log('Timer dimulai (useEffect)!');
    const intervalId = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

    // Cleanup function: akan dijalankan saat komponen unmount atau sebelum efek baru dijalankan
    return () => {
      console.log('Timer dibersihkan (useEffect cleanup)!');
      clearInterval(intervalId); // Hentikan interval
    };
  }, []); // Array dependensi kosong, efek dan cleanup hanya dijalankan sekali

  return (
    <p>Waktu berjalan: {seconds} detik (Functional Component)</p>
  );
}

// Untuk mencoba unmount dan melihat cleanup:
/*
function App() {
  const [showTimer, setShowTimer] = React.useState(true);
  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <button onClick={() => setShowTimer(!showTimer)}>
        {showTimer ? 'Sembunyikan Timer' : 'Tampilkan Timer'}
      </button>
      {showTimer && <SimpleTimer />}
    </div>
  );
}
export default App;
*/

Setiap kali SimpleTimer dihilangkan dari layar (di-unmount), fungsi yang dikembalikan oleh useEffect (yaitu () => clearInterval(intervalId);) akan dijalankan, memastikan timer berhenti dan tidak menyebabkan kebocoran memori.

Dengan memahami lifecycle methods di Class Component dan useEffect di Functional Component, kamu sekarang memiliki kekuatan penuh untuk mengontrol perilaku komponenmu di setiap tahapan hidupnya. Ini adalah fondasi penting untuk membangun aplikasi React yang responsif, efisien, dan bebas bug. Selanjutnya, kita akan membahas tentang styling komponenmu agar terlihat cantik!

Bab 7: Styling Component di React

Oke, setelah kita berhasil membuat komponen yang interaktif dan cerdas dengan props dan state, sekarang saatnya kita buat mereka terlihat menarik! UI yang fungsional memang penting, tapi UI yang cantik dan mudah digunakan itu yang bikin user betah. Di bab ini, kita akan bahas berbagai cara untuk "mendandani" komponen React-mu. Ada banyak pilihan, dan setiap metode punya keunggulannya sendiri. Yuk, kita lihat!

Inline Styling

Ini adalah cara paling sederhana untuk menerapkan styling di React. Mirip dengan atribut style di HTML, tapi dengan beberapa perbedaan kunci.

Cara Kerja:

Kamu menulis properti CSS sebagai objek JavaScript langsung di dalam atribut style pada elemen JSX. Nama properti CSS yang biasanya ditulis dengan kebab-case (contoh: background-color) di CSS biasa, harus ditulis dengan camelCase di JavaScript (contoh: backgroundColor). Nilai propertinya harus berupa string atau number.

Keunggulan:

  • Cepat dan Mudah: Sangat cepat untuk styling sederhana atau debugging.
  • Scoped by Default: Style hanya diterapkan pada elemen yang kamu targetkan, jadi tidak akan ada konflik dengan style di tempat lain.
  • Dinamis: Mudah menerapkan style berdasarkan state atau props secara langsung.

Kekurangan:

  • Tidak Ideal untuk Gaya Kompleks: Bisa jadi sangat berantakan dan sulit dibaca jika ada banyak properti CSS.
  • Tidak Mendukung Pseudo-selectors & Media Queries: Sulit untuk menerapkan :hover, :active, atau media queries secara langsung.
  • Tidak Ada Caching: Setiap kali komponen di-render, objek style baru dibuat.

Contoh:

import React from 'react';

function InlineStyledButton() {
  const buttonStyle = {
    backgroundColor: '#007bff',
    color: 'white',
    padding: '10px 20px',
    border: 'none',
    borderRadius: '5px',
    cursor: 'pointer',
    fontSize: '1em',
    fontWeight: 'bold'
  };

  const containerStyle = {
    textAlign: 'center',
    margin: '20px'
  };

  return (
    <div style={containerStyle}>
      <p style={{ color: 'gray', fontStyle: 'italic' }}>Ini contoh inline styling.</p>
      <button style={buttonStyle}>
        Klik Saya
      </button>
    </div>
  );
}

// Penggunaan: <InlineStyledButton />

CSS Modules

Ini adalah solusi populer yang memberikan scoping CSS secara lokal, sehingga menghindari masalah konflik nama kelas global.

Cara Kerja:

Kamu menulis file CSS seperti biasa, tapi dengan konvensi penamaan .module.css (misalnya Button.module.css). Saat kamu mengimpor file CSS ini ke dalam komponen JavaScript-mu, setiap nama kelas CSS akan diubah menjadi nama unik secara otomatis (contoh: Button_button__a1b2c).

Keunggulan:

  • Scoped (Lokal): Mencegah konflik nama kelas global sepenuhnya. Setiap kelas CSS menjadi unik untuk komponen yang mengimpornya.
  • CSS Murni: Kamu masih bisa menulis CSS biasa dengan semua fitur yang kamu suka (pseudo-selectors, media queries, keyframes, dll.).
  • Maintainability: Lebih mudah merawat kode CSS karena setiap modul berhubungan langsung dengan komponennya.

Kekurangan:

  • Sedikit Tambahan Konfigurasi: Memerlukan build tool (seperti Webpack yang sudah ada di Create React App atau Next.js) untuk memprosesnya.
  • Tidak Semuanya Dinamis: Untuk style yang sangat dinamis berdasarkan state, kamu mungkin perlu kombinasi dengan inline style atau variabel CSS.

Contoh:

  • Buat file CSS: Button.module.css
/* Button.module.css */
.btn {
  background-color: #28a745;
  color: white;
  padding: 12px 25px;
  border: none;
  border-radius: 7px;
  cursor: pointer;
  font-size: 1.1em;
  transition: background-color 0.3s ease;
}

.btn:hover {
  background-color: #218838;
}

.container {
  text-align: center;
  margin: 30px;
  background-color: #f9f9f9;
  padding: 20px;
  border-radius: 10px;
}
  • Gunakan di komponen React: CssModuleExample.jsx
import React from 'react';
import styles from './Button.module.css'; // Import sebagai objek 'styles'

function CssModuleExample() {
  return (
    <div className={styles.container}>
      <p>Ini contoh styling dengan CSS Modules.</p>
      <button className={styles.btn}>
        Tombol CSS Module
      </button>
    </div>
  );
}

// Penggunaan: <CssModuleExample />

Saat dijalankan, kelas btn dan container akan diubah menjadi sesuatu seperti Button_btn__xyz123 dan Button_container__abc456 secara otomatis.

CSS-in-JS (Contoh: Styled Components)

CSS-in-JS adalah filosofi di mana kamu menulis kode CSS langsung di dalam file JavaScript. Ada banyak library populer untuk ini, salah satunya yang paling terkenal adalah Styled Components.

Cara Kerja (dengan Styled Components):

Kamu membuat komponen React baru yang sudah "terstylized" menggunakan tagged template literals. Semua style yang kamu tulis akan otomatis di-scoped ke komponen tersebut dan injected sebagai tag <style> di kepala dokumen HTML.

Keunggulan:

  • Dynamic Styling: Sangat mudah membuat style yang dinamis berdasarkan props atau state komponen.
  • Scoped by Default: Style benar-benar terisolasi ke komponen, tidak ada konflik nama.
  • Kolokasi: CSS dan logika komponen berada dalam satu file, memudahkan pemeliharaan.
  • Auto-prefixing: Secara otomatis menambahkan vendor prefixes untuk kompatibilitas browser.
  • Theming: Mendukung theming aplikasi dengan mudah.

Kekurangan:

  • Tambahan Library: Membutuhkan instalasi library tambahan (misalnya styled-components).
  • Kurva Pembelajaran Awal: Sintaks mungkin terasa sedikit aneh di awal.
  • Debugging Tools: Meskipun sudah membaik, debugging terkadang bisa sedikit lebih rumit dibandingkan CSS biasa.

Contoh (dengan Styled Components):

  • Instalasi:

npm install styled-components atau yarn add styled-components

  • Gunakan di komponen React: StyledComponentExample.jsx
import React from 'react';
import styled from 'styled-components'; // Import styled

// Buat komponen Button yang sudah ter-style
const StyledButton = styled.button`
  background-color: ${props => (props.$primary ? '#007bff' : '#6c757d')}; /* Warna dinamis berdasarkan prop $primary */
  color: white;
  padding: 12px 25px;
  border: none;
  border-radius: 7px;
  cursor: pointer;
  font-size: 1.1em;
  transition: background-color 0.3s ease;

  &:hover { /* Pseudo-selector juga bisa */
    background-color: ${props => (props.$primary ? '#0056b3' : '#5a6268')};
  }
`;

const StyledContainer = styled.div`
  text-align: center;
  margin: 30px;
  background-color: #e9ecef;
  padding: 20px;
  border-radius: 10px;
  border: 1px solid #dee2e6;
`;

function StyledComponentExample() {
  return (
    <StyledContainer>
      <p>Ini contoh styling dengan Styled Components.</p>
      <StyledButton $primary>
        Tombol Utama
      </StyledButton>
      <StyledButton style={{ marginLeft: '10px' }}>
        Tombol Sekunder
      </StyledButton>
    </StyledContainer>
  );
}

// Penggunaan: <StyledComponentExample />

Perhatikan penggunaan $primary sebagai transient prop untuk menghindari diteruskannya prop ke elemen DOM yang sebenarnya.

Menggunakan Preprocessor CSS (Sass/Less)

Preprocessor CSS seperti Sass (Syntactically Awesome Style Sheets) atau Less memungkinkan kamu menulis CSS dengan fitur-fitur yang lebih canggih seperti variabel, nesting, mixins, dan fungsi. Kode yang kamu tulis akan "dikompilasi" menjadi CSS biasa yang bisa dibaca oleh browser.

Cara Kerja:

Kamu menulis file .scss, .sass, atau .less seperti biasa. Kemudian, build tool (seperti Webpack) akan memprosesnya dan mengubahnya menjadi file .css biasa. File CSS ini kemudian bisa diimpor ke komponen React-mu seperti CSS biasa, atau bahkan dikombinasikan dengan CSS Modules.

Keunggulan:

  • Fitur Lanjutan: Variabel untuk warna, font, dll., nesting untuk struktur yang lebih rapi, mixins untuk kode berulang, dan fungsi untuk komputasi.
  • Modularitas: Meskipun bukan scoping otomatis, kamu bisa memecah style menjadi banyak file kecil dan mengimpornya.
  • Sintaks Lebih Powerful: Membuat penulisan CSS jadi lebih efisien dan terstruktur.
  • Familiar: Jika kamu sudah terbiasa dengan Sass/Less di proyek non-React, ini akan sangat familiar.

Kekurangan:

  • Memerlukan Kompilasi: Ada langkah build tambahan untuk mengubahnya menjadi CSS.
  • Tambahan Dependensi: Membutuhkan package seperti node-sass (untuk Sass) atau less (untuk Less).
  • Tidak Ada Scoping Otomatis: Kecuali dikombinasikan dengan CSS Modules atau strategi penamaan kelas yang ketat (misal BEM), masih ada potensi konflik nama kelas global.

Contoh (dengan Sass/SCSS):

  • Instalasi (di proyek CRA/Webpack):

npm install node-sass atau yarn add node-sass

  • Buat file SCSS: App.scss
/* App.scss */
$primary-color: #6200ea;
$secondary-color: #03dac6;
$text-color: #333;

.app-container {
  font-family: 'Arial', sans-serif;
  text-align: center;
  padding: 20px;
  background-color: #f0f2f5;
  min-height: 100vh;

  h1 {
    color: $primary-color;
    margin-bottom: 25px;
  }

  .info-box {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    padding: 20px;
    margin: 20px auto;
    max-width: 600px;
    color: $text-color;

    p {
      line-height: 1.6;
      margin-bottom: 15px;
    }

    strong {
      color: $secondary-color;
    }
  }
}
  • Import di komponen React (misalnya App.jsx)
import React from 'react';
import './App.scss'; // Import file SCSS secara langsung

function SassExampleComponent() {
  return (
    <div className="app-container">
      <h1>Menggunakan Sass di React</h1>
      <div className="info-box">
        <p>Ini adalah contoh bagaimana kita bisa mengorganisir *style* dengan <strong>Sass</strong>.</p>
        <p>Variabel, *nesting*, dan fitur Sass lainnya membuat CSS jadi lebih terstruktur.</p>
      </div>
    </div>
  );
}

export default SassExampleComponent;

// Pastikan App.js me-render SassExampleComponent
/*
import React from 'react';
import ReactDOM from 'react-dom/client';
import SassExampleComponent from './SassExampleComponent';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <SassExampleComponent />
  </React.StrictMode>
);
*/

Pilihan metode styling ini sangat tergantung pada preferensi pribadi, ukuran proyek, dan kebutuhan spesifik.

  • Inline Styling cocok untuk style yang sangat dinamis atau cepat.
  • CSS Modules adalah pilihan solid jika kamu ingin scoping otomatis tapi tetap menulis CSS biasa.
  • CSS-in-JS (seperti Styled Components) ideal jika kamu menyukai kolokasi logic dan style, serta style yang sangat dinamis dan theming.
  • Preprocessor CSS (Sass/Less) bagus jika kamu sudah terbiasa dengan fiturnya dan ingin style yang terorganisir dengan baik secara modular.

Banyak proyek modern bahkan menggabungkan beberapa metode ini (misalnya CSS Modules untuk utility classes dan Styled Components untuk komponen spesifik). Eksplorasi dan temukan metode yang paling nyaman untukmu! Di bab selanjutnya, kita akan membahas Praktik Terbaik dalam Menggunakan Component.

Bab 8: Praktik Terbaik dalam Menggunakan Component

Oke, kita sudah belajar banyak tentang apa itu komponen, bagaimana cara mereka "berkomunikasi" melalui props dan state, serta bagaimana mendandaninya. Sekarang, ini bagian yang nggak kalah penting: bagaimana cara kita menulis komponen yang bagus? Maksudnya, yang mudah dibaca, dirawat, dan dikembangkan. Ini adalah kunci untuk membangun aplikasi React yang skalabel dan solid!

Membuat Component yang Reusable dan Modular

Ini adalah salah satu tujuan utama dari component-based architecture. Komponen yang reusable (bisa dipakai ulang) dan modular itu seperti alat multifungsi di dapur atau balok Lego yang serbaguna.

Reusable (Bisa Dipakai Ulang):

Sebuah komponen disebut reusable jika kita bisa menggunakannya di berbagai tempat dalam aplikasi (atau bahkan di proyek lain) tanpa perlu banyak perubahan.

  • Bagaimana cara membuatnya?
    • Generik: Buat komponen seumum mungkin. Misalnya, daripada membuat LoginButton dan RegisterButton yang terpisah, buatlah komponen Button generik yang menerima props seperti text, onClick, dan variant (misalnya 'primary', 'secondary').
    • Terima Props dengan Baik: Desain props secara fleksibel agar komponen bisa menerima data yang berbeda dan menyesuaikan tampilannya/perilakunya.
    • Hindari Ketergantungan Internal yang Kuat: Jangan terlalu bergantung pada struktur DOM di luar dirinya atau state global yang spesifik.

Modular:

Artinya, setiap komponen adalah unit yang terisolasi dan mandiri. Ia punya tugas spesifik dan tidak terlalu banyak campur tangan dengan komponen lain kecuali melalui props dan state yang didefinisikan dengan jelas.

  • Bagaimana cara membuatnya?
    • Pecah Logika: Jika sebuah komponen punya banyak tugas (misalnya, menampilkan data, mengambil data, dan mengelola form), pecah menjadi beberapa komponen yang lebih kecil, masing-masing dengan tugas spesifiknya.
    • Fokus pada Satu Hal: Idealnya, satu komponen melakukan satu hal dengan baik. Ini terkait dengan prinsip SRP yang akan kita bahas selanjutnya.
    • Folder Terpisah: Seringkali, setiap komponen atau grup komponen punya folder sendiri yang berisi file JSX, CSS, dan mungkin file tesnya.

Contoh Praktik Reusable & Modular:

Daripada:

// Kurang Reusable
function ArticleCardForHomepage({ title, summary }) { /* ... */ }
function ArticleCardForCategoryPage({ title, summary, category }) { /* ... */ }

Lebih baik:

// Lebih Reusable & Modular
function ArticleCard({ title, summary, category, onReadMore }) {
  // Logic untuk menampilkan kartu artikel
  // Tidak peduli di mana kartu ini akan ditampilkan
  return (
    <div>
      <h3>{title}</h3>
      <p>{summary}</p>
      {category && <small>Kategori: {category}</small>}
      <button onClick={onReadMore}>Baca Selengkapnya</button>
    </div>
  );
}

// Kemudian di Homepage: <ArticleCard title="..." summary="..." onReadMore={...} />
// Di CategoryPage: <ArticleCard title="..." summary="..." category="Teknologi" onReadMore={...} />

Prinsip Single Responsibility (SRP) untuk Component

Prinsip Single Responsibility (SRP) adalah salah satu prinsip desain perangkat lunak SOLID yang sangat relevan untuk komponen React. Intinya sederhana: Setiap komponen harus memiliki hanya satu alasan untuk berubah.

  • Apa Maksudnya?

Ini berarti sebuah komponen harus fokus pada satu tugas atau bagian fungsionalitas yang spesifik. Jika kamu menemukan sebuah komponen yang melakukan terlalu banyak hal, atau yang harus diubah karena dua alasan yang berbeda, itu mungkin melanggar SRP.

  • Contoh Pelanggaran SRP:

Misalnya, kamu punya komponen UserDashboard yang:

  1. Mengambil data pengguna dari API.
  2. Menampilkan daftar pesanan pengguna.
  3. Menampilkan form untuk memperbarui profil pengguna.
  4. Mengelola navigasi dashboard.

Komponen ini punya banyak "alasan untuk berubah": jika API berubah, jika tampilan daftar pesanan berubah, jika validasi form berubah, atau jika struktur navigasi berubah.

  • Menerapkan SRP:

Pecah UserDashboard menjadi komponen-komponen yang lebih kecil dan fokus pada satu tugas:

  • UserDashboard (hanya mengatur tata letak dan meneruskan data)
  • UserOrdersList (menampilkan daftar pesanan)
  • UserProfileForm (mengelola form profil)
  • DashboardNav (mengelola navigasi)
  • UserDataFetcher (mungkin sebuah custom Hook atau komponen yang hanya bertugas fetching data)

Ini membuat kode lebih bersih, lebih mudah diuji, dan lebih mudah dikelola.

Folder Structure untuk Proyek React Skala Besar

Struktur folder yang baik adalah fondasi untuk proyek React yang rapi dan mudah di-maintain, terutama saat proyek mulai tumbuh besar. Tidak ada satu struktur yang "paling benar", tapi ada beberapa pendekatan umum:

  • Struktur Berdasarkan Fitur (Fitur-First):

Ini adalah pendekatan yang paling direkomendasikan untuk proyek skala besar. Kamu mengelompokkan semua file yang terkait dengan sebuah fitur dalam satu folder.

src/
├── components/         // Komponen yang sangat generik/global (misal: Button, Modal, Card)
├── features/
│   ├── Auth/           // Semua yang terkait dengan autentikasi
│   │   ├── components/
│   │   │   ├── LoginForm.jsx
│   │   │   └── RegisterForm.jsx
│   │   ├── hooks/
│   │   │   └── useAuth.js
│   │   ├── pages/
│   │   │   └── LoginPage.jsx
│   │   └── AuthProvider.jsx
│   ├── Products/       // Semua yang terkait dengan produk
│   │   ├── components/
│   │   │   ├── ProductCard.jsx
│   │   │   └── ProductList.jsx
│   │   ├── api/
│   │   │   └── productsApi.js
│   │   ├── pages/
│   │   │   └── ProductsPage.jsx
│   │   └── index.js // Export semua dari fitur ini
│   └── UserProfile/
│       ├── components/
│       ├── hooks/
│       └── pages/
├── pages/              // Komponen yang menjadi "halaman" rute
│   ├── HomePage.jsx
│   └── AboutPage.jsx
├── layouts/            // Layout dasar aplikasi (Header, Footer, Sidebar)
│   └── MainLayout.jsx
├── hooks/              // Custom Hooks global (misal: useDebounce, useLocalStorage)
├── utils/              // Fungsi utility (misal: formatCurrency, validateEmail)
├── assets/             // Gambar, ikon, font
├── styles/             // Global CSS, variabel, mixins
├── App.jsx             // Komponen root aplikasi
└── index.js            // Entry point aplikasi

Keunggulan: Mudah menemukan kode yang terkait dengan fitur tertentu, memudahkan pemindahan/penghapusan fitur, dan mengurangi "jarak" antar file yang saling terkait.

  • Struktur Berdasarkan Tipe (Type-First / Flat):

Ini lebih umum di proyek yang lebih kecil. Semua komponen ada di satu folder components, semua halaman di pages, dll.

src/
├── components/
│   ├── Button.jsx
│   ├── Modal.jsx
│   ├── UserProfile.jsx
│   └── ProductCard.jsx
├── pages/
│   ├── HomePage.jsx
│   ├── ProductPage.jsx
│   └── LoginPage.jsx
├── hooks/
│   ├── useAuth.js
│   └── useDebounce.js
├── api/
│   ├── authApi.js
│   └── productApi.js
├── utils/
├── App.jsx
└── index.js

Keunggulan: Simpel untuk proyek kecil.

Kekurangan: Menjadi sulit dikelola dan dinavigasi saat proyek membesar, karena file-file terkait fitur tersebar di banyak folder.

Pilihlah struktur yang paling sesuai dengan ukuran dan kompleksitas proyekmu. Untuk proyek baru yang potensial akan membesar, pendekatan "Fitur-First" sangat direkomendasikan.

Debugging Component di React Developer Tools

Sebagai developer React, React Developer Tools adalah sahabat terbaikmu! Ini adalah ekstensi browser (tersedia untuk Chrome dan Firefox) yang memungkinkan kamu untuk menginspeksi hierarki komponen React, melihat props dan state mereka secara real-time, dan bahkan memodifikasi state dan props untuk tujuan debugging.

Bagaimana Cara Menggunakannya?

  1. Instalasi:
    • Cari "React Developer Tools" di Chrome Web Store atau Firefox Add-ons.
    • Instal ekstensi tersebut.
  2. Membuka DevTools:
    • Buka aplikasi React kamu di browser.
    • Buka Developer Tools browser (biasanya dengan menekan F12 atau Ctrl+Shift+I / Cmd+Opt+I).
    • Kamu akan melihat tab baru bernama "Components" dan "Profiler".
  3. Tab "Components":
    • Inspeksi Hirarki Komponen: Di panel kiri, kamu bisa melihat seluruh pohon komponen React yang dirender di halaman. Ini sangat membantu untuk memahami struktur aplikasimu.
    • Melihat/Mengubah Props & State: Saat kamu memilih sebuah komponen di panel kiri, panel kanan akan menampilkan props dan state saat ini dari komponen tersebut. Kamu bahkan bisa mengubah nilainya secara langsung untuk melihat bagaimana komponen bereaksi. Ini super berguna untuk menguji berbagai skenario!
    • Melihat Hooks: Untuk Functional Component, kamu bisa melihat nilai state dari useState dan dependensi useEffect.
    • Melacak Render: Ada opsi untuk "Highlight updates when components render" yang akan memberikan bingkai berwarna pada komponen setiap kali mereka di-render. Ini bagus untuk mengidentifikasi re-render yang tidak perlu.
  4. Tab "Profiler":
    • Ini adalah fitur yang lebih canggih untuk menganalisis performa rendering komponenmu. Kamu bisa merekam sesi interaksi dan melihat komponen mana yang di-render ulang, berapa lama waktu yang dibutuhkan, dan mengapa mereka di-render. Berguna untuk optimasi performa!

Tips Debugging Lainnya:

  • console.log(): Jangan remehkan kekuatan console.log()! Masih menjadi cara yang sangat efektif untuk melihat nilai variabel atau melacak alur eksekusi di titik-titik tertentu.
  • Breakpoint: Gunakan debugger browser untuk mengatur breakpoint di kode JavaScript-mu. Ini akan menghentikan eksekusi kode di titik tertentu, memungkinkan kamu memeriksa nilai variabel secara step-by-step.
  • Error Messages: Selalu baca pesan error di console dengan teliti. React punya pesan error yang cukup informatif dan seringkali menunjuk langsung ke masalah.
  • React StrictMode: Bungkus aplikasi utamamu dengan <React.StrictMode>. Ini akan membantu menemukan potensi masalah dalam aplikasi selama pengembangan dengan melakukan pemeriksaan tambahan dan memberikan peringatan (misalnya, tentang deprecated lifecycles atau efek samping yang tidak bersih).

Menerapkan praktik terbaik ini akan sangat membantumu dalam membangun aplikasi React yang lebih tangguh, mudah di-maintain, dan berkinerja baik. Anggap ini sebagai investasi jangka panjang untuk kualitas kodemu!

Di bab terakhir, kita akan mencoba membuat studi kasus sederhana dan merangkum perjalanan belajarmu ini.

Bab 9: Studi Kasus: Membangun Aplikasi Sederhana dengan Komponen

Oke, selamat! Kita sudah bahas semua teori dasar yang penting tentang komponen React: dari definisi, jenis-jenisnya, cara komunikasi pakai props dan state, hingga styling dan praktik terbaik. Sekarang, saatnya kita gabungkan semua pengetahuan itu dalam sebuah studi kasus mini! Kita akan membangun aplikasi sederhana untuk melihat bagaimana semua konsep ini bekerja sama di dunia nyata.

Target kita? Membuat aplikasi "Daftar Tugas Sederhana" (To-Do List). Ini adalah proyek "Halo Dunia"-nya para developer web, dan sangat bagus untuk memahami fundamental.

Perencanaan Struktur Component untuk Aplikasi

Sebelum langsung ngoding, selalu luangkan waktu untuk merencanakan struktur komponenmu. Ingat prinsip modularitas dan Single Responsibility (SRP) yang sudah kita bahas.

Untuk aplikasi To-Do List sederhana kita, kira-kira komponen apa saja yang kita butuhkan?

  1. App (Komponen Root):
    • Ini adalah komponen utama kita. Dia akan mengelola daftar tugas (todos) sebagai statenya dan meneruskannya ke komponen anak.
    • Dia juga akan punya fungsi untuk menambah, menghapus, atau mengubah status tugas, lalu meneruskan fungsi-fungsi ini ke komponen anak yang relevan.
  2. TodoForm (Form Penambah Tugas):
    • Tugasnya spesifik: menyediakan input teks dan tombol untuk menambahkan tugas baru.
    • Dia akan mengelola state dari input teksnya sendiri.
    • Ketika tombol "Tambah" diklik, dia akan memanggil fungsi yang diterima dari App (melalui props) untuk menambahkan tugas baru.
  3. TodoList (Daftar Tugas):
    • Tugasnya spesifik: menampilkan daftar semua tugas.
    • Dia akan menerima array todos sebagai props dari App.
    • Dia juga akan menerima fungsi-fungsi untuk mengubah status atau menghapus tugas, lalu meneruskannya ke setiap TodoItem.
  4. TodoItem (Item Tugas Individu):
    • Tugasnya spesifik: menampilkan satu buah tugas (teks, tombol toggle status selesai, tombol hapus).
    • Dia akan menerima satu objek tugas (todo) sebagai props.
    • Dia akan memanggil fungsi yang diterima dari TodoList (yang asalnya dari App) ketika tombol toggle atau hapus diklik.

Visualisasi Hirarki Komponen:

App
├── TodoForm
└── TodoList
    └── TodoItem (di-render berkali-kali untuk setiap tugas)

Langkah-langkah Membangun Aplikasi Interaktif Sederhana

Mari kita mulai membangunnya satu per satu! Anggap kamu sudah punya proyek React dasar (misalnya dengan Create React App atau Vite).

  1. Siapkan File Komponen:

Buat file-file berikut di folder src (atau src/components):

  • App.jsx (sudah ada, kita akan modifikasi)
  • TodoForm.jsx
  • TodoList.jsx
  • TodoItem.jsx
  • Kita bisa tambahkan index.css atau App.css untuk styling global sederhana.

2. TodoItem.jsx (Komponen Paling Kecil)

// src/TodoItem.jsx
import React from 'react';

function TodoItem({ todo, onToggleComplete, onDelete }) {
  const itemStyle = {
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: '10px',
    borderBottom: '1px solid #eee',
    backgroundColor: todo.completed ? '#e0ffe0' : 'white', // Warna hijau jika selesai
    textDecoration: todo.completed ? 'line-through' : 'none', // Coret jika selesai
    color: todo.completed ? '#888' : '#333'
  };

  const buttonStyle = {
    padding: '5px 10px',
    borderRadius: '4px',
    cursor: 'pointer',
    marginLeft: '8px'
  };

  const toggleButtonStyle = {
    ...buttonStyle,
    backgroundColor: todo.completed ? '#ffc107' : '#28a745', // Kuning untuk undo, hijau untuk complete
    color: 'white',
    border: 'none'
  };

  const deleteButtonStyle = {
    ...buttonStyle,
    backgroundColor: '#dc3545', // Merah untuk hapus
    color: 'white',
    border: 'none'
  };

  return (
    <li style={itemStyle}>
      <span>{todo.text}</span>
      <div>
        <button onClick={() => onToggleComplete(todo.id)} style={toggleButtonStyle}>
          {todo.completed ? 'Batalkan' : 'Selesai'}
        </button>
        <button onClick={() => onDelete(todo.id)} style={deleteButtonStyle}>
          Hapus
        </button>
      </div>
    </li>
  );
}

export default TodoItem;

3. TodoList.jsx

// src/TodoList.jsx
import React from 'react';
import TodoItem from './TodoItem'; // Import TodoItem

function TodoList({ todos, onToggleComplete, onDelete }) {
  const listStyle = {
    listStyle: 'none',
    padding: 0,
    marginTop: '20px',
    border: '1px solid #ddd',
    borderRadius: '8px',
    boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
  };

  return (
    <ul style={listStyle}>
      {/* Melakukan mapping setiap objek todo menjadi komponen TodoItem */}
      {todos.map(todo => (
        <TodoItem
          key={todo.id} // Key sangat penting untuk list di React!
          todo={todo}
          onToggleComplete={onToggleComplete}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
}

export default TodoList;

4. TodoForm.jsx

// src/TodoForm.jsx
import React, { useState } from 'react';

function TodoForm({ onAddTodo }) {
  const [todoText, setTodoText] = useState(''); // State untuk input form

  const handleSubmit = (e) => {
    e.preventDefault(); // Mencegah refresh halaman
    if (todoText.trim() === '') return; // Jangan tambahkan jika kosong

    onAddTodo(todoText); // Panggil fungsi dari App
    setTodoText(''); // Kosongkan input setelah ditambahkan
  };

  const formStyle = {
    display: 'flex',
    gap: '10px',
    marginBottom: '20px'
  };

  const inputStyle = {
    flexGrow: 1,
    padding: '10px',
    borderRadius: '5px',
    border: '1px solid #ddd',
    fontSize: '1em'
  };

  const buttonStyle = {
    padding: '10px 20px',
    backgroundColor: '#007bff',
    color: 'white',
    border: 'none',
    borderRadius: '5px',
    cursor: 'pointer',
    fontSize: '1em'
  };

  return (
    <form onSubmit={handleSubmit} style={formStyle}>
      <input
        type="text"
        placeholder="Tambahkan tugas baru..."
        value={todoText}
        onChange={(e) => setTodoText(e.target.value)}
        style={inputStyle}
      />
      <button type="submit" style={buttonStyle}>
        Tambah Tugas
      </button>
    </form>
  );
}

export default TodoForm;

5. App.jsx (Komponen Root)

// src/App.jsx
import React, { useState, useEffect } from 'react';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
import './App.css'; // Opsional: Untuk styling global

function App() {
  // State untuk menyimpan daftar tugas
  // Kita coba ambil dari localStorage jika ada, kalau tidak, array kosong
  const [todos, setTodos] = useState(() => {
    const savedTodos = localStorage.getItem('todos');
    return savedTodos ? JSON.parse(savedTodos) : [];
  });

  // Efek samping untuk menyimpan todos ke localStorage setiap kali ada perubahan
  useEffect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  }, [todos]); // Akan dijalankan setiap kali state 'todos' berubah

  // Fungsi untuk menambah tugas baru
  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(), // ID unik berdasarkan timestamp
      text: text,
      completed: false
    };
    setTodos(prevTodos => [...prevTodos, newTodo]); // Tambahkan tugas baru
  };

  // Fungsi untuk mengubah status selesai/belum selesai
  const toggleComplete = (id) => {
    setTodos(prevTodos =>
      prevTodos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  // Fungsi untuk menghapus tugas
  const deleteTodo = (id) => {
    setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
  };

  const appContainerStyle = {
    maxWidth: '600px',
    margin: '50px auto',
    padding: '30px',
    border: '1px solid #ccc',
    borderRadius: '10px',
    boxShadow: '0 5px 15px rgba(0,0,0,0.1)',
    backgroundColor: '#f8f8f8'
  };

  const headerStyle = {
    textAlign: 'center',
    color: '#333',
    marginBottom: '30px'
  };

  return (
    <div style={appContainerStyle}>
      <h1 style={headerStyle}>Daftar Tugas Ku 🚀</h1>
      <TodoForm onAddTodo={addTodo} /> {/* Meneruskan fungsi addTodo sebagai prop */}
      <TodoList
        todos={todos} {/* Meneruskan state todos sebagai prop */}
        onToggleComplete={toggleComplete} {/* Meneruskan fungsi toggleComplete */}
        onDelete={deleteTodo} {/* Meneruskan fungsi deleteTodo */}
      />
      {todos.length === 0 && (
        <p style={{ textAlign: 'center', color: '#666', marginTop: '20px' }}>
          Belum ada tugas. Ayo tambahkan yang baru!
        </p>
      )}
    </div>
  );
}

export default App;

6. App.css (Optional, untuk Styling Global Tambahan)

/* src/App.css */
body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f7f6;
  color: #333;
}

Penerapan Props dan State dalam Aplikasi Nyata

Mari kita bedah bagaimana props dan state bekerja bersama di aplikasi To-Do List kita:

  1. State di App.jsx:
    • App adalah komponen induk yang paling tinggi, jadi dia bertanggung jawab mengelola daftar tugas (todos) sebagai state utamanya.
    • const [todos, setTodos] = useState(...) inilah yang menyimpan seluruh data aplikasi kita.
    • Ketika todos berubah (misalnya ada tugas baru, tugas dihapus, atau status diubah), App akan di-re-render, dan perubahan itu akan diteruskan ke komponen anak.
    • Kita juga menggunakan useEffect di App untuk menyimpan dan memuat todos dari localStorage, sehingga daftar tugas tidak hilang saat browser ditutup. Ini contoh nyata penggunaan Efek Samping!
  2. Meneruskan Fungsi sebagai Props (onAddTodo, onToggleComplete, onDelete):
    • Komponen TodoForm, TodoList, dan TodoItem adalah komponen "bodoh" (dumb components) yang hanya tahu cara menampilkan sesuatu dan memicu sebuah event. Mereka tidak tahu bagaimana cara menambah, menghapus, atau mengubah tugas.
    • App yang tahu cara melakukannya (karena dia yang punya state todos). Jadi, App meneruskan fungsi-fungsi ini sebagai props ke komponen anaknya.
      • TodoForm menerima onAddTodo. Ketika form disubmit, dia memanggil props.onAddTodo(todoText).
      • TodoList menerima onToggleComplete dan onDelete. Dia tidak melakukannya sendiri, dia hanya meneruskannya ke setiap TodoItem.
      • TodoItem menerima onToggleComplete dan onDelete. Ketika tombolnya diklik, dia memanggil props.onToggleComplete(todo.id) atau props.onDelete(todo.id).
  3. Meneruskan Data sebagai Props (todos, todo):
    • App meneruskan array todos ke TodoList sebagai prop todos.
    • TodoList kemudian melakukan .map() pada array todos dan untuk setiap item, dia me-render satu TodoItem.
    • Setiap TodoItem menerima satu objek todo sebagai prop todo. Ini yang memungkinkan setiap TodoItem menampilkan teks dan status tugasnya sendiri.
  4. State Lokal di TodoForm.jsx:
    • TodoForm memiliki state lokalnya sendiri (todoText) yang dikelola oleh useState.
    • State ini hanya relevan untuk input form di dalam TodoForm itu sendiri. Ketika kamu mengetik di input, hanya TodoForm yang di-re-render dan state todoTextnya yang berubah.
    • Ketika form di-submit, nilai todoText baru ini diteruskan ke App melalui prop onAddTodo.

Ini adalah pola umum yang akan kamu temukan di banyak aplikasi React: state di komponen induk, data mengalir ke bawah sebagai props, dan fungsi callback diteruskan ke bawah sebagai props untuk memungkinkan komponen anak "berbicara" kembali ke induk dan memicu perubahan state.

Selamat! Kamu baru saja membangun aplikasi React interaktif sederhana dengan konsep component-based, props, state, dan lifecycle (useEffect). Ini adalah langkah besar dalam perjalananmu sebagai developer React!

Di bab terakhir, kita akan merangkum semua yang sudah kita pelajari dan melihat apa langkah selanjutnya untuk mengembangkan keahlian React-mu.

Bab 10: 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 11: Kesimpulan dan Langkah Selanjutnya

Selamat! Kamu sudah sampai di bab terakhir dari tutorial ini. Ini bukan akhir dari perjalananmu di dunia React, tapi justru awal yang baru. Kamu sudah menjejakkan kaki di fondasi yang kuat, dan sekarang saatnya kita merangkum apa saja yang sudah kamu pelajari dan melihat ke mana arah selanjutnya.

Merangkum Konsep Kunci Component

Mari kita kilas balik poin-poin terpenting yang sudah kita bahas mengenai komponen React:

  1. Component adalah Blok Bangunan Utama: Ingat, React itu tentang memecah UI yang kompleks menjadi bagian-bagian kecil yang mandiri dan bisa dipakai ulang. Ini membuat kode lebih mudah diatur, dipahami, dan dikembangkan.
  2. Dua Jenis Component:
    • Class Component: Cara lama, pakai class dan this.state serta lifecycle methods seperti componentDidMount, componentDidUpdate, dan componentWillUnmount. Masih akan kamu temui di proyek lama.
    • Functional Component (dengan Hooks): Ini adalah cara modern dan direkomendasikan. Mereka adalah fungsi JavaScript biasa yang jadi super kuat berkat Hooks seperti useState (untuk state) dan useEffect (untuk efek samping/lifecycle). Lebih ringkas dan bersih!
  3. Props: Komunikasi dari Induk ke Anak: Props adalah cara satu arah untuk mengirim data dari komponen induk ke komponen anak. Sifatnya read-only di komponen anak, nggak bisa diubah sembarangan.
  4. State: Memori Internal Komponen: State adalah data yang dikelola secara internal oleh sebuah komponen dan bisa berubah seiring waktu. Perubahan state akan memicu komponen untuk di-render ulang. Ingat pentingnya immutability saat mengupdate state (jangan langsung mengubah, tapi buat salinan baru).
  5. Styling Komponen: Ada banyak cara mendandani komponenmu:
    • Inline Styling: Cepat dan langsung, tapi kurang fleksibel.
    • CSS Modules: Memberi scoping otomatis untuk menghindari konflik nama.
    • CSS-in-JS (misal Styled Components): Menulis CSS langsung di JavaScript, sangat fleksibel untuk style dinamis.
    • Preprocessor CSS (Sass/Less): Memungkinkan fitur CSS yang lebih canggih seperti variabel dan nesting.
  6. Praktik Terbaik:
    • Buat komponen yang reusable (bisa dipakai ulang) dan modular (fokus pada satu tugas).
    • Terapkan Prinsip Single Responsibility (SRP): satu komponen, satu alasan untuk berubah.
    • Atur Folder Structure dengan baik (misal, berdasarkan fitur) agar proyekmu tetap rapi.
    • Manfaatkan React Developer Tools untuk debugging!

Kamu sudah membangun fondasi yang kokoh untuk petualanganmu di React!

Sumber Belajar Lanjutan untuk React JS

Perjalanan belajarmu tidak berhenti di sini, justru baru dimulai! Dunia web development itu dinamis banget, selalu ada hal baru untuk dipelajari. Berikut beberapa sumber dan topik yang bisa kamu eksplorasi lebih lanjut:

Ini adalah sumber belajar terbaik dan paling akurat. Dokumentasi terbaru React didesain sangat ramah pemula dan mencakup semua konsep secara mendalam. Jangan pernah ragu untuk kembali ke sana.

  • Platform Belajar Online:

Saya saranin di buildwithangga.com karena di sana tempat ternyaman dan paling enak belajar react js, karena materi di sana di jelaskan by project.Jadi kamu bisa langsung ngerti pakai sehari-harinya. Nah belajar by project ini sangat bermanfaat banget buat kita dan tentunya membuat belajar kita lebih cepat, karena kamu bakal tau bagian mana yang sering digunakan atau tidak di gunakan agar kamu tidak buang-buang waktu lagi dengan belajar dan hasilnya efisien💪🏻🔥.