1. Pengenalan React Native
React Native adalah framework open-source yang dikembangkan oleh Meta (Facebook) untuk membangun aplikasi mobile menggunakan JavaScript dan React. Tidak seperti hybrid apps yang berjalan di WebView, React Native menggunakan native UI components yang memberikan pengalaman pengguna yang lebih baik.
React Native pertama kali diumumkan oleh Mark Zuckerberg pada tahun 2015 dan telah digunakan oleh aplikasi populer seperti Instagram, Facebook, Shopify, Discord, Pinterest, dan Uber Eats. Framework ini memungkinkan developer web yang sudah familiar dengan React untuk langsung membangun aplikasi mobile tanpa perlu belajar Swift atau Kotlin dari awal.
Mengapa Memilih React Native?
| Keunggulan | Penjelasan |
|---|---|
| Kode Bersama | Hingga 90% kode bisa dibagikan antara Android dan iOS |
| Native Feel | Menggunakan native UI components — tampilan sesuai platform |
| Fast Refresh | Perubahan kode langsung terlihat tanpa full rebuild |
| Ekosistem Besar | Didukung npm — ratusan ribu package JavaScript tersedia |
| Komunitas Aktif | Jutaan developer, dokumentasi lengkap, banyak tutorial |
| Meta Support | Digunakan di produk-produk Meta, di-maintain secara aktif |
| Code Push | Update aplikasi tanpa melalui App Store / Play Store |
┌─────────────────────────────────────────────────────────┐ │ JavaScript Thread │ │ ┌─────────────────────────────────────────────────┐ │ │ │ Your React Native Code (JS/TSX) │ │ │ │ ┌─────────────┐ ┌──────────────┐ │ │ │ │ │ Components │ │ Business │ │ │ │ │ │ (JSX) │ │ Logic │ │ │ │ │ └──────┬──────┘ └──────┬───────┘ │ │ │ └─────────┼────────────────┼─────────────────────┘ │ │ ▼ ▼ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ React Native Bridge (JSON Messages) │ │ │ └───────────────────────┬─────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ Native Thread (Platform Specific) │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ iOS Native │ │ Android │ │ │ │ │ │ UIKit Views │ │ Native Views │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘
2. Setup & Instalasi
Ada dua cara untuk membuat proyek React Native: Expo (recommended untuk pemula) dan React Native CLI (untuk proyek yang membutuhkan native module).
Opsi 1: Expo (Recommended)
# Instalasi Expo CLI secara global npm install -g expo-cli # Buat proyek baru dengan Expo npx create-expo-app MyRNApp cd MyRNApp # Jalankan development server npx expo start # Scan QR code dengan Expo Go di HP # Atau tekan 'a' untuk Android emulator, 'i' untuk iOS simulator
Opsi 2: React Native CLI
# Buat proyek dengan React Native CLI npx react-native init MyRNApp --version latest cd MyRNApp # Jalankan di Android npx react-native run-android # Jalankan di iOS (hanya macOS) cd ios && pod install && cd .. npx react-native run-ios # Prasyarat: # - Node.js >= 18 # - JDK 17 (untuk Android) # - Android Studio + Android SDK # - Xcode + CocoaPods (untuk iOS, macOS only)
Struktur Proyek
MyRNApp/ ├── node_modules/ ├── src/ │ ├── components/ ← Komponen reusable │ │ ├── Header.js │ │ └── Card.js │ ├── screens/ ← Halaman/layar aplikasi │ │ ├── HomeScreen.js │ │ ├── DetailScreen.js │ │ └── ProfileScreen.js │ ├── navigation/ ← Konfigurasi navigasi │ │ └── AppNavigator.js │ ├── services/ ← API service layer │ │ └── api.js │ └── utils/ ← Utility functions │ └── helpers.js ├── App.js ← Entry point aplikasi ├── app.json ← Konfigurasi Expo/RN ├── package.json └── babel.config.js
3. Core Components
React Native menggunakan core components yang memetakan ke native UI platform. Berbeda dengan web yang menggunakan div, span, img — React Native menggunakan View, Text, Image, dll.
| React Native | HTML Equivalent | Fungsi |
|---|---|---|
View | div | Container utama untuk layout |
Text | span / p | Menampilkan teks |
Image | img | Menampilkan gambar |
TextInput | input | Input teks dari user |
ScrollView | div (overflow:auto) | Konten yang bisa di-scroll |
FlatList | Tidak ada | List yang dioptimasi untuk data banyak |
TouchableOpacity | button | Elemen yang bisa diketuk dengan feedback opacity |
Pressable | button | Elemen yang bisa diketuk (lebih fleksibel) |
Component Dasar
import React from 'react';
import {
View,
Text,
Image,
TextInput,
TouchableOpacity,
ScrollView,
FlatList,
ActivityIndicator,
Alert,
} from 'react-native';
// ===== App Utama =====
export default function App() {
const [teks, setTeks] = React.useState('');
const data = [
{ id: '1', nama: 'React Native', kategori: 'Framework' },
{ id: '2', nama: 'Flutter', kategori: 'Framework' },
{ id: '3', nama: 'SwiftUI', kategori: 'Native' },
{ id: '4', nama: 'Jetpack Compose', kategori: 'Native' },
];
return (
<ScrollView style={{ flex: 1, padding: 16 }}>
{/* Text */}
<Text style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 16 }}>
React Native Components
</Text>
{/* Image */}
<Image
source={{ uri: 'https://picsum.photos/300/200' }}
style={{ width: '100%', height: 200, borderRadius: 12, marginBottom: 16 }}
/>
{/* TextInput */}
<TextInput
value={teks}
onChangeText={setTeks}
placeholder="Ketik sesuatu..."
style={{
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
padding: 12,
marginBottom: 16,
fontSize: 16,
}}
/>
{/* TouchableOpacity */}
<TouchableOpacity
onPress={() => Alert.alert('Halo!', `Anda menulis: ${teks}`)}
style={{
backgroundColor: '#2196F3',
padding: 14,
borderRadius: 8,
alignItems: 'center',
marginBottom: 16,
}}
>
<Text style={{ color: 'white', fontSize: 16, fontWeight: 'bold' }}>
Tekan Saya
</Text>
</TouchableOpacity>
{/* FlatList — Optimized list */}
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 8 }}>
Daftar Framework:
</Text>
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{
padding: 12,
backgroundColor: '#f5f5f5',
borderRadius: 8,
marginBottom: 8,
}}>
<Text style={{ fontSize: 16, fontWeight: 'bold' }}>{item.nama}</Text>
<Text style={{ color: '#666' }}>{item.kategori}</Text>
</View>
)}
/>
</ScrollView>
);
}
Gunakan FlatList untuk daftar yang panjang atau data dari API — FlatList hanya me-render item yang terlihat di layar (lazy rendering). Gunakan ScrollView hanya untuk konten yang sedikit. Menggunakan ScrollView dengan ribuan item akan menyebabkan masalah performa.
4. StyleSheet & Flexbox
React Native menggunakan StyleSheet API yang mirip dengan CSS namun ditulis dalam JavaScript. Semua layout menggunakan Flexbox secara default (default direction: column, bukan row seperti di web).
StyleSheet.create()
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
function ProductCard({ nama, harga, gambar }) {
return (
<View style={styles.card}>
<Text style={styles.nama}>{nama}</Text>
<Text style={styles.harga}>Rp {harga.toLocaleString()}</Text>
<TouchableOpacity style={styles.tombol}>
<Text style={styles.tombolText}>Beli</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginVertical: 8,
marginHorizontal: 16,
// Shadow (iOS)
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
// Shadow (Android)
elevation: 4,
},
nama: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
marginBottom: 4,
},
harga: {
fontSize: 16,
color: '#4CAF50',
fontWeight: '600',
marginBottom: 12,
},
tombol: {
backgroundColor: '#2196F3',
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 8,
alignItems: 'center',
},
tombolText: {
color: '#ffffff',
fontSize: 14,
fontWeight: 'bold',
},
});
Flexbox di React Native
import { View, Text, StyleSheet } from 'react-native';
// Flexbox Layout Demo
function FlexboxDemo() {
return (
<View style={styles.container}>
{/* Header — Flex row */}
<View style={styles.header}>
<Text style={styles.logo}>BeebaneLabs</Text>
<Text style={styles.menu}>☰</Text>
</View>
{/* Content — Flex column dengan space-between */}
<View style={styles.content}>
<View style={[styles.box, { backgroundColor: '#FF6B6B' }]} />
<View style={[styles.box, { backgroundColor: '#4ECDC4' }]} />
<View style={[styles.box, { backgroundColor: '#45B7D1' }]} />
</View>
{/* Footer — Flex row dengan evenly distributed */}
<View style={styles.footer}>
<Text style={styles.footerItem}>🏠 Home</Text>
<Text style={styles.footerItem}>🔍 Search</Text>
<Text style={styles.footerItem}>👤 Profile</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1, // Mengisi seluruh layar
},
header: {
flexDirection: 'row', // Horizontal layout
justifyContent: 'space-between', // Spasi antar item
alignItems: 'center', // Vertikal center
padding: 16,
backgroundColor: '#333',
},
logo: { color: '#fff', fontSize: 20, fontWeight: 'bold' },
menu: { color: '#fff', fontSize: 24 },
content: {
flex: 1, // Mengisi sisa ruang
justifyContent: 'space-around', // Spasi merata
alignItems: 'center',
padding: 16,
},
box: {
width: '80%',
height: 80,
borderRadius: 12,
},
footer: {
flexDirection: 'row',
justifyContent: 'space-evenly',
paddingVertical: 12,
backgroundColor: '#f5f5f5',
borderTopWidth: 1,
borderTopColor: '#eee',
},
footerItem: { fontSize: 14 },
});
Di React Native, flexDirection default-nya adalah column (vertikal), bukan row seperti di web CSS. Selain itu, semua dimensi menggunakan angka tanpa satuan (pixel density-independent). Tidak ada display: flex karena semua View sudah flex secara default.
5. Navigasi dengan React Navigation
React Navigation adalah library navigasi paling populer untuk React Native. Library ini menyediakan berbagai jenis navigator: Stack, Tab, Drawer, dan Bottom Tab.
Instalasi React Navigation
# Instalasi dependencies npm install @react-navigation/native @react-navigation/native-stack # Dependencies tambahan npm install react-native-screens react-native-safe-area-context # Untuk bottom tabs npm install @react-navigation/bottom-tabs # Untuk drawer npm install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
Stack Navigator
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Text, View, TouchableOpacity, StyleSheet } from 'react-native';
const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();
// ===== HOME SCREEN =====
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24, marginBottom: 20 }}>🏠 Beranda</Text>
<TouchableOpacity
style={styles.navButton}
onPress={() =>
navigation.navigate('Detail', {
itemId: 42,
itemName: 'Produk React Native',
})
}
>
<Text style={styles.navButtonText}>Buka Detail</Text>
</TouchableOpacity>
</View>
);
}
// ===== DETAIL SCREEN =====
function DetailScreen({ route, navigation }) {
const { itemId, itemName } = route.params;
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24 }}>📋 Detail Produk</Text>
<Text style={{ fontSize: 16, marginTop: 8 }}>ID: {itemId}</Text>
<Text style={{ fontSize: 16 }}>Nama: {itemName}</Text>
<TouchableOpacity
style={styles.navButton}
onPress={() => navigation.goBack()}
>
<Text style={styles.navButtonText}>← Kembali</Text>
</TouchableOpacity>
</View>
);
}
// ===== PROFILE SCREEN =====
function ProfileScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24 }}>👤 Profil</Text>
</View>
);
}
// ===== TAB NAVIGATOR =====
function HomeTabs() {
return (
<Tab.Navigator
screenOptions={{
tabBarActiveTintColor: '#2196F3',
tabBarInactiveTintColor: '#999',
}}
>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{ tabBarIcon: () => <Text>🏠</Text> }}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{ tabBarIcon: () => <Text>👤</Text> }}
/>
</Tab.Navigator>
);
}
// ===== APP UTAMA =====
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: '#2196F3' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' },
}}
>
<Stack.Screen
name="HomeTabs"
component={HomeTabs}
options={{ title: 'Aplikasi Mobile' }}
/>
<Stack.Screen
name="Detail"
component={DetailScreen}
options={{ title: 'Detail Produk' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
navButton: {
backgroundColor: '#2196F3',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
marginTop: 16,
},
navButtonText: { color: '#fff', fontSize: 16, fontWeight: 'bold' },
});
6. State Management
Untuk aplikasi sederhana, useState dan useContext React sudah cukup. Untuk aplikasi yang lebih kompleks, pertimbangkan Zustand, Redux Toolkit, atau Jotai.
useState & useReducer
import React, { useState, useReducer } from 'react';
import { View, Text, TextInput, FlatList, TouchableOpacity } from 'react-native';
// ===== Todo App dengan useState =====
function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (input.trim()) {
setTodos([
...todos,
{ id: Date.now().toString(), text: input, done: false },
]);
setInput('');
}
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<View style={{ flex: 1, padding: 16 }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 16 }}>
📝 Todo List
</Text>
<View style={{ flexDirection: 'row', marginBottom: 16 }}>
<TextInput
value={input}
onChangeText={setInput}
placeholder="Tambah todo..."
style={{
flex: 1,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
padding: 10,
marginRight: 8,
}}
/>
<TouchableOpacity
onPress={addTodo}
style={{
backgroundColor: '#4CAF50',
paddingHorizontal: 20,
borderRadius: 8,
justifyContent: 'center',
}}
>
<Text style={{ color: '#fff', fontWeight: 'bold' }}>+</Text>
</TouchableOpacity>
</View>
<Text style={{ marginBottom: 8, color: '#666' }}>
{todos.filter(t => t.done).length}/{todos.length} selesai
</Text>
<FlatList
data={todos}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{
flexDirection: 'row',
alignItems: 'center',
padding: 12,
backgroundColor: item.done ? '#e8f5e9' : '#fff',
borderRadius: 8,
marginBottom: 8,
borderWidth: 1,
borderColor: '#eee',
}}>
<TouchableOpacity
onPress={() => toggleTodo(item.id)}
style={{ marginRight: 12 }}
>
<Text style={{ fontSize: 20 }}>
{item.done ? '✅' : '⬜'}
</Text>
</TouchableOpacity>
<Text style={{
flex: 1,
fontSize: 16,
textDecorationLine: item.done ? 'line-through' : 'none',
color: item.done ? '#999' : '#333',
}}>
{item.text}
</Text>
<TouchableOpacity onPress={() => deleteTodo(item.id)}>
<Text style={{ fontSize: 18, color: '#f44336' }}>🗑️</Text>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
export default TodoApp;
Global State dengan Context API
import React, { createContext, useContext, useReducer } from 'react';
// ===== CONTEXT & REDUCER =====
const AppContext = createContext();
const initialState = {
user: null,
theme: 'dark',
cart: [],
};
function appReducer(state, action) {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload };
case 'TOGGLE_THEME':
return { ...state, theme: state.theme === 'dark' ? 'light' : 'dark' };
case 'ADD_TO_CART':
return { ...state, cart: [...state.cart, action.payload] };
case 'REMOVE_FROM_CART':
return {
...state,
cart: state.cart.filter((_, i) => i !== action.payload),
};
case 'CLEAR_CART':
return { ...state, cart: [] };
default:
return state;
}
}
// ===== PROVIDER =====
function AppProvider({ children }) {
const [state, dispatch] = useReducer(appReducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
}
// ===== CUSTOM HOOK =====
function useApp() {
const context = useContext(AppContext);
if (!context) throw new Error('useApp must be used within AppProvider');
return context;
}
// ===== PENGGUNAAN DI KOMPONEN =====
function CartScreen() {
const { state, dispatch } = useApp();
return (
<View>
<Text>Keranjang: {state.cart.length} item</Text>
<TouchableOpacity
onPress={() => dispatch({ type: 'CLEAR_CART' })}
>
<Text>Kosongkan Keranjang</Text>
</TouchableOpacity>
</View>
);
}
// Bungkus App dengan Provider:
// <AppProvider><App /></AppProvider>
7. API Calls & Networking
React Native mendukung fetch API secara built-in, dan bisa juga menggunakan axios untuk HTTP requests yang lebih fleksibel.
// ===== API SERVICE =====
const BASE_URL = 'https://jsonplaceholder.typicode.com';
export const api = {
// GET request
async getPosts() {
try {
const response = await fetch(`${BASE_URL}/posts`);
if (!response.ok) throw new Error('Gagal memuat data');
return await response.json();
} catch (error) {
console.error('API Error:', error);
throw error;
}
},
// POST request
async createPost(title, body) {
try {
const response = await fetch(`${BASE_URL}/posts`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, body, userId: 1 }),
});
return await response.json();
} catch (error) {
console.error('API Error:', error);
throw error;
}
},
};
// ===== HOOK UNTUK FETCH DATA =====
function usePosts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchPosts = async () => {
try {
setLoading(true);
const data = await api.getPosts();
setPosts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
useEffect(() => { fetchPosts(); }, []);
return { posts, loading, error, refetch: fetchPosts };
}
// ===== POSTS SCREEN =====
function PostsScreen() {
const { posts, loading, error, refetch } = usePosts();
if (loading) {
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<ActivityIndicator size="large" color="#2196F3" />
</View>
);
}
if (error) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'red', fontSize: 16 }}>Error: {error}</Text>
<TouchableOpacity onPress={refetch}>
<Text style={{ color: '#2196F3', marginTop: 12 }}>Coba Lagi</Text>
</TouchableOpacity>
</View>
);
}
return (
<FlatList
data={posts.slice(0, 20)}
keyExtractor={(item) => item.id.toString()}
refreshing={loading}
onRefresh={refetch}
renderItem={({ item }) => (
<View style={{
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee',
}}>
<Text style={{ fontWeight: 'bold', fontSize: 16 }}>
{item.title}
</Text>
<Text style={{ color: '#666', marginTop: 4 }}>
{item.body.substring(0, 100)}...
</Text>
</View>
)}
/>
);
}
8. Platform Differences
Salah satu tantangan React Native adalah menangani perbedaan antara iOS dan Android. React Native menyediakan API Platform untuk mendeteksi platform dan menulis kode spesifik per platform.
import { Platform, StyleSheet, Alert } from 'react-native';
// ===== DETEKSI PLATFORM =====
const isIOS = Platform.OS === 'ios';
const isAndroid = Platform.OS === 'android';
// ===== PLATFORM-SPECIFIC VALUES =====
const styles = StyleSheet.create({
container: {
paddingTop: Platform.OS === 'ios' ? 44 : 24, // Status bar height
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
},
android: {
elevation: 4,
},
default: {},
}),
},
header: {
...Platform.select({
ios: {
fontFamily: 'Helvetica Neue',
},
android: {
fontFamily: 'Roboto',
},
}),
},
});
// ===== PLATFORM-SPECIFIC FILE EXTENSIONS =====
// Buat 2 file:
// ButtonComponent.ios.js ← untuk iOS
// ButtonComponent.android.js ← untuk Android
// React Native otomatis memilih file yang benar!
import ButtonComponent from './ButtonComponent'; // Auto-resolve
// ===== ALERT YANG BERBEDA =====
function showAlert() {
if (Platform.OS === 'ios') {
Alert.alert(
'Konfirmasi',
'Apakah Anda yakin?',
[
{ text: 'Batal', style: 'cancel' },
{ text: 'Ya', onPress: () => console.log('OK') },
]
);
} else {
Alert.alert(
'Konfirmasi',
'Apakah Anda yakin?',
[
{ text: 'TIDAK', style: 'cancel' },
{ text: 'YA', onPress: () => console.log('OK') },
]
);
}
}
// ===== NAVIGATION HEADER BERBEDA =====
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerStyle: {
backgroundColor: Platform.OS === 'ios' ? '#fff' : '#2196F3',
},
headerTintColor: Platform.OS === 'ios' ? '#2196F3' : '#fff',
headerBackTitle: Platform.OS === 'ios' ? 'Kembali' : undefined,
}}
/>
</Stack.Navigator>
| Aspek | iOS | Android |
|---|---|---|
| Font Default | San Francisco / Helvetica | Roboto |
| Shadow | shadowColor, shadowOffset, dll. | elevation |
| Navigation Back | Geser dari kiri | Tombol back fisik/gesture |
| Tab Bar | Di bawah | Di bawah (Material You) |
| Push Notification | APNs (via FCM/EAS) | FCM |
| Status Bar | Light/Dark content | Light/Dark background |
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang React Native: