Episode ini membongkar debugging jembatan JS-native di React Native dengan Hermes: memahami alur panggilan melintasi batas native module, melihat internal calls lewat profiler, lalu menangani crash reporting dan native stack traces dari produksi.

Episode 17 menutup dengan GC tuning: memahami heap behavior, mengurangi pause time, dan memburu memory leaks di perangkat low-memory. Kali ini kita masih di Fase 5, tapi pandangannya bergeser dari memori ke batas antar bahasa: jembatan JS-native di React Native. Kalau aplikasi kalian memanggil modul native, di sinilah error paling misterius lahir — error yang tidak muncul sebagai stack trace JavaScript biasa.
Roadmap episode 18: peta jembatan JS-native dengan Hermes, cara men-debug panggilan melintasi batas tersebut, melihat internal calls lewat profiler, memahami serialisasi data di boundary, lalu menangani crash reporting dan native stack traces.
React Native bukan aplikasi JavaScript murni. Kode JS kalian dieksekusi oleh Hermes, sedangkan UI, sensor, dan layanan sistem hidup di native. Keduanya dihubungkan oleh bridge (arsitektur lama) atau TurboModule + JSI (New Architecture).
import { NativeModules } from "react-native";
const { FileScanner } = NativeModules;
async function scanWithLog(path) {
const started = Date.now();
try {
const result = await FileScanner.scanDirectory(path);
console.log(`[native-bridge] scan selesai dalam ${Date.now() - started}ms`);
return result;
} catch (err) {
console.warn("[native-bridge] scan gagal:", err.message);
throw err;
}
}Perhatikan pola di atas: setiap kali kode menyeberang ke native, ada titik di mana hal-hal bisa melambat atau gagal. Menempelkan log dan pengukuran tepat di boundary akan sangat membantu debugging.
Debugging jembatan dimulai dari log. Di dev, jalankan Metro di satu terminal dan aplikasi di terminal lain:
npx react-native run-android
adb logcat -s ReactNativeJS:V ReactNative:VTag ReactNativeJS memuat console.log dari kode JS yang berjalan di Hermes, sedangkan tag ReactNative memuat log dari sisi native. Kalau panggilan modul native kalian diam tanpa output, cek dulu apakah log JS muncul — jika tidak, masalahnya ada di Hermes atau bundle, bukan di modul native. Ini trik isolasi yang sangat murah; npx react-native info juga membantu memastikan versi Hermes, React Native, dan toolchain konsisten sebelum menyalahkan kode.
Info
Kalau kalian menggunakan New Architecture, JSI membuat panggilan sinkron dan log muncul dalam urutan yang lebih natural. Di arsitektur bridge lama, antrean asinkron bisa membuat urutan log tampak acak — jangan terkecoh, urutkan dengan timestamp, bukan dengan urutan kemunculan.
Kadang log tidak cukup — kalian perlu melihat tumpukan panggilan lengkap, termasuk di mana eksekusi keluar dari JavaScript. Chrome DevTools / React Native DevTools yang terhubung ke Hermes menampilkan panggilan JS, tapi untuk melihat sisi native, gunakan profiler native:
adb shell dumpsys gfxinfo <package> framestats memberi angka frame rendering yang bisa disandingkan dengan profil JS.Kunci memahaminya: sebuah fungsi JS yang terlihat "sebentar" bisa saja membuang ratusan milidetik di dalam modul native di bawahnya. Profiler yang hanya melihat JS akan menunjukkan call stack yang berakhir di titik _nativeCall — itu petunjuk bahwa waktu sebenarnya dihabiskan di luar Hermes.
Setiap data yang menyeberang jembatan harus diserialkan (di arsitektur lama lewat JSON). Objek besar atau struktur aneh sering menjadi biang error:
undefined bisa berubah menjadi null atau hilang setelah serialisasi.Date, Map, dan Set tidak ter-serialisasi secara natural — mereka berubah menjadi objek kosong.try {
const stats = await NativeModules.DeviceStats.read();
console.log("DeviceStats keys:", Object.keys(stats ?? {}));
} catch (err) {
console.error("DeviceStats gagal:", err.code, err.userInfo ?? err.message);
}Untuk panggilan yang menyeberang sering, uji dulu bentuk data di dev dengan log Object.keys seperti di atas sebelum membangun asumsi. Lebih baik lagi: pindahkan logika berat yang hanya butuh akses native ke sisi native, sehingga jembatan tidak bolak-balik.
Error JavaScript punya stack trace JavaScript. Tapi crash di modul native — segmentation fault, kesalahan memori di sisi JNI atau ObjC — tidak menghasilkan stack trace JS. Hermes tetap bisa lapor, tapi untuk native stack trace kita perlu tool khusus:
adb logcat -d | ndk-stack -sym build/app/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib > crash-resolved.txtndk-stack memetakan alamat pada log crash ke nama fungsi di library native. Pastikan simbol berasal dari build yang sama dengan yang dirilis — build berbeda berarti alamat berbeda dan hasilnya menyesatkan. Kalian juga bisa memakai addr2line dengan binary .so yang tepat:
addr2line -f -C -e build/app/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib/arm64-v8a/libreactnative.so 0x4a3f2cUntuk production, integrasikan crash reporter seperti Sentry dalam mode Hermes + React Native. Sentry memetakan native stack trace ke fungsi asli dan source map ke kode JS:
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "https://dsn.kalian@sentry.io/1",
tracesSampleRate: 0.25,
environment: __DEV__ ? "development" : "production",
});Warning
Source map dan simbol native adalah pasangan yang tidak boleh terpisah dari versi build. Simpan keduanya di arsip CI yang diindeks oleh versi rilis — tanpa itu, stack trace produksi yang diterima setahun lagi tidak bisa disimbolkan dengan benar.
Kebiasaan penting: sebelum merilis, uji crash di build rilis buatan dengan adb shell am crash <package> dan pastikan laporan yang masuk terbaca — baik sisi JS maupun sisi native.
Debugging native integration adalah keterampilan paling mahal di stack React Native karena error-nya sering tidak punya wajah JavaScript. Dengan memetakan jembatan JS-native, menempelkan pengukuran di boundary, memakai profiler dua sisi, dan menyiapkan symbolication yang benar, kalian mengubah crash misterius menjadi laporan yang bisa ditindaklanjuti.
Inti yang harus dibawa pulang:
ReactNativeJS) dari log native (ReactNative) untuk mengisolasi sisi yang bermasalah.Di episode 19, kita memasuki Fase 6: Build Systems, CI & Release — mendefinisikan pipeline CI untuk Hermes builds, memvalidasi bytecode dan source maps, dan menegakkan deployment checks untuk Android serta iOS. Sampai jumpa!