React Native 0.86 ในปี 2026: Edge-to-Edge Android, DevTools และคำถามสัมภาษณ์

เชี่ยวชาญ React Native 0.86 พร้อมการรองรับ edge-to-edge Android 15, การปรับปรุง DevTools และคำถามสัมภาษณ์เชิงปฏิบัติ คู่มือฉบับสมบูรณ์พร้อมตัวอย่างโค้ด

แผนภาพเวิร์กโฟลว์การพัฒนา edge-to-edge Android ด้วย React Native 0.86

React Native 0.86 นำเสนอการรองรับ edge-to-edge อย่างครอบคลุมสำหรับ Android 15+ ทำให้เป็นรุ่นที่สองติดต่อกันที่ไม่มี breaking changes เปิดตัวในเดือนมิถุนายน 2026 เวอร์ชันนี้แก้ไขปัญหา API ของ layout และ measurement ที่ถูกกระทบอย่างเงียบ ๆ จากนโยบาย edge-to-edge บังคับของ Android

ไม่มี Breaking Changes

React Native 0.86 ยังคงความเข้ากันได้อย่างสมบูรณ์กับเวอร์ชัน 0.85 กระบวนการอัปเกรดไม่ต้องการการแก้ไขโค้ดสำหรับแอปพลิเคชันส่วนใหญ่ ทำให้เป็นหนึ่งในการเปลี่ยนผ่านเวอร์ชัน major ที่ราบรื่นที่สุดในประวัติศาสตร์ React Native

ทำความเข้าใจ Edge-to-Edge บน Android 15

Layout แบบ edge-to-edge อนุญาตให้เนื้อหาขยายไปใต้องค์ประกอบ UI ของระบบ เช่น status bar และ navigation bar Android 15 บังคับใช้พฤติกรรมนี้โดยค่าเริ่มต้น โดยไม่คำนึงถึงการตั้งค่าแอปพลิเคชัน React Native 0.86 รับประกันว่า API ทั้งหมดของ measurement และ layout ทำงานได้อย่างถูกต้องภายใต้เงื่อนไขเหล่านี้

ฟังก์ชัน measureInWindow ตอนนี้ส่งคืนพิกัดที่ถูกต้องเมื่อโหมด edge-to-edge ถูกเปิดใช้งาน ก่อนหน้านี้ การวัดอาจถูกเลื่อนโดยความสูงขององค์ประกอบ UI ของระบบ ทำให้เกิดบั๊กในการวางตำแหน่งบน overlay, tooltip และ floating action button

SafeAreaHandler.tsxtypescript
import React, { useRef, useCallback } from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

interface TooltipPosition {
  x: number;
  y: number;
}

export function SafeAreaHandler() {
  const buttonRef = useRef<View>(null);
  const insets = useSafeAreaInsets();

  const showTooltip = useCallback(() => {
    // measureInWindow now returns correct coordinates on Android 15+
    buttonRef.current?.measureInWindow((x, y, width, height) => {
      const position: TooltipPosition = {
        x: x + width / 2,
        // Coordinates are accurate even with edge-to-edge enabled
        y: y + height + 8,
      };
      console.log('Tooltip position:', position);
    });
  }, []);

  return (
    <View style={[styles.container, { paddingTop: insets.top }]}>
      <View ref={buttonRef} collapsable={false}>
        <TouchableOpacity onPress={showTooltip} style={styles.button}>
          <Text style={styles.buttonText}>Show Tooltip</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  button: { backgroundColor: '#007AFF', padding: 16, borderRadius: 8 },
  buttonText: { color: '#FFFFFF', fontWeight: '600' },
});

Prop collapsable={false} รับประกันว่า View จะรักษา reference ของ native สำหรับการวัด หากไม่มีสิ่งนี้ React Native อาจทำการ optimize View native นั้นออกไป ทำให้ measureInWindow ล้มเหลว

การแก้ไข KeyboardAvoidingView สำหรับ Android 15

Component KeyboardAvoidingView คำนวณพื้นที่ว่างไม่ถูกต้องเมื่อโหมด edge-to-edge ถูกเปิดใช้งาน เวอร์ชัน 0.86 แก้ไขปัญหานี้โดยการนำ insets ของ UI ระบบมาคำนวณในความสูงที่ใช้งานได้

ChatInput.tsxtypescript
import React, { useState } from 'react';
import {
  KeyboardAvoidingView,
  TextInput,
  Platform,
  StyleSheet,
  View,
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

export function ChatInput() {
  const [message, setMessage] = useState('');
  const insets = useSafeAreaInsets();

  return (
    <KeyboardAvoidingView
      // behavior="padding" works correctly on Android 15+ edge-to-edge
      behavior={Platform.OS === 'ios' ? 'padding' : 'padding'}
      style={styles.container}
      // keyboardVerticalOffset accounts for headers and safe areas
      keyboardVerticalOffset={Platform.select({ ios: 88, android: 0 })}
    >
      <View style={styles.content}>
        {/* Chat messages would render here */}
      </View>
      <View style={[styles.inputContainer, { paddingBottom: insets.bottom }]}>
        <TextInput
          value={message}
          onChangeText={setMessage}
          placeholder="Type a message..."
          style={styles.input}
          multiline
        />
      </View>
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  content: { flex: 1 },
  inputContainer: { borderTopWidth: 1, borderTopColor: '#E5E5E5', padding: 8 },
  input: { minHeight: 40, padding: 12, backgroundColor: '#F5F5F5', borderRadius: 20 },
});

บน Android 15 การใช้ behavior="height" มักทำให้เกิดการกระโดดของ layout Behavior padding ให้ animation ที่นุ่มนวลกว่าและการวางตำแหน่งที่คาดเดาได้มากขึ้นเมื่อ edge-to-edge ถูกเปิดใช้งาน

การอัปเดต StatusBar ระหว่างการแสดง Modal

บั๊กที่คงอยู่นานป้องกันการเปลี่ยนแปลง style และ visibility ของ StatusBar ขณะที่ Modal เปิดอยู่ React Native 0.86 แก้ไขปัญหานี้ ทำให้สามารถปรับแต่ง status bar แบบไดนามิกในขั้นตอน modal ได้

ThemedModal.tsxtypescript
import React, { useState, useEffect } from 'react';
import { Modal, View, StatusBar, Text, Pressable, StyleSheet } from 'react-native';

interface ThemedModalProps {
  visible: boolean;
  onClose: () => void;
  darkMode?: boolean;
}

export function ThemedModal({ visible, onClose, darkMode = false }: ThemedModalProps) {
  // StatusBar changes now apply correctly while modal is visible
  useEffect(() => {
    if (visible) {
      StatusBar.setBarStyle(darkMode ? 'light-content' : 'dark-content', true);
      StatusBar.setBackgroundColor(darkMode ? '#1A1A1A' : '#FFFFFF', true);
    }
    return () => {
      // Restore original status bar when modal closes
      StatusBar.setBarStyle('dark-content', true);
      StatusBar.setBackgroundColor('#FFFFFF', true);
    };
  }, [visible, darkMode]);

  return (
    <Modal visible={visible} animationType="slide" onRequestClose={onClose}>
      <View style={[styles.modalContent, darkMode && styles.darkBackground]}>
        <StatusBar
          barStyle={darkMode ? 'light-content' : 'dark-content'}
          backgroundColor={darkMode ? '#1A1A1A' : '#FFFFFF'}
        />
        <Text style={[styles.title, darkMode && styles.lightText]}>
          Modal Content
        </Text>
        <Pressable onPress={onClose} style={styles.closeButton}>
          <Text style={styles.closeButtonText}>Close</Text>
        </Pressable>
      </View>
    </Modal>
  );
}

const styles = StyleSheet.create({
  modalContent: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#FFFFFF' },
  darkBackground: { backgroundColor: '#1A1A1A' },
  title: { fontSize: 24, fontWeight: 'bold', color: '#000000' },
  lightText: { color: '#FFFFFF' },
  closeButton: { marginTop: 20, padding: 12, backgroundColor: '#007AFF', borderRadius: 8 },
  closeButtonText: { color: '#FFFFFF', fontWeight: '600' },
});

การแก้ไขนี้มีความสำคัญเป็นพิเศษสำหรับแอปพลิเคชันที่ใช้งานการสลับ dark mode ในขั้นตอน modal เช่น หน้าจอการตั้งค่าหรือตัวแสดงรูปภาพ

พร้อมที่จะพิชิตการสัมภาษณ์ React Native แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

การจำลอง Light และ Dark Mode ใน DevTools

React Native DevTools แนะนำการรองรับ Emulation.setEmulatedMedia ทำให้สามารถสลับธีมได้ทันทีโดยไม่ต้องเปลี่ยนการตั้งค่าอุปกรณ์หรือ emulator เข้าถึงฟีเจอร์นี้ผ่าน Command Palette ด้วย Cmd+Shift+P (macOS) หรือ Ctrl+Shift+P (Windows/Linux)

ThemeProvider.tsxtypescript
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useColorScheme, Appearance } from 'react-native';

type Theme = 'light' | 'dark';

interface ThemeContextValue {
  theme: Theme;
  toggleTheme: () => void;
  colors: typeof lightColors;
}

const lightColors = {
  background: '#FFFFFF',
  surface: '#F5F5F5',
  text: '#000000',
  textSecondary: '#666666',
  primary: '#007AFF',
  border: '#E5E5E5',
};

const darkColors = {
  background: '#1A1A1A',
  surface: '#2D2D2D',
  text: '#FFFFFF',
  textSecondary: '#A0A0A0',
  primary: '#0A84FF',
  border: '#3D3D3D',
};

const ThemeContext = createContext<ThemeContextValue | null>(null);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  // DevTools emulation triggers colorScheme changes via setEmulatedMedia
  const systemColorScheme = useColorScheme();
  const [theme, setTheme] = useState<Theme>(systemColorScheme ?? 'light');

  useEffect(() => {
    // Listen for DevTools theme emulation changes
    const subscription = Appearance.addChangeListener(({ colorScheme }) => {
      if (colorScheme) {
        setTheme(colorScheme);
      }
    });
    return () => subscription?.remove();
  }, []);

  const toggleTheme = () => setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
  const colors = theme === 'dark' ? darkColors : lightColors;

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme, colors }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme(): ThemeContextValue {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

สถานะ media ที่จำลองจะถูกรีเซ็ตเมื่อ DevTools ตัดการเชื่อมต่อ รับประกันว่า build ของ production จะไม่ได้รับผลกระทบจากการทดสอบธีมในระหว่าง development

คำถามสัมภาษณ์: React Native 0.86 และ Edge-to-Edge

การสัมภาษณ์ทางเทคนิคในปี 2026 ให้ความสำคัญกับความเข้ากันได้กับ Android 15 และการใช้งาน edge-to-edge มากขึ้น การเชี่ยวชาญแนวคิดเหล่านี้แสดงให้เห็นถึงความเข้าใจในแนวปฏิบัติการพัฒนา mobile สมัยใหม่ สำหรับการเตรียมตัวสัมภาษณ์อย่างครอบคลุม สามารถสำรวจโมดูล คำถามสัมภาษณ์ debugging React Native ได้

โหมด edge-to-edge คืออะไรและทำไม Android 15 ถึงบังคับใช้

โหมด edge-to-edge ขยายเนื้อหาแอปพลิเคชันไปใต้องค์ประกอบ UI ของระบบ (status bar, navigation bar, ตัวบ่งชี้ gesture) Android 15 บังคับใช้พฤติกรรมนี้โดยค่าเริ่มต้นเพื่อให้ประสบการณ์ที่ดื่มด่ำและ UI ที่สอดคล้องกันทั่วทั้งแพลตฟอร์ม แอปพลิเคชันต้องจัดการ safe area insets เพื่อป้องกันเนื้อหาถูกบดบังโดย UI ระบบ

พฤติกรรม measureInWindow เปลี่ยนแปลงอย่างไรเมื่อ edge-to-edge ถูกเปิดใช้งาน

ก่อน React Native 0.86 measureInWindow ส่งคืนพิกัดสัมพัทธ์กับหน้าต่างที่มองเห็น ซึ่งไม่รวมพื้นที่ UI ของระบบ เมื่อใช้ edge-to-edge หน้าต่างขยายไปถึงขอบหน้าจอ แต่การวัดไม่ถูกต้องเนื่องจาก offset เวอร์ชัน 0.86 รับประกันว่าพิกัดถูกต้องโดยไม่คำนึงถึงสถานะ edge-to-edge

Behavior ของ KeyboardAvoidingView ที่แนะนำบน Android 15 คืออะไร

ใช้ behavior="padding" บน Android 15 เมื่อ edge-to-edge ถูกเปิดใช้งาน Behavior height อาจทำให้เกิดการกระโดดของ layout เนื่องจากการคำนวณความสูงที่ใช้งานได้เปลี่ยนแปลงเมื่อ keyboard ปรากฏ Behavior padding ให้การเปลี่ยนผ่านที่นุ่มนวลกว่าและการวางตำแหน่งที่คาดเดาได้มากขึ้น

Safe area insets แตกต่างกันอย่างไรระหว่าง iOS และ Android edge-to-edge

บน iOS safe area insets มีมาตั้งแต่ iPhone X สำหรับ notch และ home indicator บน Android edge-to-edge insets รวมถึงความสูงของ status bar, ความสูงของ navigation bar และพื้นที่ตัวบ่งชี้ gesture ไลบรารี react-native-safe-area-context ให้ค่า insets ข้ามแพลตฟอร์มผ่าน useSafeAreaInsets()

Pressable android_ripple กับ PlatformColor

React Native 0.86 เพิ่มการรองรับ PlatformColor ให้กับ prop android_ripple ทำให้สามารถใช้สี ripple แบบไดนามิกที่ตอบสนองต่อการตั้งค่าธีมของระบบ

ThemedButton.tsxtypescript
import React from 'react';
import { Pressable, Text, PlatformColor, Platform, StyleSheet } from 'react-native';

interface ThemedButtonProps {
  title: string;
  onPress: () => void;
}

export function ThemedButton({ title, onPress }: ThemedButtonProps) {
  return (
    <Pressable
      onPress={onPress}
      style={({ pressed }) => [
        styles.button,
        pressed && styles.pressed,
      ]}
      android_ripple={{
        // PlatformColor adapts to system theme automatically
        color: Platform.OS === 'android'
          ? PlatformColor('?attr/colorControlHighlight')
          : undefined,
        borderless: false,
        foreground: true,
      }}
    >
      <Text style={styles.text}>{title}</Text>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  button: {
    padding: 16,
    backgroundColor: Platform.select({
      android: PlatformColor('?attr/colorPrimary'),
      default: '#007AFF',
    }),
    borderRadius: 8,
    alignItems: 'center',
  },
  pressed: { opacity: 0.8 },
  text: { color: '#FFFFFF', fontWeight: '600', fontSize: 16 },
});

การใช้ ?attr/colorControlHighlight รับประกันว่าสี ripple ตรงตามแนวทาง Material Design และปรับตัวตามธีมสว่าง/มืดที่กำหนดค่าในระดับระบบ

Image.getSize ส่งคืนขนาดที่ถูกต้อง

เมธอด Image.getSize และ Image.getSizeWithHeaders ตอนนี้ส่งคืนขนาดรูปภาพต้นฉบับแทนค่าที่ถูก downsample การแก้ไขนี้จำเป็นสำหรับการคำนวณ aspect ratio ที่ถูกต้องและ layout รูปภาพแบบ responsive

ResponsiveImage.tsxtypescript
import React, { useState, useEffect } from 'react';
import { Image, View, useWindowDimensions, StyleSheet } from 'react-native';

interface ResponsiveImageProps {
  uri: string;
  maxHeight?: number;
}

export function ResponsiveImage({ uri, maxHeight = 400 }: ResponsiveImageProps) {
  const { width: screenWidth } = useWindowDimensions();
  const [dimensions, setDimensions] = useState({ width: screenWidth, height: 200 });

  useEffect(() => {
    // getSize now returns true source dimensions, not downsampled values
    Image.getSize(
      uri,
      (width, height) => {
        const aspectRatio = width / height;
        let calculatedHeight = screenWidth / aspectRatio;
        
        // Constrain to maxHeight while maintaining aspect ratio
        if (calculatedHeight > maxHeight) {
          calculatedHeight = maxHeight;
        }
        
        setDimensions({
          width: screenWidth,
          height: calculatedHeight,
        });
      },
      (error) => {
        console.error('Failed to get image size:', error);
      }
    );
  }, [uri, screenWidth, maxHeight]);

  return (
    <View style={styles.container}>
      <Image
        source={{ uri }}
        style={{ width: dimensions.width, height: dimensions.height }}
        resizeMode="cover"
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { width: '100%' },
});

การแก้ไขนี้ขจัดความจำเป็นในการใช้ workaround ที่ดึงขนาดรูปภาพผ่าน module native หรือการประมวลผลรูปภาพฝั่ง server

การจัดการ Response HTTP ขนาดใหญ่โดยไม่เกิด OutOfMemoryError

NetworkingModule ก่อนหน้านี้โยน OutOfMemoryError เมื่อจัดการ response HTTP ขนาดใหญ่ เวอร์ชัน 0.86 ใช้การประมวลผลแบบ chunk สำหรับ response ที่เกินขีดจำกัดหน่วยความจำ

LargeFileDownload.tsxtypescript
import { useState, useCallback } from 'react';

interface DownloadProgress {
  loaded: number;
  total: number;
  percentage: number;
}

export function useLargeFileDownload() {
  const [progress, setProgress] = useState<DownloadProgress | null>(null);
  const [error, setError] = useState<string | null>(null);

  const downloadFile = useCallback(async (url: string): Promise<Blob | null> => {
    try {
      setError(null);
      setProgress({ loaded: 0, total: 0, percentage: 0 });

      const response = await fetch(url);
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const contentLength = response.headers.get('Content-Length');
      const total = contentLength ? parseInt(contentLength, 10) : 0;

      // NetworkingModule now handles large responses without OutOfMemoryError
      const reader = response.body?.getReader();
      if (!reader) {
        throw new Error('Response body is not readable');
      }

      const chunks: Uint8Array[] = [];
      let loaded = 0;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        chunks.push(value);
        loaded += value.length;
        
        setProgress({
          loaded,
          total,
          percentage: total > 0 ? Math.round((loaded / total) * 100) : 0,
        });
      }

      const blob = new Blob(chunks);
      setProgress({ loaded, total, percentage: 100 });
      return blob;
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Download failed';
      setError(message);
      return null;
    }
  }, []);

  return { downloadFile, progress, error };
}

สำหรับการดาวน์โหลดไฟล์ที่เกิน 50MB ควรพิจารณาใช้ react-native-blob-util หรือไลบรารีจัดการไฟล์ native ที่คล้ายกันเพื่อการจัดการหน่วยความจำที่ดีขึ้น

Header Cookie ที่ส่งผ่าน constructor ของ WebSocket ก่อนหน้านี้ถูกลบก่อนถึง server สิ่งนี้ป้องกันการยืนยันตัวตนแบบ session สำหรับการเชื่อมต่อ WebSocket

AuthenticatedWebSocket.tstypescript
type MessageHandler = (data: string) => void;
type ErrorHandler = (error: Event) => void;

interface WebSocketOptions {
  url: string;
  sessionCookie: string;
  onMessage: MessageHandler;
  onError?: ErrorHandler;
}

export function createAuthenticatedWebSocket({
  url,
  sessionCookie,
  onMessage,
  onError,
}: WebSocketOptions): WebSocket {
  // Cookie header now reaches the server correctly in 0.86
  const ws = new WebSocket(url, undefined, {
    headers: {
      Cookie: sessionCookie,
      // Additional headers as needed
      'X-Client-Version': '1.0.0',
    },
  });

  ws.onopen = () => {
    console.log('WebSocket connected with session cookie');
  };

  ws.onmessage = (event) => {
    if (typeof event.data === 'string') {
      onMessage(event.data);
    }
  };

  ws.onerror = (error) => {
    console.error('WebSocket error:', error);
    onError?.(error);
  };

  ws.onclose = (event) => {
    console.log(`WebSocket closed: ${event.code} ${event.reason}`);
  };

  return ws;
}

การแก้ไขนี้มีความสำคัญสำหรับแอปพลิเคชันที่ใช้การจัดการ session แบบ cookie ร่วมกับฟีเจอร์ real-time โมดูล คำถามสัมภาษณ์ networking React Native กล่าวถึง pattern การใช้งาน WebSocket อย่างละเอียด

สรุป

  • React Native 0.86 ให้การแก้ไข edge-to-edge ที่จำเป็นสำหรับ Android 15 โดยไม่มี breaking changes
  • measureInWindow, KeyboardAvoidingView และ Dimensions ทำงานได้อย่างถูกต้องเมื่อ edge-to-edge ถูกเปิดใช้งาน
  • การอัปเดต StatusBar ตอนนี้มีผลขณะที่ component Modal กำลังแสดงอยู่
  • การจำลองธีมของ DevTools เร่งการพัฒนาและทดสอบ dark mode
  • การรองรับ PlatformColor ใน android_ripple ทำให้สามารถตอบสนองการสัมผัสตามธีม
  • Image.getSize ส่งคืนขนาดต้นฉบับที่ถูกต้องสำหรับ layout แบบ responsive
  • Response HTTP ขนาดใหญ่ไม่ทำให้เกิด OutOfMemoryError อีกต่อไป
  • Header Cookie ของ WebSocket ถึง server ตามที่คาดหวัง

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แท็ก

#react-native
#android
#mobile-development
#edge-to-edge
#devtools

แชร์

บทความที่เกี่ยวข้อง