Mobile

React Native Dasar: Mobile App dengan JavaScript

TOKEN

Panduan lengkap membangun aplikasi mobile cross-platform dengan React Native — dari core components, StyleSheet, navigasi, state management, API calls, hingga perbedaan platform

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 BersamaHingga 90% kode bisa dibagikan antara Android dan iOS
Native FeelMenggunakan native UI components — tampilan sesuai platform
Fast RefreshPerubahan kode langsung terlihat tanpa full rebuild
Ekosistem BesarDidukung npm — ratusan ribu package JavaScript tersedia
Komunitas AktifJutaan developer, dokumentasi lengkap, banyak tutorial
Meta SupportDigunakan di produk-produk Meta, di-maintain secara aktif
Code PushUpdate aplikasi tanpa melalui App Store / Play Store
Diagram: Arsitektur React Native
┌─────────────────────────────────────────────────────────┐
│  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)

Bash
# 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

Bash
# 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

File Structure
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
ViewdivContainer utama untuk layout
Textspan / pMenampilkan teks
ImageimgMenampilkan gambar
TextInputinputInput teks dari user
ScrollViewdiv (overflow:auto)Konten yang bisa di-scroll
FlatListTidak adaList yang dioptimasi untuk data banyak
TouchableOpacitybuttonElemen yang bisa diketuk dengan feedback opacity
PressablebuttonElemen yang bisa diketuk (lebih fleksibel)

Component Dasar

JavaScript — Core Components
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>
  );
}
💡 FlatList vs 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()

JavaScript — StyleSheet
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

JavaScript — Flexbox Layout
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 },
});
⚠️ Perbedaan Flexbox Web vs React Native

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.

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

Bash
# 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

JavaScript — Navigation Setup
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

JavaScript — State Management
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

JavaScript — 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.

JavaScript — API Service & Fetch
// ===== 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.

JavaScript — Platform-Specific Code
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 DefaultSan Francisco / HelveticaRoboto
ShadowshadowColor, shadowOffset, dll.elevation
Navigation BackGeser dari kiriTombol back fisik/gesture
Tab BarDi bawahDi bawah (Material You)
Push NotificationAPNs (via FCM/EAS)FCM
Status BarLight/Dark contentLight/Dark background

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang React Native:

Pertanyaan 1: Component apa yang digunakan sebagai pengganti div di React Native?

a) Box
b) Container
c) View
d) Wrapper

Pertanyaan 2: Apa default flexDirection di React Native?

a) row
b) column
c) row-reverse
d) column-reverse

Pertanyaan 3: Library apa yang paling umum digunakan untuk navigasi di React Native?

a) React Router
b) React Navigation
c) Expo Router
d) Native Navigation

Pertanyaan 4: Mengapa FlatList lebih disarankan daripada ScrollView untuk daftar panjang?

a) FlatList lebih cantik tampilannya
b) FlatList hanya me-render item yang terlihat (lazy rendering)
c) ScrollView tidak mendukung scroll
d) FlatList bisa menampilkan gambar

Pertanyaan 5: Bagaimana cara mendeteksi platform (iOS/Android) di React Native?

a) navigator.userAgent
b) Platform.OS dari 'react-native'
c) Device.platform
d) React.platform
🔍 Zoom
100%
🎨 Tema