useEffect — adalah hook React yang memungkinkan Anda menjalankan efek samping di komponen fungsional, menggantikan metode siklus hidup komponen kelas: componentDidMount, componentDidUpdate dan componentWillUnmount. Menurut React Documentation (2025), useEffect dijalankan setelah React mengonfirmasi perubahan di DOM, yang menjamin akses ke pohon DOM terkini. Hook menerima fungsi efek dan array dependensi opsional yang mengontrol frekuensi eksekusi.
Poin Utama
useEffect — adalah hook yang ditambahkan di React 16.8 untuk menjalankan efek samping di komponen fungsional. Yang dimaksud dengan efek samping adalah operasi yang tidak terkait langsung dengan rendering UI: permintaan HTTP ke API, langganan event, bekerja dengan timer, manipulasi DOM, pencatatan log dan integrasi dengan pustaka pihak ketiga.
Sebelum munculnya hooks, semua operasi ini harus ditempatkan di metode siklus hidup komponen kelas: componentDidMount untuk inisialisasi, componentDidUpdate untuk bereaksi terhadap perubahan props, componentWillUnmount untuk pembersihan. useEffect menggabungkan ketiga skenario dalam satu API terpadu, di mana array dependensi menentukan kapan efek harus dijalankan. Ini menyederhanakan logika dan mengurangi duplikasi kode, terutama dalam skenario dengan langganan.
Menurut React DevTools Usage Survey (2024), useEffect adalah hook terpopuler kedua setelah useState, digunakan di 89% aplikasi React. Sebagian besar pengembang menggunakannya untuk memuat data, sinkronisasi dengan sistem eksternal, dan mengelola langganan event DOM.
import { useEffect } from 'react';
function UserProfile({ userId }) {
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
}
useEffect menjalankan fungsi efek yang diberikan setelah React selesai merender dan memperbarui DOM. Ini adalah perbedaan utama dari perhitungan selama rendering: efek tidak memblokir penggambaran, yang sangat penting untuk kinerja UX. Jika efek dijalankan secara sinkron, pengguna akan melihat antarmuka «membeku» saat memuat data.
Siklus hidup efek tipikal terdiri dari tiga fase. Saat mounting komponen, React menjalankan efek. Pada setiap pembaruan, jika setidaknya satu dependensi dalam array berubah, React pertama-tama menjalankan fungsi pembersihan efek sebelumnya, lalu efek baru. Saat unmount komponen, hanya fungsi pembersihan yang dijalankan.
Menurut React Team — useEffect RFC (2024), implementasi internal useEffect menggunakan antrean efek samping di pohon fiber. Setelah konfirmasi perubahan (commit phase), React menelusuri antrean ini dan memanggil fungsi efek sesuai urutan deklarasinya di komponen. Setiap simpul fiber menyimpan referensi ke efek sebelumnya untuk pembersihan dan eksekusi ulang yang benar.
| Tahap | Tindakan React | Kapan dijalankan |
|---|---|---|
| Mounting | Memanggil fungsi efek | Setelah render pertama |
| Pembaruan | cleanup → efek | Saat dependensi berubah |
| Unmount | Hanya cleanup | Saat komponen dihapus |
Array dependensi — argumen kedua useEffect — menentukan kapan efek harus dijalankan ulang. React membandingkan setiap nilai dalam array dengan render sebelumnya menggunakan Object.is. Jika setidaknya satu nilai berubah, efek dijalankan ulang. Jika array kosong ([]), efek hanya dijalankan sekali setelah mounting.
Pemilihan dependensi yang tepat — bagian tersulit dari bekerja dengan useEffect. Array harus mencakup semua variabel dan fungsi yang digunakan di dalam efek dan dapat berubah antar render. Melewatkan dependensi menyebabkan stale closures — efek «melihat» nilai usang dari render sebelumnya. Menambahkan dependensi yang tidak perlu menyebabkan eksekusi ulang berlebihan dan potensi bug.
// Dependensi mengontrol kapan efek dijalankan ulang
useEffect(() => {
document.title = `User: ${user.name}`;
}, [user.name]); // jalankan ulang hanya saat user.name berubah
// eslint-disable-next-line react-hooks/exhaustive-deps
// Jika Anda melewatkan dependensi, Anda mendapatkan data usang
React menyediakan plugin eslint-plugin-react-hooks dengan aturan exhaustive-deps, yang secara otomatis memeriksa kelengkapan array dependensi. Menurut Meta Engineering Blog (2024), mengaktifkan plugin ini mengurangi jumlah bug terkait hooks sebesar 72%. Disarankan untuk memperbaiki semua peringatan exhaustive-deps, bukan menekannya dengan komentar, kecuali dalam kasus langka dengan logika kustom.
Jika Anda tidak memberikan array dependensi sama sekali, useEffect akan dijalankan setelah setiap render. Ini mungkin berguna untuk sinkronisasi dengan DOM atau pencatatan log, tetapi seringkali merupakan kesalahan: efek terlalu sering dijalankan, yang mengakibatkan penurunan kinerja. Dalam kebanyakan kasus, Anda harus memberikan array kosong (sekali saat mounting) atau array dengan props/state tertentu.
Array kosong ([]) berarti efek tidak bergantung pada nilai apa pun dan dijalankan secara ketat satu kali. Ini analog dengan componentDidMount di komponen kelas. Namun perlu diingat: jika di dalam efek digunakan props atau state tetapi tidak disebutkan dalam array dependensi, efek akan menggunakan nilai awalnya dan tidak akan pernah melihat pembaruan. Ini disebut stale capture dan sering menjadi sumber bug yang sulit dilacak.
| Array dependensi | Perilaku | Analogi dari kelas |
|---|---|---|
| Tanpa argumen | Setelah setiap render | componentDidUpdate |
| [] | Sekali saat mounting | componentDidMount |
| [a, b] | Saat a atau b berubah | Analogi componentWillReceiveProps |
| return cleanup | Mengelola unmount | componentWillUnmount |
Fungsi pembersihan (cleanup) — adalah fungsi yang dapat dikembalikan oleh useEffect dari callback-nya. React memanggilnya saat unmount komponen dan sebelum menjalankan ulang efek saat dependensi berubah. Cleanup diperlukan untuk membatalkan langganan, timer, permintaan, dan sumber daya apa pun yang harus dibebaskan.
Contoh tipikal — langganan WebSocket. Saat mounting, koneksi dibuat; saat pembaruan dependensi — dibuat ulang (cleanup menutup yang lama, efek membuka yang baru); saat unmount — ditutup. Tanpa cleanup, setiap remounting komponen akan membuat koneksi WebSocket baru, yang menyebabkan kebocoran memori dan banyak koneksi.
useEffect(() => {
const socket = new WebSocket('wss://api.example.com');
socket.onmessage = event => setData(event.data);
// Fungsi pembersihan — berjalan saat unmount dan sebelum eksekusi ulang
return () => {
socket.close();
};
}, []);
Menurut React Documentation (2025), AbortController — pendekatan modern untuk membatalkan permintaan fetch di cleanup. Jika efek melakukan permintaan HTTP dan komponen di-unmount sebelum selesai, permintaan terus berjalan, dan setState setelah unmount menyebabkan kesalahan. Buat AbortController di dalam efek dan panggil controller.abort() di cleanup untuk membatalkan permintaan.
Kesalahan paling umum — melewatkan dependensi. Misalnya, efek menggunakan prop userId, tetapi array dependensi kosong. Akibatnya, efek dijalankan sekali dengan nilai awal userId dan tidak pernah bereaksi terhadap perubahannya. Pengembang melihat komponen menerima userId baru, tetapi data tidak diperbarui. eslint-plugin-react-hooks dengan aturan exhaustive-deps mendeteksi bug semacam itu secara otomatis.
// ❌ Race condition — tanpa pembatalan
useEffect(() => {
fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);
// ✅ Diperbaiki dengan AbortController
useEffect(() => {
const controller = new AbortController();
fetch(`/api/user/${userId}`, { signal: controller.signal })
.then(res => setUser(res));
return () => controller.abort();
}, [userId]);
Untuk mengatasi masalah perulangan tak terbatas, hindari menempatkan logika yang memperbarui state berdasarkan state sebelumnya di dalam useEffect. Gunakan bentuk fungsional setState atau lakukan perhitungan di luar efek. Jika efek berlangganan storage atau event browser, pastikan instance pendengar dibuat sekali, bukan setiap render.
Pertanyaan Umum
Secara langsung — tidak, karena useEffect mengharapkan pengembalian fungsi sinkron atau undefined. Jika callback dideklarasikan sebagai async, ia mengembalikan Promise yang diabaikan React, dan mekanisme pembersihan berhenti bekerja. Solusi: panggil fungsi async di dalam efek: useEffect(() => { async function load() { ... }; load(); }, []).
Tidak ada batasan. React merekomendasikan memisahkan logika yang tidak terkait ke dalam useEffect terpisah, bahkan jika mereka memiliki array dependensi yang sama. Setiap efek harus bertanggung jawab atas satu tugas efek samping yang jelas: satu untuk langganan, lainnya untuk memuat data, ketiga untuk sinkronisasi judul tab. Ini menyederhanakan pemahaman dan debugging.
Di React Strict Mode (hanya mode pengembangan) semua efek di-mount, di-unmount, dan di-mount kembali. Ini adalah fitur, bukan bug — React memeriksa apakah pembersihan berfungsi dengan benar. Jika setelah unmount dan remount efek berperilaku tidak benar (misalnya langganan duplikat), pembersihan Anda tidak lengkap. Di produksi, efek dijalankan sekali.
Gunakan AbortController. Buat controller di dalam efek, berikan controller.signal ke fetch, dan di cleanup panggil controller.abort(). Jika komponen di-unmount sebelum permintaan selesai, fetch akan dibatalkan dan setState tidak akan dipanggil. Ini mencegah race condition dan kesalahan «Can't perform a React state update on an unmounted component».
useEffect akan dijalankan setelah setiap render tanpa pengecualian. Ini berarti setiap setState di dalam efek akan menyebabkan render baru → efek baru → perulangan tak terbatas. Dalam praktiknya, efek tanpa array dependensi hampir selalu merupakan kesalahan. Pengecualian — pencatatan log atau sinkronisasi dengan sistem eksternal di mana setiap render memerlukan sinkronisasi.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga