Episode ini membedah state, props, dan hooks di React Native: useState, useEffect, useRef, useMemo, dan useCallback, pola komposisi komponen, serta typing props dengan TypeScript agar kode aman dari kesalahan tipe.

Di episode 4 kalian belajar membuat tampilan statis. Sekarang tampilan itu perlu hidup: merespons ketukan tombol, memuat data dari server, dan menyimpan nilai input. Semua itu dikendalikan oleh state, props, dan hooks — konsep yang sama dengan React di web, tapi dengan nuansa mobile. Episode 5 membedah hooks yang paling sering dipakai — useState, useEffect, useRef, useMemo, dan useCallback — lalu bagaimana menyusun komponen dengan props dan komposisi, serta typing props menggunakan TypeScript. Ini adalah jantung logika interaktif aplikasi kalian.
useState menyimpan nilai yang bisa berubah dan memicu re-render saat berubah:
import React, { useState } from "react";
import { View, Text, Pressable, StyleSheet } from "react-native";
export function Counter() {
const [count, setCount] = useState(0);
return (
<View style={styles.box}>
<Text style={styles.label}>{count}</Text>
<Pressable style={styles.button} onPress={() => setCount(count + 1)}>
<Text>Tambah</Text>
</Pressable>
</View>
);
}Perhatikan pola pada Counter: useState(0) menginisialisasi state dengan 0, dan setCount(count + 1) memperbarui nilai. Setiap panggilan setCount memicu re-render dengan nilai baru.
Pola yang sangat umum di mobile adalah mengikat TextInput ke state:
const [nama, setNama] = useState("");
<TextInput
value={nama}
onChangeText={setNama}
placeholder="Masukkan nama"
style={styles.input}
/>value={nama} dan onChangeText={setNama} membuat input ini terkontrol penuh — nilai selalu sinkron dengan state nama. Pola ini dasar untuk form, pencarian, dan chat.
useEffect menangani efek samping: mengambil data, subscribe event, atau mengubah hal di luar render. Di mobile, lifecycle komponen mengikuti mounting dan unmounting yang sama dengan React web:
import React, { useState, useEffect } from "react";
import { View, Text } from "react-native";
export function UserStatus() {
const [status, setStatus] = useState("memuat...");
useEffect(() => {
let aktif = true;
fetch("https://api.example.com/status")
.then((res) => res.json())
.then((data) => {
if (aktif) setStatus(data.status);
});
return () => {
aktif = false;
};
}, []);
return (
<View>
<Text>Status: {status}</Text>
</View>
);
}Pola pada UserStatus di atas memperlihatkan dua kebiasaan penting: dependency array kosong [] artinya efek hanya berjalan sekali setelah mount, dan fungsi cleanup aktif = false mencegah pembaruan state setelah komponen unmount — mencegah error umum di React Native.
Jika efek memakai nilai dari props atau state, masukkan nilai itu ke dependency array. Misalnya useEffect(..., [userId]) akan menjalankan ulang fetch setiap kali userId berubah — pola dasar untuk layar detail.
useRef menyimpan nilai yang tidak memicu re-render saat berubah, dan nilainya bertahan seumur hidup komponen. Dua pemakaian utama:
ScrollView untuk scroll programatik.Contoh pemakaian kedua: timerRef.current menyimpan id interval yang di-set oleh setInterval dan dibersihkan oleh clearInterval. Karena tidak memicu render, menyimpannya di useRef jauh lebih bersih daripada di useState.
useMemo meng-cache hasil perhitungan yang mahal. Nilai di dalamnya hanya dihitung ulang saat dependency berubah:
const daftarUrut = useMemo(() => {
return daftar.sort((a, b) => b.skor - a.skor);
}, [daftar]);useCallback mengembalikan referensi fungsi yang sama selama dependency tidak berubah. Ini penting saat fungsi diteruskan sebagai prop ke komponen anak yang di-memo — mencegah re-render yang tidak perlu di episode 8.
const onKlik = useCallback(() => {
console.log("diklik");
}, []);TypeScript membuat kontrak komponen eksplisit. Definisikan props sebagai interface dan tandai optional dengan tanda tanya:
type ButtonProps = {
title: string;
onPress: () => void;
disabled?: boolean;
};
export function Button({ title, onPress, disabled = false }: ButtonProps) {
return (
<Pressable onPress={onPress} disabled={disabled}>
<Text>{title}</Text>
</Pressable>
);
}Pada Button di atas, disabled? bersifat optional dengan default false. Saat memanggil <Button title="Simpan" onPress={simpan} />, editor langsung memvalidasi props yang dikirim.
type CardProps = {
children: React.ReactNode;
};
export function Card({ children }: CardProps) {
return <View style={styles.card}>{children}</View>;
}Dengan pola ini, <Card><Text>Konten bebas</Text></Card> bekerja untuk konten apa pun. Komposisi lebih fleksibel daripada menebak semua kombinasi props.
Tip
Pisahkan komponen "dumb" (hanya menerima props dan merender) dari komponen "smart" yang memegang state. Struktur ini membuat logika mudah dites dan dipakai ulang, seperti yang akan kalian lihat di episode 20.
Episode 5 menghidupkan aplikasi kalian: state dengan useState, efek samping dengan useEffect, nilai stabil dengan useRef, optimasi dengan useMemo dan useCallback, serta kontrak props yang aman berkat TypeScript.
Inti yang harus dibawa pulang:
useState menyimpan nilai yang memicu re-render.useEffect menangani efek samping, dengan cleanup untuk mencegah update setelah unmount.useRef menyimpan nilai stabil yang tidak memicu re-render.useMemo meng-cache hasil kalkulasi; useCallback menstabilkan referensi fungsi.TextInput terkontrol mengikat value ke state.Di episode 6 selanjutnya kita akan membahas navigasi: React Navigation dengan stack, tab, dan drawer, alternatif file-based Expo Router, serta konfigurasi deep linking untuk membuka layar tertentu dari URL.