Skip to content

บทที่ 12 — React Native (Mobile Application)

← บทที่ 11 | สารบัญ | บทที่ 13 →

🔴 ระดับ: สูง — อ่านทีหลังได้ บทนี้ออกนอกโลกเว็บไปทำแอปมือถือ ถ้ายังโฟกัสเว็บอยู่ ข้ามไปก่อนได้

📖 คำศัพท์รวมของบท:

  • React Native = เครื่องมือเขียนแอปมือถือ iOS/Android ด้วย React
  • native = ของแท้ของ OS — <Button> ของ iOS/Android โดยตรง (ไม่ใช่ HTML)
  • native module = ฟีเจอร์เครื่อง (camera, GPS, sensor, push) ที่ JS เรียก native code
  • Expo = framework ครอบ React Native ทำให้ใช้ง่าย (ไม่ต้อง Xcode/Android Studio เริ่ม)
  • EAS (Expo Application Services) = cloud service ของ Expo สำหรับ build + submit + OTA (มี free tier)
  • Expo Go = แอปทดสอบบนมือถือ — scan QR แล้วเปิด project ของเราได้ทันที (ใช้ library ที่ Expo bundle ไว้)
  • Dev Build = build แอปของเราเองที่มี native module พิเศษ (ใช้ Firebase, MMKV ฯลฯ) — ใช้แทน Expo Go เมื่อจำเป็น
  • OTA (Over-The-Air) Update = update JS ผ่านอินเตอร์เน็ตโดยที่ user ไม่ต้องไป store download ใหม่
  • safe area = พื้นที่ปลอดภัยที่ notch/status bar/home indicator ไม่บัง
  • bridge = สะพานเก่าที่ส่งคำสั่ง JS ↔ native (ผ่าน JSON)
  • JSI (JavaScript Interface) = สะพานแบบใหม่ที่เร็วกว่า — JS เรียก native ตรงๆ
  • New Architecture = ชุด feature ใหม่ที่ใช้ JSI (Fabric + TurboModules) → เร็วกว่า bridge เดิม — default ตั้งแต่ RN 0.76 (ปี 2024)

💡 dev มือใหม่: Expo จัดการเรื่อง bridge/JSI/New Architecture ให้แล้ว — ไม่ต้องเข้าใจ internals (เบื้องลึก — กลไกภายในของ library/framework) พวกนี้ก็เขียน app ได้ ผ่านบทนี้แล้วค่อยขุดลึกเมื่อจำเป็น

ถ้ารู้ React แล้ว — React Native ทำให้คุณสร้าง app iOS + Android ด้วย JS

บทนี้สอน:

  • React Native ทำงานยังไง (เทียบกับ web)
  • Expo — framework ปี 2026 ที่ทำให้ React Native ใช้ง่าย
  • Component ที่ต่างจาก web
  • Navigation (React Navigation / Expo Router)
  • Style + Layout
  • Native module
  • Deploy ขึ้น store

บทนี้ภาพรวม — เรียนเชิงลึกควรอ่าน docs Expo + React Native


1. React Native คืออะไร

React Native = "เขียน React แต่ render เป็น native UI ของ mobile (UIView ของ iOS, View ของ Android)"

ลองนึกถึงแอป Grab กับเว็บ Grab บนมือถือ — เว็บ Grab คือเบราว์เซอร์ขนาดเล็กที่ฝังอยู่ใน app (WebView) ปุ่มและตัวอักษรเป็นของเบราว์เซอร์ ไม่ใช่ของ iOS หรือ Android โดยตรง ส่วนแอป Grab จริงๆ ใช้ปุ่มของ iOS/Android โดยตรง — กดแล้วรู้สึกต่างกัน มีแรงสั่นสะเทือน (haptic) ตอบสนองแบบที่ระบบออกแบบมา ผิวสัมผัสเป็นของ native จริงๆ นั่นแหละคือ native UI React Native สร้าง UI แบบหลัง — ไม่ใช่เว็บที่ฝังในแอป แต่เป็น UI ของระบบจริงๆ (เบื้องหลังคือ UIView ของ iOS / View ของ Android — คลาสพื้นฐานสุดที่ native app ทุกตัวถูกสร้างจาก ไม่ต้องจำหรือแตะตรงนี้เอง แค่รู้ไว้ว่ามันคือรากฐานที่ React Native ไปเกาะอยู่)

text
React Web              React Native
─────────              ────────────
<div>          ↔       <View>
<span>         ↔       <Text>
<img>          ↔       <Image>
<button>       ↔       <Pressable>
<input>        ↔       <TextInput>
CSS            ↔       StyleSheet (object)

ไม่ใช่ "หุ้ม WebView (component ที่ฝัง browser เล็ก ๆ ใน app — เหมือนเปิดเว็บอยู่ในแอป)" — render เป็น native จริง ๆ → performance ดี + look-and-feel native (หน้าตาและการใช้งานเหมือน app ของระบบเอง)


2. React Native vs Flutter vs Native

React NativeFlutterNative (Swift/Kotlin)
LanguageJavaScript/TSDartSwift / Kotlin
Code reuseiOS + Android + Web (~80%)iOS + Android + Webแต่ละ platform แยก
Performanceดีดีกว่าดีที่สุด
วิธีคุยกับ OS (JS ↔ native)ผ่านตัวกลาง (ช้ากว่านิดหน่อย)วาด UI เอง (ไม่ต้องคุยกับ OS)ตรง (เร็วที่สุด)
Learning curve (สำหรับ React dev)⭐ ต่ำมากสูง (ภาษาใหม่)สูงมาก
Hot reload
Communityใหญ่กลาง-ใหญ่ใหญ่

ถ้าคุณรู้ React อยู่แล้ว → React Native = ทางเลือกที่ไว้ใจได้


Part 1: Setup with Expo

3. ทำไมใช้ Expo

Expo = framework + tooling รอบ React Native:

  • ไม่ต้องตั้ง Xcode / Android Studio ตั้งแต่ต้น
  • มี "Expo Go" app — scan QR → run app บนมือถือทันที
  • มี module สำเร็จ (camera, location, sensors, ...)
  • ปี 2026 = standard ใช้ React Native (ตรวจ SDK ล่าสุดได้ที่ expo.dev)

⭐ React Native New Architecture (default ตั้งแต่ RN 0.76)

💡 dev มือใหม่: Expo จัดการเรื่อง New Architecture ให้แล้วทั้งหมด — ไม่ต้องเข้าใจรายละเอียดข้างล่างก็เขียน app ได้ปกติ อ่านผ่านตาไว้รู้จักชื่อก็พอ

ปี 2026 React Native ใช้ New Architecture เป็น default วิธีเข้าใจง่ายที่สุดคือเปรียบวิธีที่ JS คุยกับระบบ native ของเครื่อง แบบเก่าเหมือน ฝากคนกลางแปลจดหมาย — JS เขียนคำสั่งเป็น JSON ส่งให้ bridge แปลงแล้วส่งต่อให้ native ซึ่งใช้เวลา แบบใหม่เหมือน โทรหาตรง — JS คุยกับ native โดยตรง ได้คำตอบทันที ไม่มีตัวกลาง

แบบเก่าแบบใหม่
วิธีคุย JS ↔ nativeส่งผ่านตัวกลาง (JSON ไปมา) — ช้าคุยตรง — เร็ว
ผลที่เห็นช้ากว่า (พอใช้ได้)เร็วกว่า เห็นได้ชัดในงานหนัก

ผลกระทบกับ developer:

  • ส่วนใหญ่ของ code เก่ายังใช้ได้ (Expo จัดการ compat)
  • บาง 3rd-party library ที่ไม่ update → ต้องใช้ Old Architecture (มี flag)
  • รองรับ concurrent React (React โหมดทำงานคู่ขนาน — interrupt update ได้) เต็มที่ (useTransition, useDeferredValue ทำงานเหมือน web)

Prerequisites: ต้องลง Node.js ก่อน (ดาวน์โหลดจาก nodejs.org) — npm และ npx จะมาพร้อมกัน npx = คำสั่งรัน package โดยไม่ต้องลงก่อน ต่างจาก npm install ตรงที่ดาวน์โหลดมารันแล้วลบทิ้ง

bash
# template default (TypeScript + expo-router)
npx create-expo-app@latest my-app

# หรือเลือก template แบบ blank ถ้าอยากเริ่มเปล่า
npx create-expo-app@latest my-app --template blank-typescript

⚠️ -t flag เฉยๆ ไม่ได้ — ต้องใส่ชื่อ template ตามหลัง (--template blank-typescript) หรือไม่ใส่เลย (จะได้ default ที่มี expo-router + TypeScript ครบ) ชื่อ template อาจเปลี่ยนไปตาม SDK version — เช็ครายชื่อ template ที่ใช้ได้จริงด้วย npx create-expo-app --template (ไม่ใส่ค่าตามหลัง จะขึ้น list ให้เลือก)

bash
cd my-app          # เข้าโฟลเดอร์ที่สร้างใหม่
npx expo start     # รันใน terminal (VS Code terminal, PowerShell, หรือ Command Prompt ก็ได้)

เมื่อรันแล้วจะเห็น QR code ขึ้นใน terminal และ browser เปิดหน้า Expo DevTools อัตโนมัติ → Scan QR ด้วย Expo Go app (ค้นหาว่า "Expo Go" ใน App Store หรือ Google Play Store) → เห็น app ทันที!

💡 Expo Go vs Development Build — เริ่มต้นใช้ Expo Go พอ (ครอบคลุม Checkpoint 12.1-12.3 ทั้งหมด)

Expo Go ใช้ได้แค่ library ที่ Expo bundle (รวมไว้ใน app) ไว้แล้ว (camera, location, async storage — ครบสำหรับ app ทั่วไป) ถ้าต้อง native module ที่ไม่อยู่ใน Expo Go (react-native-firebase, MMKV, library ที่มี custom native code = โค้ดเฉพาะของ platform เช่น Swift/Kotlin) → ต้องใช้ Development Build แทน (ละเอียดในส่วน Deploy):

bash
npx expo install expo-dev-client
eas build --profile development --platform ios   # หรือ android

หลังจาก build เสร็จ ติดตั้ง .ipa/.apk ลงเครื่อง → npx expo start --dev-client ก็จะใช้ Development Build แทน Expo Go (ได้ทุก native module + ยัง hot reload)


4. โครงสร้าง Expo (App Router)

Expo รุ่นใหม่ใช้ file-based routing — ชื่อไฟล์ในโฟลเดอร์ app/ กลายเป็น route อัตโนมัติ: index.tsx = หน้าแรก, about.tsx = หน้า /about, _layout.tsx = layout ของกลุ่มหน้านั้น ไม่ต้องเขียน route config เอง (คล้าย Next.js สำหรับคนที่รู้ Next.js อยู่แล้ว):

text
my-app/
├── app/                    ← file-based routing (เหมือน Next.js)
│   ├── _layout.tsx         ← root layout
│   ├── index.tsx           ← / (home)
│   └── (tabs)/             ← tab navigation
│       ├── _layout.tsx
│       ├── home.tsx
│       └── settings.tsx
├── components/
├── assets/
└── app.json                ← config

Part 2: Component พื้นฐาน

5. View + Text + StyleSheet

หมายเหตุ: ไฟล์ React Native ใช้นามสกุล .tsx = TypeScript + JSX (ถ้าอยากรู้เรื่อง TypeScript เพิ่มเติมดูบท 13) ถ้าไม่ใช้ TypeScript ใช้ .jsx แทนได้

tsx
import { View, Text, StyleSheet } from 'react-native';

export default function Home() {
    return (
        <View style={styles.container}>
            <Text style={styles.title}>Hello, React Native!</Text>
            <Text style={styles.subtitle}>From your phone</Text>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
        padding: 16,
    },
    title: {
        fontSize: 24,
        fontWeight: 'bold',
        marginBottom: 8,
    },
    subtitle: {
        fontSize: 14,
        color: '#666',
    },
});

สำคัญ:

  • ทุก text ต้องอยู่ใน <Text> (ไม่มี text node ลอย ๆ)
  • ไม่มี <div>, ไม่มี <span> — ใช้ <View>, <Text>
  • Style = object (camelCase) ไม่ใช่ CSS string
  • ไม่มี cascading (ไม่มีการสืบทอด style จาก parent ไป child — ใน CSS ของเว็บ ถ้าตั้ง color: red ที่ <body> ลูกทุกตัวจะได้สีแดงตามอัตโนมัติ แต่ใน React Native ต้องส่ง style ให้แต่ละ component เอง ตั้งที่ parent แล้วลูกไม่ได้รับ)

📖 cascading = การสืบทอด style จาก parent → child โดยอัตโนมัติ (มีใน CSS web, ไม่มีใน React Native)

  • Default flexDirection: 'column' (ต่างจาก web)

6. Layout — Flexbox

React Native จัด layout ด้วย flexbox เหมือน web แต่มีจุดต่างที่ต้องระวัง — default flexDirection เป็น column (web เป็น row), flex: 1 หมายถึง "กินพื้นที่ที่เหลือ" และไม่มี grid ความเข้าใจผิดเรื่อง default direction ทำให้ layout เพี้ยนบ่อยตอนมาจาก web:

tsx
<View style={{ flex: 1, flexDirection: 'row', gap: 8 }}>
    <View style={{ flex: 1, backgroundColor: 'red' }} />
    <View style={{ flex: 2, backgroundColor: 'blue' }} />
    <View style={{ flex: 1, backgroundColor: 'green' }} />
</View>

7. Image

แสดงรูปด้วย <Image> ที่รับ source ได้ทั้ง remote ({ uri }) และ local (require) — จุดต่างจาก web คือต้องระบุ width/height เสมอ (ไม่มี auto จาก HTML) ปี 2026 นิยม expo-image ที่เร็วกว่าและมี cache + blurhash placeholder:

tsx
import { Image } from 'react-native';

// Remote
<Image
    source={{ uri: 'https://example.com/photo.jpg' }}
    style={{ width: 200, height: 200 }}
/>

// Local
<Image source={require('./assets/logo.png')} style={{ width: 100, height: 100 }} />

ปี 2026 ใช้ expo-image (เร็วกว่า + cache + blurhash)


8. Touchable / Pressable — แทน button

tsx
import { Pressable, Text } from 'react-native';

<Pressable
    onPress={() => alert('clicked')}
    style={({ pressed }) => ({
        backgroundColor: pressed ? '#0050cc' : '#0066ff',
        padding: 12,
        borderRadius: 8,
    })}
>
    <Text style={{ color: 'white', textAlign: 'center' }}>Click me</Text>
</Pressable>

Pressable = แทน Touchable* ทุกชนิด (TouchableOpacity = กด-แล้วจาง, TouchableHighlight, TouchableNativeFeedback, TouchableWithoutFeedback) ซึ่งทั้งหมดถือเป็น legacy ใน RN ปัจจุบัน — Pressable รองรับ feedback ละเอียดกว่าและใช้งานได้ในทุกกรณี


9. TextInput

TextInput คือ input ของ React Native — ใช้ value + onChangeText (ไม่ใช่ onChange แบบ web) จุดสำคัญคือ keyboardType ที่เลือก keyboard ให้เหมาะกับข้อมูล (email/numeric/phone) ช่วย UX บนมือถือมาก และ secureTextEntry สำหรับ password:

tsx
import { TextInput, View } from 'react-native';
import { useState } from 'react';

export default function NameInput() {
    const [text, setText] = useState('');

    return (
        <View>
            <TextInput
                value={text}
                onChangeText={setText}
                placeholder="Enter name"
                style={{ borderWidth: 1, padding: 8, borderRadius: 4 }}
                keyboardType="default"                 // ใช้ "default" สำหรับ text ทั่วไป, "numeric"/"email-address"/"phone-pad" เมื่อรับข้อมูลชนิดนั้น
                autoCapitalize="none"
                secureTextEntry={false}                // true = password
            />
        </View>
    );
}

10. ScrollView vs FlatList

tsx
// ScrollView — เลื่อนได้ แต่ render ทุก item (ใช้กับ list สั้น < 50 รายการ)
import { ScrollView } from 'react-native';

function ItemList({ items }: { items: Array<{ id: number }> }) {
    return (
        <ScrollView>
            {items.map(item => <Item key={item.id} {...item} />)}
        </ScrollView>
    );
}

⚠️ ScrollView render ทุก item พร้อมกัน — ใช้กับ list <50 ตัว

tsx
// FlatList — virtualized (render เฉพาะ item ที่มองเห็นบนจอ ส่วนที่เลื่อนพ้นไปจะถูกลบออกชั่วคราว)
// → ประหยัด memory ทำให้ list ยาวมาก ๆ ไม่กระตุก
import { FlatList } from 'react-native';

// items, loadMore, isRefreshing, refresh มาจาก state/props ของ component
function ItemList({ items, loadMore, isRefreshing, refresh }: {
    items: Array<{ id: number }>;
    loadMore: () => void;
    isRefreshing: boolean;
    refresh: () => void;
}) {
    return (
        <FlatList
            data={items}
            keyExtractor={item => String(item.id)}
            renderItem={({ item }) => <Item {...item} />}
            onEndReached={loadMore}
            refreshing={isRefreshing}
            onRefresh={refresh}
        />
    );
}

FlatList = render เฉพาะที่เห็น + pull-to-refresh + infinite scroll


11. SafeAreaView — handle notch + status bar

มือถือสมัยใหม่มี notch, status bar และ home indicator ที่อาจบังเนื้อหา — SafeAreaView ครอบเนื้อหาให้อยู่ในพื้นที่ปลอดภัย (เว้นขอบให้พ้นส่วนเหล่านั้น) อัตโนมัติตามอุปกรณ์ ลืมใช้แล้วข้อความจะโดน notch บังบน iPhone:

หมายเหตุ: react-native-safe-area-context มาพร้อม Expo SDK แล้ว ไม่ต้อง install เพิ่ม แต่ต้องครอบ root ของ app ด้วย <SafeAreaProvider> ใน _layout.tsx ก่อนใช้งาน SafeAreaView ถึงจะทำงานถูกต้อง

tsx
// app/_layout.tsx — ต้องใส่ SafeAreaProvider ที่ root ก่อน
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function RootLayout() {
    return <SafeAreaProvider>{/* ... */}</SafeAreaProvider>;
}
tsx
import { SafeAreaView } from 'react-native-safe-area-context';

<SafeAreaView style={{ flex: 1 }}>
    <Text>เนื้อหาไม่โดน notch บัง</Text>
</SafeAreaView>

iPhone X+ มี notch — ใช้ SafeAreaView ครอบ


Part 3: Navigation

12. Expo Router (file-based)

navigation ใน React Native เคยต้อง config เอง (React Navigation) แต่ Expo Router ทำให้ใช้ file-based routing เหมือน Next.js — ไฟล์ใน app/ กลายเป็น route, [id].tsx คือ dynamic route, _layout.tsx คือ layout เขียนน้อยลงและคุ้นมือคนที่รู้ Next:

ก่อนอื่น ทำไม mobile app ถึงต้องมีระบบ navigation แยกต่างหาก? ลองนึกดูว่าถ้าใช้ if/else render component ธรรมดา เมื่อกดปุ่มกลับ (back) บน Android หรือปัดจากขอบซ้ายบน iOS ระบบจะไม่รู้ว่า "หน้าก่อนหน้า" คืออะไร เพราะ if/else ไม่ได้เก็บประวัติไว้

Stack Navigator คือกองจาน — เปิดหน้าใหม่ = วางจานใหม่บนกอง, กด Back = เอาจานบนสุดออก ระบบจึงรู้ว่าต้อง slide กลับไปที่ไหน และ animation เข้า-ออกก็ทำงานถูกต้อง

Expo Router ห่อ Stack Navigator นั้นไว้ภายใน แล้วให้เราแค่สร้างไฟล์ — ชื่อไฟล์กลายเป็นชื่อหน้าโดยอัตโนมัติ

ใหม่ + ใช้ง่าย — เหมือน Next.js

text
app/
├── _layout.tsx              ← root layout
├── index.tsx                ← /
├── about.tsx                ← /about
├── posts/
│   ├── index.tsx            ← /posts
│   └── [id].tsx             ← /posts/123
└── (tabs)/
    ├── _layout.tsx          ← tab navigator
    ├── home.tsx
    └── settings.tsx

Layout — Stack Navigator

tsx
// app/_layout.tsx
import { Stack } from 'expo-router';

export default function RootLayout() {
    return (
        <Stack>
            <Stack.Screen name="index" options={{ title: 'Home' }} />
            <Stack.Screen name="about" options={{ title: 'About' }} />
        </Stack>
    );
}

Tab Layout

tsx
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';

export default function TabsLayout() {
    return (
        <Tabs>
            <Tabs.Screen name="home" options={{ title: 'Home' }} />
            <Tabs.Screen name="settings" options={{ title: 'Settings' }} />
        </Tabs>
    );
}
tsx
import { Link, router } from 'expo-router';

<Link href="/about">Go to About</Link>
<Link href={`/posts/${post.id}`}>{post.title}</Link>

// programmatic
router.push('/about');
router.replace('/login');
router.back();

Dynamic Route

tsx
// app/posts/[id].tsx
import { useLocalSearchParams } from 'expo-router';

export default function PostDetail() {
    const { id } = useLocalSearchParams();
    return <Text>Post {id}</Text>;
}

13. React Navigation (alternative)

ทำไมสอน React Navigation ทั้งที่แนะนำ Expo Router? — เพราะ codebase เก่าในงานจริงส่วนใหญ่ยังใช้ React Navigation อยู่ การอ่านและแก้โค้ดเก่าต้องรู้จัก pattern นี้ สำหรับ project ใหม่ใช้ Expo Router ดีกว่า

ก่อน Expo Router — มาตรฐานคือ @react-navigation/native

tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

<NavigationContainer>
    <Stack.Navigator>
        <Stack.Screen name="Home" component={Home} />
        <Stack.Screen name="Detail" component={Detail} />
    </Stack.Navigator>
</NavigationContainer>

ปี 2026 — Expo Router ดีกว่าสำหรับ project ใหม่


Part 4: Style Library

14. NativeWind — Tailwind สำหรับ React Native

Optional section — เหมาะสำหรับคนที่รู้ Tailwind CSS อยู่แล้ว ถ้ายังไม่รู้ Tailwind ข้ามไปก่อนได้

Tailwind CSS = utility class library สำหรับ CSS — แทนที่จะเขียน { color: 'blue', padding: 16 } ก็ใช้ class สั้น ๆ เช่น text-blue-500 p-4 แทน ทำให้ style เร็วและ consistent NativeWind นำ Tailwind มาใช้ใน React Native ด้วย prop className:

ถ้าคุ้นกับ Tailwind จาก web ก็ใช้ต่อใน React Native ได้ด้วย NativeWind — เขียน className แบบเดียวกัน ทำให้ style consistent ระหว่าง web กับ mobile และไม่ต้องเขียน StyleSheet object ยาว ๆ:

bash
npm install nativewind tailwindcss

⚠️ NativeWind ต้องการ setup เพิ่มเติม — ไม่ใช่แค่ install แล้วใช้ได้เลย ต้องตั้งค่า babel.config.js, metro.config.js, tailwind.config.js และ import global CSS ใน layout ด้วย ดู setup ครบที่ nativewind.dev/getting-started/expo-router เพราะ setup อาจเปลี่ยนตาม Expo SDK version

tsx
<View className="flex-1 items-center justify-center bg-white">
    <Text className="text-2xl font-bold mb-2">Hello!</Text>
    <Pressable className="bg-blue-500 px-4 py-2 rounded">
        <Text className="text-white">Click</Text>
    </Pressable>
</View>

ใช้ class Tailwind ได้ — ดี + consistent กับ web


15. Tamagui

อีกทางเลือกคือ Tamagui — UI kit + style system ที่ออกแบบมาเพื่อ React Native โดยเฉพาะ มี compiler optimize style ตอน build จึง render เร็วกว่า NativeWind และมาพร้อม component สำเร็จ เหมาะถ้าต้องการ performance สูงและ component library ในตัว:

UI kit + style system ที่ optimize สำหรับ React Native (เร็วกว่า NativeWind)

tsx
// Tamagui shorthand props: ai = alignItems, jc = justifyContent, f = flex
<YStack ai="center" jc="center" f={1}>
    <H1>Hello</H1>
    <Button onPress={() => console.log('clicked')}>Click</Button>
</YStack>

Part 5: API + State

16. Fetch + TanStack Query

ข่าวดี: data fetching ใน React Native ใช้ TanStack Query เหมือน web เป๊ะ — โค้ด query/cache เหมือนกันทุกอย่าง ต่างแค่ component ที่แสดงผล (<FlatList>, <ActivityIndicator> แทน HTML) ทำให้ logic ฝั่ง data reuse ข้าม platform ได้:

bash
npm install @tanstack/react-query

ต้องครอบ app ด้วย <QueryClientProvider> ก่อนใช้งาน (เหมือนบท web — ถ้ายังไม่คุ้นดูบทที่สอน TanStack Query ก่อน):

tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
export default function App() {
    return <QueryClientProvider client={queryClient}>{/* ... */}</QueryClientProvider>;
}

ใช้เหมือน web:

tsx
import { useQuery } from '@tanstack/react-query';
import { ActivityIndicator, FlatList, Text } from 'react-native';

type User = { id: number; name: string };

function UserList() {
    const { data, isLoading } = useQuery<User[]>({
        queryKey: ['users'],
        queryFn: () => fetch('https://api.example.com/users').then(r => r.json())
    });
    
    if (isLoading) return <ActivityIndicator />;
    
    return (
        <FlatList
            data={data}
            keyExtractor={u => String(u.id)}
            renderItem={({ item }) => <Text>{item.name}</Text>}
        />
    );
}

17. AsyncStorage / MMKV — เก็บ data ใน device

useState เก็บข้อมูลในหน่วยความจำ (RAM) — ปิดแอปแล้วข้อมูลหายทันที ลองนึกว่าเขียนโน้ตบนกระดาษทราย แค่พัดลมเป่าก็หาย AsyncStorage คือกล่องเก็บของในมือถือที่ยังอยู่แม้ปิดแอปและรีสตาร์ทเครื่อง คล้าย localStorage ของเว็บ แต่ API เป็น async เพราะอ่านจาก disk (ช้ากว่า RAM) จึงต้องรอด้วย await ลองนึกตัวอย่าง: เก็บชื่อ user ไว้ใน AsyncStorage — ปิดแอปแล้วเปิดใหม่ ชื่อยังอยู่ ไม่ต้องล็อกอินใหม่

AsyncStorage (basic):

tsx
import AsyncStorage from '@react-native-async-storage/async-storage';

await AsyncStorage.setItem('token', 'xyz');
const token = await AsyncStorage.getItem('token');
await AsyncStorage.removeItem('token');

react-native-mmkv = แนะนำปี 2026 — sync API + เร็วกว่า AsyncStorage 30 เท่า

⚠️ MMKV ต้องใช้ Development Build เท่านั้น — ใช้กับ Expo Go ไม่ได้ เพราะมี native module พิเศษ ถ้ายังใช้ Expo Go อยู่ให้ใช้ AsyncStorage ไปก่อน

tsx
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV();

storage.set('token', 'xyz');               // sync!
const token = storage.getString('token');  // sync!
storage.delete('token');

สำหรับ token + auth — ใช้ expo-secure-store (encrypted = เข้ารหัส — ข้อมูลถูกแปลงให้อ่านไม่ออกถ้าไม่มีกุญแจ) ด้วย Keychain ของ iOS / Keystore ของ Android (ระบบเก็บ secret ของแต่ละ OS)


Part 6: Native Features

18. Camera + Photo

จุดเด่นของ mobile คือเข้าถึง native feature ได้ — Expo มี module สำเร็จให้เรียกใช้ง่าย เริ่มจากกล้อง/รูป: expo-image-picker เปิด gallery หรือกล้องให้ user เลือกรูป (จัดการ permission ให้แล้ว) คืน uri มาใช้ต่อ:

bash
npx expo install expo-image-picker
tsx
import * as ImagePicker from 'expo-image-picker';
import { useState } from 'react';

const [image, setImage] = useState<string | null>(null);

async function pickImage() {
    const result = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ['images'],   // SDK 51+ syntax (เดิม MediaTypeOptions.Images — deprecated)
        allowsEditing: true,
        quality: 0.8,
    });
    if (!result.canceled) {
        setImage(result.assets[0].uri);
    }
}

⚠️ API ของ mediaTypes เปลี่ยนหลายครั้งตาม Expo SDK version (จาก MediaTypeOptions.Images → array of strings อย่างที่เห็นด้านบน) — ถ้าโค้ดพัง ให้ตรวจสอบ syntax ล่าสุดตาม SDK version ที่ใช้จริงใน expo-image-picker docs

19. Location

เข้าถึงตำแหน่ง GPS ด้วย expo-location — จุดสำคัญคือต้องขอ permission จาก user ก่อน (requestForegroundPermissionsAsync) แล้วค่อยอ่านพิกัด นี่เป็น pattern ของทุก native feature: ขอสิทธิ์ → เช็ค status → ใช้งาน:

bash
npx expo install expo-location
tsx
import * as Location from 'expo-location';
import { Alert } from 'react-native';

const { status } = await Location.requestForegroundPermissionsAsync();
if (status === 'granted') {
    const location = await Location.getCurrentPositionAsync({});
    console.log(location.coords.latitude, location.coords.longitude);
} else {
    Alert.alert('ต้องการสิทธิ์ GPS', 'ไปเปิดใน Settings > แอปของคุณ > Location');
}
  • pattern นี้เหมือนกันทุก native feature — camera, microphone, contacts — ถ้า status !== 'granted' ต้อง fallback หรือแจ้ง user เสมอ อย่าปล่อยให้ app เงียบหายไปเฉยๆ เพราะ user จะไม่รู้ว่าเกิดอะไรขึ้น

20. Push Notification

push notification เป็นฟีเจอร์ที่ mobile ทำได้แต่ web ทำยาก ฝั่ง iOS ใช้ระบบชื่อ APNs (Apple Push Notification service — บริการส่ง notification ของ Apple) ส่วนฝั่ง Android ใช้ FCM (Firebase Cloud Messaging — บริการส่ง notification ของ Firebase ซึ่งเป็นบริการ backend สำเร็จรูปของ Google) ทั้งสองระบบนี้ setup เองยุ่งพอสมควร Expo จึงมี push service ฟรีที่ห่อความยุ่งของทั้งคู่ไว้ ให้ส่ง notification ข้าม platform ผ่าน API เดียว:

ลองนึก push notification เหมือนระบบไปรษณีย์ถึงบ้าน — มือถือของ user ต้องแจ้ง Expo server ก่อนว่า "บ้านฉันอยู่ที่นี่" นั่นคือ Expo push token (รหัสประจำเครื่องของ user คนนั้น) จากนั้น server ของคุณส่ง notification ผ่าน Expo ไปถึง APNs/FCM ซึ่งส่งถึงมือถือปลายทาง ขั้นตอนแรกที่ต้องทำเสมอคือขอ permission แล้วรับ token:

tsx
import * as Notifications from 'expo-notifications';

// ขอ permission ก่อน — ถ้าไม่ขอ notification จะไม่แสดงบน iOS
const { status } = await Notifications.requestPermissionsAsync();

// รับ token ประจำเครื่องของ user คนนี้
const token = await Notifications.getExpoPushTokenAsync();

// ส่ง token ไปเก็บที่ server ของคุณ เพื่อใช้ส่ง notification ในอนาคต
console.log(token.data); // "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"
bash
npx expo install expo-notifications

ใช้ Expo's push service (ฟรี) — handle iOS + Android ในที่เดียว


21. Other Expo Modules

Moduleทำอะไร
expo-cameraถ่ายรูป
expo-hapticsสั่น (haptic feedback)
expo-sensorsaccelerometer, gyroscope
expo-secure-storeencrypted storage (token, password)
expo-file-systemอ่าน/เขียน file ใน app sandbox
expo-sharingshare content ไป app อื่น
expo-linkingdeep link
expo-localizationi18n
expo-avaudio + video

Part 7: Deploy

22. Build with EAS

การ build app native ปกติต้องมีเครื่องมือเฉพาะ (Xcode บน Mac สำหรับ iOS) — EAS Build คือบริการ cloud ของ Expo ที่ build ให้บน server ทำให้ build iOS ได้แม้ไม่มี Mac แค่สั่งจาก CLI:

Prerequisites: ต้องสมัครบัญชีที่ expo.dev ก่อน (eas login จะล้มเหลวถ้าไม่มี account)

bash
npm install -g eas-cli    # -g = global install (ลงครั้งเดียว ใช้ได้ทุก project)
eas login
eas build --platform ios
eas build --platform android

EAS Build = cloud service ของ Expo ที่ build ให้ — ไม่ต้องมี Mac สำหรับ iOS


23. Submit to Store

หลัง build เสร็จก็ส่งขึ้น store ด้วย eas submit ที่ upload ให้อัตโนมัติ — แต่ต้องมีบัญชี developer ก่อน (Apple $99/ปี, Google $25 ครั้งเดียว) นี่เป็นต้นทุนคงที่ที่ต้องวางแผนก่อนปล่อย app จริง:

bash
eas submit --platform ios
eas submit --platform android

ต้องมี:

  • iOS: Apple Developer account ($99/year ≈ 3,600 บาท ตามอัตราปัจจุบัน — ชำระผ่านบัตรเครดิต/เดบิต international)
  • Android: Google Play account ($25 one-time ≈ 900 บาท ชำระครั้งเดียว)

24. OTA Update — เปลี่ยน JS โดยไม่ต้อง submit ใหม่

bash
eas update

User เปิด app ครั้งต่อไป → download JS bundle ใหม่ → app เปลี่ยน

ไม่ต้องรอ Apple review (1-2 วัน) — perfect สำหรับ fix bug ด่วน

⚠️ ใช้ได้กับ JS only — ถ้าเปลี่ยน native module ต้อง build + submit ใหม่

⚠️ ข้อจำกัด Apple: OTA update ใช้ได้กับ bug fix และ content update เท่านั้น — ห้ามใช้เปลี่ยน core functionality ของ app อย่างมีนัยสำคัญ (ผิด App Store Review Guidelines 2.5.2 อาจโดน reject หรือ remove ออกจาก store) Expo EAS Update ระบุข้อจำกัดนี้ใน docs ของตัวเองด้วย


Part 8: ⚠️ React Native vs Web — สิ่งที่ต่าง

WebReact Native
HTML element<div>, <p>, <button><View>, <Text>, <Pressable>
CSSstringobject
<Text> ต้องครอบทุก textไม่✅ ใช่
Default flex directionrowcolumn
Cascade styleใช่ไม่
Hoverใช่ไม่ (mobile)
RoutingReact RouterExpo Router / React Navigation
localStorageใช่AsyncStorage
Window / Documentใช่ไม่
CSS animationใช่Reanimated / Moti

25. Animation — Reanimated

animation บน mobile ต้องลื่น 60fps

ทำไมต้องใช้ Reanimated? — React Native มี 2 thread:

  • JS thread = รัน JavaScript ของเรา (logic, state, API call)
  • UI thread = native ใช้วาดหน้าจอ (ต้องว่างเสมอเพื่อให้ลื่น)

ถ้า animate บน JS thread แล้ว JS thread busy (กำลัง fetch data ฯลฯ) → animation กระตุก Reanimated ย้าย animation ไปรันบน UI thread โดยตรง → ลื่น 60fps แม้ JS thread ยุ่ง

ถ้าใช้ useState เลื่อนกล่องจะเกิดอะไร? — setState → React re-render → JS thread คำนวณ style ใหม่ → ส่งผลไปยัง UI thread ทุก frame แต่ถ้า JS thread กำลังยุ่งอยู่ (เช่น fetch ข้อมูล) frame ถัดไปก็ต้องรอ ผลคือกระตุก useSharedValue ต่างตรงที่ค่านี้ "อาศัยอยู่" บน UI thread ตลอดเวลา JS thread ส่งคำสั่ง "เคลื่อนไป 150" ครั้งเดียวแล้วจบ UI thread จัดการ interpolate ทุก frame เองโดยไม่ต้องรอ JS อีก ยุ่งแค่ไหนก็ไม่กระทบ

ใช้แค่ 3 hook: useSharedValue (เก็บค่าที่ animate) + useAnimatedStyle (แปลงเป็น style) + withSpring/withTiming (ทำให้เด้ง/ลื่น):

bash
npx expo install react-native-reanimated
tsx
import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';

function Box() {
    const offset = useSharedValue(0);
    
    const animatedStyle = useAnimatedStyle(() => ({
        transform: [{ translateX: offset.value }],
    }));
    
    return (
        <>
            <Animated.View style={[styles.box, animatedStyle]} />
            <Pressable onPress={() => { offset.value = withSpring(Math.random() * 200); }}>
                <Text>Move</Text>
            </Pressable>
        </>
    );
}

Reanimated run animation บน UI thread → smooth 60 FPS แม้ JS thread busy


26. ⚠️ Common Pitfalls

Pitfallแก้
Text นอก <Text>wrap ใน <Text>
Style เป็น string "color: red"object { color: 'red' }
ScrollView กับ list ใหญ่ใช้ FlatList
ไม่ handle SafeAreaใช้ SafeAreaView หรือ useSafeAreaInsets()
Sync API ของ web (localStorage)ใช้ async (AsyncStorage)
Animation บน JS threadใช้ Reanimated (UI thread)
Build แบบเดียวสำหรับ iOS + Androidtest 2 platform — ต่างกัน
Skip permission requestต้อง request ก่อนใช้ camera/location

27. Checkpoint

📝 หมายเหตุ: checkpoint บทนี้ ตั้งใจไม่มีเฉลย ให้ลองทำเองก่อน ถ้าติดให้ย้อนไปดูตัวอย่างโค้ดในบทแล้วดัดแปลง

🛠️ Checkpoint 12.1 — Hello Native
สร้าง Expo app → run บนมือถือ → แสดง "Hello, [your name]"
เพิ่ม Pressable ที่ alert เมื่อกด

🛠️ Checkpoint 12.2 — Note App

  • หน้า list ของ note (FlatList)
  • หน้า add (TextInput + save)
  • เก็บใน AsyncStorage
  • ลบได้ด้วย swipe (เพิ่ม react-native-gesture-handler — ต้องใส่ import 'react-native-gesture-handler' ที่ไฟล์ root ก่อน และใช้ร่วมกับ Swipeable component หรือดู docs สำหรับ swipe-to-delete)

🛠️ Checkpoint 12.3 — Camera

  • กดถ่ายรูป → preview
  • save ลง gallery
  • มี gallery list ของรูปที่ถ่าย

🛠️ Checkpoint 12.4 — Build to Store
ใช้ EAS build APK สำหรับ Android → install ลงมือถือเอง (ไม่ต้อง submit Play Store ก็ได้)


28. สรุปบท

✅ React Native = "เขียน React → render native UI" (ไม่ใช่ WebView)
Expo = framework ที่ทำให้ React Native ใช้ง่ายปี 2026
Expo Go = QR scan → run บนมือถือทันที (เร็ว, ไม่ต้อง Xcode/Android Studio)
✅ Component ต่าง web: <View>, <Text>, <Pressable>, <TextInput>, <Image>, <FlatList>, <SafeAreaView>
✅ Style = object (camelCase), no cascade, default flex column
Expo Router = file-based routing (เหมือน Next.js)
NativeWind = Tailwind สำหรับ RN
TanStack Query + Zustand (state management library จากบทที่ 7 — ใช้กับ React Native ได้เหมือน web เลย ไม่มีอะไรพิเศษ) ใช้ได้เหมือนเดิม
EAS = build + submit + OTA update ใน cloud
✅ Reanimated สำหรับ smooth animation (UI thread)


29. React Native Web — รัน RN code บน web

react-native-web แปลง <View>, <Text>, <Pressable><div>, <span>, <button> ใน browser

bash
npx expo install react-native-web @expo/metro-runtime
npx expo start --web

→ app เดียวกัน run ได้ทั้ง iOS + Android + Web — universal app (code เดียวรันได้ทุก platform)

ใช้เมื่อ: ต้องการ share UI ระหว่าง mobile + web (เช่น Meta Ads Manager ที่ระบุใน React Native Web official docs)


➡️ บทถัดไป

ยังไม่จบ — บทที่ 13-15 เป็น advanced track:

  • 13 TypeScript Deep — generic component, polymorphic, exhaustive check
  • 14 React Internals — Fiber, hooks linked list, concurrent rendering
  • 15 Accessibility + i18n + SEO

ถ้าจะเขียน production app — ไปต่อบท 13-15


🎉 จบ React Native track

หลังจากบทที่ 0-12 คุณมีพื้นฐานเต็ม:

  • Foundation (intro, JSX, component, props, state, event)
  • Hooks deep dive
  • Forms + Validation
  • Data Fetching (TanStack Query)
  • Routing
  • Styling + UI Libraries (Tailwind, shadcn, Radix)
  • State Management (Zustand, Jotai, Redux Toolkit)
  • Testing (Vitest, RTL, Playwright)
  • Advanced Patterns (Portal, ErrorBoundary, Suspense, Compound)
  • Animation + Performance (Motion, virtualization)
  • Next.js (SSR, Server Components, Server Actions)
  • React Native (mobile)

ก้าวต่อไป

ทำอะไร
TanStack Router / Starttype-safe routing + full-stack
GraphQL (Apollo / urql)alternative API
TanStack DBclient-side database
WebAssemblyhigh-perf code in browser
Three.js + React Three Fiber3D ใน React
Solid.js / Qwikcompetitor framework — น่าศึกษา
AI / LLM integrationVercel AI SDK

← บทที่ 11 | ← สารบัญ React | ← สารบัญหลัก