Langsung ke konten
KamusNgoding
Pemula React 5 menit baca

Apa itu Component Lifecycle di React? Penjelasan Lengkap untuk Pemula

#react #lifecycle #component #beginner

Apa itu Component Lifecycle di React? Penjelasan Lengkap untuk Pemula

Pendahuluan

Pernahkah kamu bertanya-tanya apa yang terjadi di balik layar saat komponen React muncul di halaman, berubah, lalu menghilang? Semua itu diatur oleh sesuatu yang disebut Component Lifecycle — siklus hidup sebuah komponen.

Bayangkan lifecycle seperti siklus hidup manusia: lahir, tumbuh dewasa, lalu meninggal. Komponen React pun mengalami hal serupa: mount (muncul di DOM), update (berubah saat state atau props berubah), dan unmount (dihapus dari DOM).

Memahami lifecycle sangat penting agar kamu bisa mengontrol kapan data di-fetch, kapan event listener dipasang atau dilepas, dan bagaimana mencegah memory leak di aplikasimu. Jika kamu sudah familiar dengan State dan Hooks di React sebelumnya, artikel ini akan melengkapi pemahamanmu secara menyeluruh.


Memahami Konsep Dasar Component Lifecycle

Setiap komponen React melewati tiga fase utama:

FasePenjelasan
MountingKomponen dibuat dan dimasukkan ke dalam DOM
UpdatingKomponen diperbarui karena perubahan state atau props
UnmountingKomponen dihapus dari DOM

Analoginya seperti membuka aplikasi ride-hailing. Saat kamu buka Gojek, layar beranda mount ke tampilan. Saat kamu pilih destinasi, tampilan update. Saat kamu tutup aplikasi, semua komponen unmount dari memori.


Lifecycle di Class Components (Metode Klasik)

Sebelum Hooks hadir, React menggunakan Class Components untuk mengelola lifecycle. Ada beberapa metode lifecycle yang penting untuk dipahami:

1. componentDidMount — Setelah Komponen Muncul

Metode ini dipanggil sekali setelah komponen pertama kali muncul di DOM. Tempat terbaik untuk fetch data dari API.

import React, { Component } from 'react';

class ProfilPengguna extends Component {
  constructor(props) {
    super(props);
    this.state = {
      pengguna: null,
      loading: true,
    };
  }

  componentDidMount() {
    // Dipanggil setelah komponen muncul di DOM
    fetch('https://api.example.com/pengguna/1')
      .then((res) => res.json())
      .then((data) => {
        this.setState({ pengguna: data, loading: false });
      });
  }

  render() {
    if (this.state.loading) return <p>Memuat data...</p>;
    return <h1>Halo, {this.state.pengguna.nama}!</h1>;
  }
}

export default ProfilPengguna;

2. componentDidUpdate — Setelah Komponen Diperbarui

Dipanggil setiap kali state atau props berubah. Hati-hati menggunakannya tanpa kondisi karena bisa menyebabkan infinite loop.

import React, { Component } from 'react';

class DetailProduk extends Component {
  constructor(props) {
    super(props);
    this.state = { produk: null };
  }

  componentDidMount() {
    this.fetchDataProduk(this.props.produkId);
  }

  componentDidUpdate(prevProps, prevState) {
    // Cek apakah produkId berubah sebelum fetch ulang
    if (prevProps.produkId !== this.props.produkId) {
      this.fetchDataProduk(this.props.produkId);
    }
  }

  fetchDataProduk(id) {
    fetch(`https://api.example.com/produk/${id}`)
      .then((res) => res.json())
      .then((data) => this.setState({ produk: data }));
  }

  render() {
    if (!this.state.produk) return <p>Memuat produk...</p>;
    return (
      <div>
        <h2>{this.state.produk.nama}</h2>
        <p>Harga: Rp {this.state.produk.harga.toLocaleString('id-ID')}</p>
      </div>
    );
  }
}

export default DetailProduk;

3. componentWillUnmount — Sebelum Komponen Dihapus

Gunakan ini untuk membersihkan side effect: hapus timer, batalkan subscription, atau lepas event listener.

import React, { Component } from 'react';

class TimerBerjalan extends Component {
  componentDidMount() {
    this.timer = setInterval(() => {
      console.log('Timer berjalan...');
    }, 1000);
  }

  componentWillUnmount() {
    // Bersihkan timer agar tidak terjadi memory leak
    clearInterval(this.timer);
  }

  render() {
    return <p>Timer sedang berjalan di background.</p>;
  }
}

export default TimerBerjalan;

Lifecycle di Functional Components (Dengan useEffect)

Sejak React 16.8, cara modern mengelola lifecycle adalah dengan Hooks — khususnya useEffect. Satu hook ini bisa menggantikan componentDidMount, componentDidUpdate, dan componentWillUnmount sekaligus.

Sintaks Dasar useEffect

useEffect(() => {
  // Kode yang dijalankan saat efek berlaku

  return () => {
    // Cleanup function (setara componentWillUnmount)
  };
}, [dependencies]); // Array dependensi menentukan kapan efek dijalankan ulang

Meniru componentDidMount — Jalankan Sekali Saat Mount

Gunakan array dependensi kosong [] agar efek hanya berjalan satu kali saat komponen pertama kali muncul.

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

function DaftarArtikel() {
  const [artikel, setArtikel] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Array kosong [] = hanya jalankan sekali saat mount
    fetch('https://api.example.com/artikel')
      .then((res) => res.json())
      .then((data) => {
        setArtikel(data);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Memuat artikel...</p>;

  return (
    <ul>
      {artikel.map((item) => (
        <li key={item.id}>{item.judul}</li>
      ))}
    </ul>
  );
}

export default DaftarArtikel;

Meniru componentDidUpdate — Jalankan Saat Dependensi Berubah

Masukkan nilai yang ingin dipantau ke dalam array dependensi. Efek akan berjalan ulang setiap kali nilai tersebut berubah.

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

function PencarianProduk({ keyword }) {
  const [hasil, setHasil] = useState([]);
  const [mencari, setMencari] = useState(false);

  useEffect(() => {
    // Dijalankan setiap kali `keyword` berubah
    if (!keyword) return;

    setMencari(true);
    fetch(`https://api.example.com/produk?q=${keyword}`)
      .then((res) => res.json())
      .then((data) => {
        setHasil(data);
        setMencari(false);
      });
  }, [keyword]); // keyword sebagai dependensi

  if (mencari) return <p>Mencari "{keyword}"...</p>;

  return (
    <div>
      {hasil.length === 0 ? (
        <p>Tidak ada hasil untuk "{keyword}"</p>
      ) : (
        hasil.map((p) => <p key={p.id}>{p.nama}</p>)
      )}
    </div>
  );
}

export default PencarianProduk;

Meniru componentWillUnmount — Cleanup Function

Return sebuah fungsi dari useEffect untuk melakukan pembersihan saat komponen unmount.

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

function Notifikasi() {
  const [pesan, setPesan] = useState('');

  useEffect(() => {
    // Simulasi subscribe ke channel notifikasi
    const handleNotifikasi = (data) => {
      setPesan(data.teks);
    };

    window.addEventListener('notifikasi-masuk', handleNotifikasi);

    return () => {
      // Ini dijalankan saat komponen unmount — lepas event listener
      window.removeEventListener('notifikasi-masuk', handleNotifikasi);
    };
  }, []); // Hanya mount/unmount, tidak ada dependensi

  return (
    <div>
      <p>Mendengarkan notifikasi...</p>
      {pesan && <p style={{ color: 'green' }}>Pesan baru: {pesan}</p>}
    </div>
  );
}

export default Notifikasi;

Contoh Kasus Nyata: Timer Countdown Flash Sale

Mari kita buat komponen yang menggabungkan ketiga fase lifecycle — sebuah timer countdown yang bisa dipakai di halaman flash sale (bayangkan kamu ingin membangun fitur seperti yang ada di e-commerce besar).

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

function TimerFlashSale({ durasiDetik }) {
  const [sisaWaktu, setSisaWaktu] = useState(durasiDetik);

  useEffect(() => {
    // MOUNT: mulai interval saat komponen muncul
    const interval = setInterval(() => {
      setSisaWaktu((prev) => {
        if (prev <= 1) {
          clearInterval(interval);
          return 0;
        }
        return prev - 1;
      });
    }, 1000);

    // UNMOUNT: bersihkan interval saat komponen dihapus
    return () => clearInterval(interval);
  }, []); // Hanya jalankan sekali saat mount

  const menit = Math.floor(sisaWaktu / 60);
  const detik = sisaWaktu % 60;

  return (
    <div style={{ textAlign: 'center', padding: '1rem' }}>
      <h2>Flash Sale Berakhir Dalam:</h2>
      <p style={{ fontSize: '2.5rem', fontWeight: 'bold', color: '#e53e3e' }}>
        {String(menit).padStart(2, '0')}:{String(detik).padStart(2, '0')}
      </p>
      {sisaWaktu === 0 && (
        <p style={{ color: 'red', fontWeight: 'bold' }}>Flash sale telah berakhir!</p>
      )}
    </div>
  );
}

export default TimerFlashSale;

// Cara pakai:
// <TimerFlashSale durasiDetik={300} />  → countdown 5 menit

Komponen ini menunjukkan ketiga fase lifecycle secara nyata:

  • Mount: interval dimulai saat komponen pertama kali muncul
  • Update: sisaWaktu berkurang setiap detik → komponen re-render otomatis
  • Unmount: interval dibersihkan agar tidak bocor memori

Pemahaman tentang pola seperti ini juga erat kaitannya dengan bagaimana kamu menyusun kode JavaScript secara terstruktur — kamu bisa baca lebih lanjut di Mastering ES6 Modules: Cara Mengelola Kode JavaScript Secara Terstruktur.


Troubleshooting: Error yang Sering Muncul

Warning: Can’t perform a React state update on an unmounted component

Penyebab: Kamu melakukan setState setelah komponen sudah unmount, biasanya karena fetch data selesai setelah komponen dihapus dari DOM — misalnya pengguna berpindah halaman sebelum request API selesai.

Solusi:

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

function DataPengguna({ userId }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    let isMounted = true; // Flag untuk cek apakah komponen masih ada

    fetch(`https://api.example.com/pengguna/${userId}`)
      .then((res) => res.json())
      .then((result) => {
        if (isMounted) {
          setData(result); // Hanya update jika komponen masih mounted
        }
      });

    return () => {
      isMounted = false; // Set false saat unmount
    };
  }, [userId]);

  return <div>{data ? data.nama : 'Memuat...'}</div>;
}

export default DataPengguna;

useEffect Berjalan Terus-Menerus (Infinite Loop)

Penyebab: Kamu memasukkan objek atau array sebagai dependensi useEffect. Setiap render, objek baru dibuat dengan referensi berbeda sehingga React menganggap dependensi selalu berubah.

Solusi:

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

function DaftarProduk({ kategori }) {
  const [produk, setProduk] = useState([]);

  // ❌ Salah — objek baru setiap render menyebabkan infinite loop
  // useEffect(() => {
  //   fetchProduk({ kategori: kategori });
  // }, [{ kategori: kategori }]);

  // ✅ Benar — gunakan nilai primitif langsung sebagai dependensi
  useEffect(() => {
    fetch(`https://api.example.com/produk?kategori=${kategori}`)
      .then((res) => res.json())
      .then((data) => setProduk(data));
  }, [kategori]); // string primitif — aman sebagai dependensi

  return (
    <ul>
      {produk.map((p) => <li key={p.id}>{p.nama}</li>)}
    </ul>
  );
}

export default DaftarProduk;

componentDidMount Tidak Berjalan di Functional Component

Penyebab: Developer mencoba menggunakan metode class lifecycle (componentDidMount) di functional component — ini tidak bisa dilakukan karena sintaksnya berbeda total.

Solusi:

import React, { useEffect } from 'react';

// ❌ Salah — componentDidMount hanya ada di Class Component
// function KomponenSaya() {
//   componentDidMount() { // SyntaxError!
//     console.log('mounted');
//   }
// }

// ✅ Benar — gunakan useEffect dengan array kosong
function KomponenSaya() {
  useEffect(() => {
    console.log('Komponen berhasil mount!');
    // Lakukan fetch data, setup subscription, dll. di sini
  }, []); // [] = hanya jalankan satu kali saat mount

  return <div>Komponen saya sudah mount!</div>;
}

export default KomponenSaya;

Pertanyaan yang Sering Diajukan

Apa itu Component Lifecycle di React?

Component Lifecycle adalah serangkaian fase yang dilalui setiap komponen React sejak dibuat, diperbarui, hingga dihapus dari DOM. Tiga fase utamanya adalah mounting, updating, dan unmounting. Memahami lifecycle membantu kamu mengontrol kapan kode tertentu dijalankan dalam siklus hidup aplikasi.

Apa perbedaan useEffect dengan componentDidMount?

componentDidMount hanya tersedia di class component dan hanya berjalan sekali setelah mount. useEffect dengan array dependensi kosong [] memiliki perilaku serupa, tetapi tersedia di functional component dan jauh lebih fleksibel karena bisa menangani update dan cleanup dalam satu fungsi yang sama.

Mengapa cleanup function di useEffect itu penting?

Cleanup function mencegah memory leak — situasi di mana resource (seperti timer, subscription, atau event listener) terus berjalan meski komponennya sudah tidak ada di layar. Tanpa cleanup, aplikasimu bisa melambat atau bahkan crash setelah dipakai dalam waktu lama. Selalu tambahkan cleanup jika kamu membuat subscription atau timer di dalam useEffect.

Bagaimana cara menjalankan useEffect hanya saat props tertentu berubah?

Masukkan props tersebut ke dalam array dependensi useEffect. React akan membandingkan nilainya dengan render sebelumnya dan hanya menjalankan efek jika ada yang berubah.

useEffect(() => {
  // Hanya berjalan saat `userId` berubah
  fetchProfilPengguna(userId);
}, [userId]);

Apakah Class Component masih relevan digunakan di React modern?

Class Component masih didukung penuh dan tidak akan dihapus, tetapi komunitas React sangat merekomendasikan Functional Component + Hooks untuk kode baru. Functional Component lebih ringkas, lebih mudah di-test, dan lebih mudah dibaca terutama saat logika lifecycle menjadi kompleks.


Kesimpulan

Component Lifecycle adalah fondasi penting dalam React yang menentukan kapan dan bagaimana komponen berinteraksi dengan dunia luar — mulai dari fetch data, mengelola timer, hingga membersihkan resource saat komponen tidak diperlukan lagi.

Ringkasan yang perlu kamu ingat:

  • Mounting → gunakan useEffect(() => { ... }, []) untuk inisialisasi sekali
  • Updating → gunakan useEffect(() => { ... }, [dependensi]) untuk reaktivitas
  • Unmounting → kembalikan cleanup function dari useEffect untuk mencegah memory leak
  • Class Component menggunakan componentDidMount, componentDidUpdate, componentWillUnmount
  • Functional Component modern cukup dengan useEffect yang fleksibel

Selamat belajar dan terus berlatih! Konsep lifecycle mungkin terasa abstrak di awal, tapi semakin sering kamu mempraktikkannya di proyek nyata, semakin natural rasanya — dan kamu akan terkejut betapa banyak masalah performa bisa diselesaikan hanya dengan memahami lifecycle dengan baik. Jangan ragu untuk eksplorasi artikel-artikel lainnya di KamusNgoding, masih banyak topik seru yang menunggumu!

Artikel Terkait