Sering ngalamin over-optimization di React? Yuk, kita bahas solusinya!
Pernah nggak sih kita ngerasa kayaknya udah ngasih yang terbaik buat aplikasi React biar ngebut tapi kok malah jadi lemot dan ribet sendiri Nah kalau iya berarti kita nggak sendirian banget nih ngalamin over optimization di React ini jadi semacam jebakan Batman buat para developer yang niatnya baik ingin bikin performa makin asik tapi malah kejeblos Kita sering banget kan ya tergoda buat langsung pake memo useCallback atau useMemo di mana mana tanpa mikir panjang ujung ujungnya bukan jadi tambah lancar malah aplikasi kita jadi kerasa berat dan kode pun jadi susah diurusin
Di artikel ini kita mau ngobrolin santai kayak temen ngopi sore tentang apa itu over optimization kenapa bisa terjadi dan yang paling penting gimana sih cara kita menghindarinya Kita bakal kulik bareng solusi praktis yang bisa langsung kita terapin di proyek proyek kita supaya optimasi yang kita lakuin itu bener bener ngefek positif dan nggak malah bikin pusing Kita cari tahu yuk gimana caranya jadi developer React yang jago optimasi tapi tetap bijak biar performa aplikasi kita beneran maksimal tanpa drama kode yang ruwet
Apa Itu Over optimization di React
Over optimization itu bisa dibilang kayak kita terlalu semangat ngasih pupuk ke tanaman yang udah subur Bukannya makin bagus malah bisa mati kebanyakan pupuk Nah di React juga gitu Optimasi itu memang penting banget buat bikin aplikasi kita cepet dan responsif Tapi kalau kebablasan atau nggak pada tempatnya justru bisa jadi bumerang performa aplikasi kita malah menurun dan kode jadi susah di maintenance
Intinya over optimization ini terjadi saat kita nerapin teknik optimasi yang sebenarnya nggak diperlukan atau malah bikin overhead yang lebih besar dibanding benefit performa yang didapat Kita jadi nambahin kompleksitas di kode yang sebetulnya nggak ada masalah performa yang signifikan Padahal React sendiri itu udah punya mekanisme optimasi yang canggih loh secara default Jadi sebelum kita buru buru nambahin memo sana sini yuk kita pahami dulu konsep dasarnya
Kenapa Over optimization Sering Terjadi
Ada beberapa alasan kenapa kita sering terjebak dalam lingkaran over optimization ini Pertama seringkali kita termakan dogma bahwa setiap komponen harus dioptimasi agar tidak re render terlalu sering Padahal re render itu bagian alami dari React dan seringkali sangat cepat kok Kedua kita mungkin belum terbiasa dengan tools profiling yang ada Jadi kita cuma nebak nebak saja bagian mana yang perlu dioptimasi tanpa data yang valid Ketiga bisa juga karena tekanan dari tim atau diri sendiri buat bikin aplikasi secepat kilat Akhirnya kita pakai semua jurus optimasi yang kita tahu tanpa seleksi
Salah satu kesalahan umum adalah berasumsi bahwa semua re render itu buruk Padahal tidak semua re render itu mahal Kadang re render itu terjadi sangat cepat dan dampaknya ke performa hampir nggak ada Tapi karena kita khawatir lalu kita paksakan pakai React.memo atau useCallback Dan ini lah awal mula masalahnya Nanti kita bahas kenapa terlalu banyak memo atau useCallback itu malah bisa bikin performa turun
Ironi di Balik Niat Baik
Ironisnya niat kita untuk membuat aplikasi lebih cepat dan responsif justru berujung pada hal sebaliknya Setiap React.memo useCallback dan useMemo itu sendiri punya overhead kecil Dia harus melakukan perbandingan props atau dependensi yang tentunya butuh waktu komputasi Kalau kita nerapinnya di komponen yang sebetulnya jarang re render atau re render nya memang sudah cepat maka overhead dari memoization itu malah lebih mahal daripada keuntungan yang didapat
Bayangkan kita pasang gerbang tol di setiap gang kecil yang sepi orang lewat biaya pasang gerbang dan maintenance nya jauh lebih besar daripada pendapatan tol yang didapat Nah ini persis kayak over optimization kita nambahin logika ekstra perbandingan ekstra di mana mana yang pada akhirnya malah bikin aplikasi kita jadi lebih berat dan kompleks
Tanda Tanda Kita Terjebak Over optimization
Gimana sih cara kita tahu kalau aplikasi React kita itu udah kena over optimization Ada beberapa tanda yang bisa kita perhatiin nih Kita nggak perlu jadi ahli performa buat ngeh sama gejala gejalanya
Performa Malah Turun
Ini mungkin tanda yang paling jelas Kita udah susah payah nambahin React.memo di banyak komponen Kita pakai useCallback buat semua fungsi event handler Kita bungkus semua nilai kompleks dengan useMemo Tapi kok rasanya aplikasi kita tetep aja lemot atau bahkan malah lebih lambat dari sebelumnya Saat ada interaksi kecil aja aplikasi terasa laggy atau ada delay yang nggak wajar Ini bisa jadi indikasi kuat bahwa memoization yang kita lakuin itu malah jadi beban bukan solusi
Setiap kali kita nge wrap komponen dengan React.memo atau pake useCallback useMemo React itu harus melakukan perbandingan Kalau perbandingan itu lebih mahal daripada sekadar re render ulang komponennya ya performa kita malah jadi korban Misalnya perbandingan props di React.memo itu butuh waktu Jika props yang dibandingkan itu kompleks banget kayak object atau array besar maka proses perbandingannya bisa jadi lebih lambat daripada re render komponennya tanpa memo
Kode Jadi Ruwet dan Susah Diurusin
Selain performa yang amburadul kode kita juga bakal jadi ribet banget Susah dibaca susah di debug dan susah di maintain Lihat aja satu file komponen isinya cuma import memo useCallback useMemo di mana mana Terus setiap kali ada perubahan kecil di sebuah fungsi kita harus ingat buat update dependency array di useCallback atau useMemo Kalau lupa alamatnya bug yang nggak terduga bakal muncul
Kode yang kebanyakan useCallback atau useMemo juga bisa bikin developer lain yang baca jadi bingung Ini kok fungsi simpel aja pake useCallback ada apa ya Padahal nggak ada alasan performa yang jelas Nah ini bisa bikin learning curve buat developer baru jadi tinggi dan waktu development pun jadi lebih lama Padung terus kan jadinya
Solusi Praktis Menghindari Over optimization
Oke sekarang kita udah tahu masalahnya dan tanda tandanya Saatnya kita bahas gimana sih cara kita ngatasin over optimization ini Intinya adalah optimasi yang bijak dan terukur
Pahami Kapan Perlu Optimasi
Poin paling penting adalah jangan terburu buru melakukan optimasi premature optimization is the root of all evil kata pepatah programmer Kalo aplikasi kita udah cepet atau belum ada keluhan performa ya jangan diapa apain dulu
Jangan Premature Optimization
Kita seringkali punya insting buat langsung nulis kode yang paling efisien dari awal Padahal ini nggak selalu bagus Kita harus fokus dulu bikin kode yang bener mudah dibaca dan berfungsi sesuai kebutuhan Kalau udah jalan baru deh kita mikirin performa Optimasi itu proses iteratif bukan sesuatu yang harus dilakukan di awal proyek Percayalah kebanyakan masalah performa itu bisa diatasi di kemudian hari dengan lebih terarah
Profiling Dulu Baru Action
Ini dia kuncinya Sebelum kita mulai nambahin memo atau useCallback sana sini kita wajib hukumnya melakukan profiling Pakai React DevTools Profiler untuk ngelihat komponen mana aja yang paling sering re render mana yang paling lama re render nya dan apa penyebabnya Dari data profiling inilah kita bisa tahu titik mana yang bener bener butuh optimasi Dengan begitu kita nggak buang buang waktu dan tenaga buat ngurusin komponen yang sebenarnya nggak ada masalah
Penggunaan React.memo yang Bijak
React.memo itu tools yang powerful buat mencegah re render yang nggak perlu Tapi kita harus pakai dengan bijak
Kapan Pakai memo
Kita pakai React.memo di komponen yang punya karakteristik tertentu Pertama komponen itu re render terlalu sering Kedua re render komponen tersebut itu "mahal" artinya butuh waktu komputasi yang signifikan Ketiga props yang diterima komponen itu seringnya sama atau tidak berubah dan perbandingan props nya itu murah Misalnya komponen yang cuma nerima string atau number sebagai props
Kalau komponen kita itu re render tapi re render nya cepet banget atau props nya selalu berubah setiap kali parent nya re render ya React.memo ini nggak akan banyak membantu Malah bisa jadi bikin overhead tambahan
Hati Hati dengan Props
React.memo melakukan shallow comparison pada props secara default Artinya dia hanya membandingkan nilai primitif langsung dan referensi objek Kalau props kita adalah objek atau array baru setiap kali parent re render walaupun isinya sama React.memo akan tetap melakukan re render Ini karena referensi objeknya berbeda
Untuk mengatasi ini kita bisa pakai useCallback atau useMemo buat nge maintain referensi objek atau fungsi supaya nggak berubah Tapi ya itu tadi pastikan dulu perbandingannya itu bener bener perlu Kalau nggak malah jadi masalah baru
useCallback dan useMemo dengan Tepat
Dua Hooks ini sering banget jadi biang kerok over optimization kalau dipakai sembarangan
useCallback untuk Fungsi
useCallback itu dipakai buat memoize fungsi Artinya fungsi itu hanya akan dibuat ulang jika ada dependensi di array nya yang berubah Ini berguna banget kalau kita passing fungsi sebagai props ke komponen yang di React.memo supaya referensi fungsi itu nggak berubah setiap kali parent re render
Tapi hati hati pakai useCallback untuk semua fungsi Ingat useCallback itu ada overhead nya kalau fungsi kita cuma dipake internal di komponen yang sama atau nggak di passing ke komponen memoized ya nggak perlu pake useCallback itu cuma nambahin beban aja
useMemo untuk Nilai
Mirip dengan useCallback useMemo itu buat memoize nilai yang hasil komputasinya mahal Misalnya kita punya sebuah operasi perhitungan yang berat atau filter array yang panjang Nah hasil komputasi ini bisa kita bungkus dengan useMemo supaya nggak dihitung ulang setiap kali re render selama dependensinya nggak berubah
Sama kayak useCallback jangan pake useMemo buat nilai yang sederhana atau yang hasil komputasinya murah Misalnya cuma bikin string atau number baru itu nggak perlu banget pake useMemo Malah jadi lebih lambat karena ada overhead nya
Dependency Array Itu Penting
Mau pakai useCallback atau useMemo pastikan kita isi dependency array nya dengan benar Jangan sampai ada dependensi yang ketinggalan karena bisa menyebabkan stale closures atau bug yang susah dilacak Di sisi lain jangan juga masukin terlalu banyak dependensi kalau nggak diperlukan karena itu berarti useCallback atau useMemo akan sering dibuat ulang dan tujuan memoizationnya nggak tercapai
Kalau bingung mau masukin apa di dependency array bayangkan kita butuh variabel atau fungsi apa saja agar logika di dalam useCallback atau useMemo itu bisa jalan tanpa error dan selalu memakai nilai terbaru
Manfaatkan Context dan State Management Lain
Terlalu banyak nge passing props dari atas ke bawah bisa jadi penyebab re render yang nggak perlu Apalagi kalau props yang di passing itu berupa objek atau array yang referensinya selalu baru
Pembagian State yang Efisien
Coba deh kita pikirin ulang gimana kita meletakkan state di aplikasi kita Kadang kita meletakkan state terlalu tinggi di komponen parent sehingga setiap kali state itu berubah semua child component di bawahnya ikut re render padahal nggak semua child component butuh data state tersebut
Kita bisa memecah state menjadi bagian bagian yang lebih kecil dan meletakkannya di komponen yang lebih rendah sesuai kebutuhan Atau kita bisa gunakan React Context untuk membagikan data ke banyak komponen tanpa harus passing props satu per satu Tapi ingat Context juga ada overhead nya kalau provider nya re render semua consumer di bawahnya juga akan re render kalau nilainya berubah
Hindari Global State Berlebihan
Menggunakan state management library kayak Redux Zustand atau Jotai itu memang asik Tapi jangan sampai semua state kita jadi global State yang cuma dipakai di satu atau dua komponen sebaiknya tetap lokal saja Jangan sampai cuma buat toggle modal kita pakai global state yang malah bikin aplikasi jadi lebih kompleks
Pikirkan baik baik kapan sebuah state perlu di share secara global dan kapan cukup menjadi local state Pembagian state yang efisien akan sangat membantu mengurangi re render yang nggak perlu
Code Splitting dan Lazy Loading
Ini solusi buat masalah ukuran bundle aplikasi yang besar bukan re render tapi seringkali terkait dengan performa awal
Memecah Bundle Code
Aplikasi React yang besar itu bisa punya ukuran bundle JavaScript yang gede banget Ini bikin waktu loading awal aplikasi jadi lama Untungnya kita bisa pakai code splitting atau memecah bundle kita jadi beberapa bagian kecil Dengan begitu browser cuma perlu download bagian code yang dibutuhkan di awal sisanya bisa didownload belakangan
Memuat Komponen Saat Dibutuhkan
React punya fitur React.lazy dan Suspense yang memungkinkan kita melakukan lazy loading komponen Artinya sebuah komponen beserta code nya baru akan didownload dan di render ketika memang dibutuhkan Misalnya kita punya halaman admin yang jarang diakses Nah komponen halaman admin ini bisa kita lazy load Jadi user yang nggak pernah masuk halaman admin nggak perlu download code nya
Ini membantu banget buat meningkatkan waktu loading awal atau First Contentful Paint aplikasi kita Jadi pengalaman user makin asik
Virtualisasi List Jika Perlu
Kita seringkali punya list data yang panjang banget ratusan bahkan ribuan item Kalau kita render semua item list itu bersamaan di DOM browser kita dijamin bakal nge lag Atau bahkan crash
Virtualisasi list itu solusinya Dengan teknik ini kita hanya me render item item yang terlihat di viewport pengguna selebihnya tidak di render atau di "virtualisasi" Saat user scroll ke atas atau ke bawah item item baru akan di render dan item yang tidak terlihat akan di unmount
Library seperti react-window atau react-virtualized bisa membantu kita banget buat implementasi virtualisasi list Tapi ini khusus buat kasus list data yang sangat banyak saja ya Jangan dipakai buat list yang cuma 10 atau 20 item itu malah bikin overhead
Tools untuk Membantu Identifikasi Masalah
Tadi kita udah ngomongin tentang profiling dan pentingnya data Nah ini dia beberapa tools yang wajib kita kuasai
React DevTools Profiler
Ini adalah sahabat terbaik kita buat ngalahin over optimization Di React DevTools kita bisa masuk ke tab Profiler Di situ kita bisa mulai merekam interaksi kita dengan aplikasi Lalu setelah selesai merekam kita bisa lihat timeline re render dari semua komponen Kita bisa tahu komponen mana yang re render berapa kali kenapa re render dan berapa lama waktu re render nya
Dari data ini kita bisa tahu dengan pasti komponen mana yang perlu kita optimasi dengan React.memo useCallback atau useMemo Dengan begitu kita nggak lagi nebak nebak atau ngelakuin optimasi yang nggak perlu
Lighthouse
Lighthouse itu tools dari Google yang built in di Chrome DevTools Dia bisa ngasih report performa aksesibilitas best practices dan SEO untuk website kita Walaupun nggak spesifik ke React tapi report performa dari Lighthouse bisa ngasih gambaran umum tentang kecepatan loading aplikasi kita dan potensi bottleneck yang ada
Ini bisa jadi titik awal yang bagus buat ngelihat apakah aplikasi kita secara keseluruhan udah punya performa yang baik atau masih perlu banyak perbaikan Dari situ baru kita bisa masuk lebih detail pakai React DevTools
Intinya Lighthouse ini ngasih gambaran besar dulu baru nanti kita masuk ke detailnya pakai React DevTools
Gimana udah makin tercerahkan kan Kita udah bahas banyak banget nih mulai dari apa itu over optimization kenapa bisa terjadi tanda tandanya sampai solusi solusi praktisnya Poin pentingnya adalah optimasi itu harus bijak terukur dan berdasarkan data Jangan sampai niat baik kita buat bikin aplikasi React makin ngebut malah jadi bumerang yang bikin pusing dan performa amburadul
Kita sebagai developer harus selalu berpikir kritis Jangan asal ikut ikutan pakai semua Hook optimasi yang ada tapi nggak tahu tujuannya Lakukan profiling pahami masalahnya baru deh terapkan solusi yang tepat Dengan begitu aplikasi React kita bisa performa maksimal dan kode kita tetap bersih mudah diurusin dan pastinya makin asik buat dikembangkan Kalau ada tips lain atau pengalaman seru soal over optimization yuk sharing di kolom komentar kita diskusi bareng ya Semoga artikel ini bermanfaat buat perjalanan coding kita semua