Ternyata Ini Alasan useEffect Kita Sering Bikin Memory Leak di React
Pernah tidak kita merasa aplikasi React yang kita buat mendadak jadi lambat banget setelah dipakai berpindah halaman beberapa kali? Rasanya aneh padahal kodingan kita terlihat rapi dan tidak ada pesan kesalahan sama sekali di konsol. Masalah seperti ini sering banget muncul tanpa kita sadari dan penyebab utamanya biasanya ada di dalam penggunaan Hook useEffect yang kurang tepat.
Hook yang satu ini memang super ampuh untuk menangani proses sampingan seperti mengambil data dari server, menyetel pengukur waktu, atau mendengarkan event dari browser. Namun dibalik kemudahannya, useEffect menyimpan potensi masalah cukup besar jika kita lupa memberikan instruksi pembersihan yang benar. Memori browser akan terus terpakai oleh proses yang sebenarnya sudah tidak dibutuhkan lagi.
Dalam perjalanan belajar React, kita pasti pernah mengalami momen seperti ini. Kita merasa sudah menulis kode dengan benar, tapi browser malah makin berat. Mengapa hal tersebut bisa terjadi? Mari kita bedah bersama alasan mendasar mengapa useEffect yang kita tulis sering menyebabkan kebocoran memori serta bagaimana cara mengatasinya dengan mudah dan santai.
Memahami Kebocoran Memori Secara Sederhana
Sebelum kita masuk ke teknis penggunaan useEffect, kita perlu memahami terlebih dahulu apa itu kebocoran memori. Istilah ini memang terdengar sangat teknis, padahal konsep dasarnya cukup sederhana untuk dipahami.
Bagaimana Memori Bekerja di Browser
Ketika aplikasi React kita berjalan di dalam browser, sistem secara otomatis akan mengalokasikan sejumlah memori. Memori ini dipakai untuk menyimpan variabel, fungsi, serta komponen yang sedang tampil pada layar pengguna. Browser memiliki fitur otomatis bernama pembersih sampah atau garbage collector. Tugas utama dari fitur ini adalah menghapus data yang sudah tidak digunakan lagi dari memori agar aplikasi tetap terasa cepat dan ringan.
Momen Saat Kebocoran Memori Terjadi
Kebocoran memori terjadi ketika ada data atau proses yang sebenarnya sudah tidak dipakai oleh pengguna, namun sistem tidak bisa menghapusnya dari memori. Hal ini umumnya disebabkan karena masih ada acuan atau referensi aktif yang tertinggal di dalam kode kita.
Dalam konteks React, bayangkan ketika pengguna berpindah dari halaman utama ke halaman profil. Komponen di halaman utama seharusnya dihancurkan dan dibersihkan secara total dari memori. Namun jika ada fungsi di dalam useEffect halaman utama yang masih terus berjalan di latar belakang, browser akan menganggap komponen tersebut masih aktif. Akhirnya memori tetap tertahan dan tidak bisa dibebaskan. Jika hal ini terjadi secara berulang, performa aplikasi akan turun secara drastis.
Tiga Penyebab Utama useEffect Menyebabkan Kebocoran Memori
Ada beberapa pola koding yang sering kita gunakan tanpa sadar menjadi penyebab utama terjadinya kebocoran memori. Mari kita pelajari satu demi satu agar kita bisa menghindari kesalahan tersebut saat membuat aplikasi.
Pendengar Event Yang Lupa Dilepas
Kita sering menambahkan pendengar event ke objek window atau document di dalam useEffect. Contohnya saat kita membuat fitur pergeseran layar otomatis, mendeteksi ukuran layar browser, atau mendengarkan penekanan tombol papan ketik dari pengguna.
Masalah besar muncul ketika komponen kita dilepas dari layar. Jika kita tidak menghapus pendengar event tersebut, browser akan terus menjalankan fungsi pendengar di latar belakang setiap kali event terjadi. Fungsi tersebut masih memegang acuan ke komponen lama yang sudah tidak tampil lagi. Akibatnya komponen tersebut gagal dibersihkan oleh sistem pembersih sampah browser.
Pengukur Waktu Yang Masih Berputar
Fitur penundaan waktu seperti setTimeout dan setInterval adalah makanan rutin bagi pengembang web. Kita biasa menggunakannya untuk membuat tayangan gambar otomatis, jam digital, atau penghitung waktu mundur.
Ketika kita memasang interval di dalam useEffect tanpa menghentikannya, interval tersebut akan terus berputar secara konsisten meskipun pengguna sudah berpindah ke halaman lain. Setiap kali interval itu berputar, fungsi di dalamnya akan berusaha memperbarui keadaan atau state dari komponen yang sudah hilang. Ini adalah salah satu penyebab kebocoran memori yang paling sering dialami oleh pengembang React.
Permintaan Data Jaringan Yang Belum Selesai
Proses mengambil data dari server menggunakan fetch sering kali membutuhkan waktu beberapa detik. Skenario yang sangat sering terjadi adalah pengguna membuka suatu halaman, aplikasi mulai melakukan proses pengambilan data, lalu sebelum data selesai diunduh, pengguna secara cepat berpindah ke halaman lain.
Permintaan data tersebut sebenarnya masih terus berjalan di latar belakang jaringan browser. Ketika respons data akhirnya diterima dari server, kode kita akan berusaha menjalankan fungsi pembaruan state pada komponen yang sudah tidak aktif. Browser modern bahkan memberikan pesan peringatan di konsol tentang pembaruan state pada komponen yang sudah dilepas. Peringatan tersebut adalah tanda yang sangat jelas bahwa aplikasi kita mengalami kebocoran memori.
Fungsi Pembersih Sebagai Solusi Utama
React sebenarnya sudah menyediakan mekanisme yang sangat elegan untuk mengatasi semua masalah kebocoran memori ini. Mekanisme tersebut adalah fungsi pembersih atau cleanup function yang bisa kita pasang di dalam useEffect.
Cara Kerja Fungsi Pembersih
Fungsi pembersih adalah sebuah fungsi yang kita kembalikan di akhir blok kode useEffect. React akan secara otomatis menjalankan fungsi pembersih ini sebelum komponen dilepas dari layar atau sebelum efek tersebut dijalankan ulang pada proses pembuatan ulang komponen berikutnya.
Menulis fungsi pembersih membuat kode kita menjadi jauh lebih aman. Kita seperti memberikan instruksi yang sangat jelas kepada React, yaitu jalankan tugas ini saat komponen muncul dan bersihkan tugas ini saat komponen hilang dari layar.
Membersihkan Pendengar Event Dan Pengukur Waktu
Untuk pendengar event, kita tinggal memanggil fungsi penghapus pendengar di dalam fungsi pengembalian useEffect. Begitu pula untuk pengukur waktu, kita bisa menyimpan identitas dari interval atau setTimeout lalu memanggil fungsi penghenti pengukur waktu di dalam fungsi pembersih tersebut.
Pendekatan ini menjamin bahwa tidak ada proses latar belakang yang tertinggal saat pengguna berpindah halaman. Aplikasi kita akan tetap terasa asik dan responsif setiap saat tanpa beban memori tambahan.
Menghentikan Permintaan Jaringan Dengan AbortController
Bagaimana dengan pemanggilan API yang masih berjalan saat komponen dilepas? Kita bisa memanfaatkan fitur bawaan browser yang bernama AbortController.
Dengan membuat instansi AbortController di awal useEffect dan meneruskan sinyalnya ke fungsi pengambil data, kita bisa membatalkan permintaan jaringan tersebut kapan saja. Di dalam fungsi pembersih, kita cukup memanggil metode pembatalan. Jika pengguna berpindah halaman sebelum data selesai dimuat, proses unduh akan langsung dihentikan oleh browser secara bersih sehingga tidak akan ada pembaruan state yang ilegal.
Tips Praktis Menjaga Performa Aplikasi React
Selain memahami cara kerja fungsi pembersih, ada beberapa kebiasaan baik yang bisa kita terapkan saat membangun aplikasi React agar terhindar dari kebocoran memori.
Manfaatkan Fitur React Strict Mode
Saat kita mengembangkan aplikasi di lingkungan lokal, pastikan kita selalu mengaktifkan fitur Strict Mode dari React. Pada versi modern seperti React 18, Strict Mode secara sengaja akan menjalankan efek dua kali saat komponen pertama kali ditampilkan.
Perilaku ini dibuat khusus oleh tim pengembang React untuk membantu kita menguji apakah fungsi pembersih yang kita tulis sudah bekerja dengan sempurna atau belum. Jika kita melihat ada keganjilan seperti pendengar event terpasang ganda atau pengukur waktu berjalan dua kali lebih cepat, itu artinya fungsi pembersih kita masih belum ditulis dengan benar.
Gunakan Pustaka Pengambil Data Modern
Menulis pemanggilan data secara manual menggunakan useEffect dan fetch memang sangat bagus untuk memahami konsep dasar. Namun untuk proyek skala besar, sangat disarankan untuk berpindah menggunakan pustaka manajemen data seperti TanStack Query atau SWR.
Pustaka modern ini sudah dirancang dengan sangat matang untuk menangani pembatalan permintaan data, pengambilan ulang secara otomatis, penyimpanan tembolok, serta pembersihan memori secara otomatis tanpa perlu kita tulis secara manual di setiap komponen. Ini akan membuat kodingan kita menjadi jauh lebih bersih dan bebas dari kesalahan.
Buat Hook Kustom Untuk Logika Berulang
Jika kita memiliki logika proses sampingan yang sering digunakan di banyak tempat, buatlah hook kustom. Sebagai contoh, kita bisa membuat hook khusus untuk mendeteksi ukuran layar atau hook untuk mengelola pengukur waktu.
Dengan membungkus logika tersebut di dalam hook kustom, kita hanya perlu memasang dan menguji fungsi pembersih di satu tempat saja. Komponen lain tinggal memakai hook tersebut dengan aman tanpa perlu khawatir lupa menulis pembersihan memori.
Membangun Kebiasaan Koding Yang Lebih Baik
Memahami alasan mengapa useEffect sering membuat kebocoran memori adalah langkah awal yang sangat penting bagi kita untuk menjadi pengembang React yang lebih handal. Kebocoran memori mungkin tidak terlalu terasa saat aplikasi kita masih kecil, namun dampaknya akan sangat terasa saat aplikasi tumbuh besar dan memiliki banyak pengguna aktif.
Kunci utamanya adalah selalu ingat untuk menutup setiap pintu yang kita buka di dalam useEffect. Jika kita menambahkan pendengar event, hapuslah. Jika kita membuat interval, hentikanlah. Jika kita memulai permintaan jaringan, siapkan mekanisme pembatalannya.
Dengan menerapkan kebiasaan ini secara konsisten, aplikasi React yang kita kembangkan akan senantiasa berjalan cepat, efisien, dan memberikan pengalaman pengguna yang sangat asik. Selamat berkoding dan mari kita buat aplikasi React kita makin bersih tanpa ada kebocoran memori lagi.