React 19 Suspense และ Concurrent Rendering: Streaming SSR พร้อมคำถามสัมภาษณ์งาน 2026

คู่มือฉบับสมบูรณ์สำหรับ React 19 Suspense, concurrent rendering และ streaming SSR พร้อมตัวอย่างโค้ดจริงและคำถามสัมภาษณ์ที่พบบ่อยในปี 2026

ภาพประกอบ React 19 Suspense และ Concurrent Rendering

React 19 นำมาซึ่งการเปลี่ยนแปลงครั้งสำคัญในวิธีที่แอปพลิเคชันจัดการกับการ render แบบ asynchronous Suspense และ concurrent rendering ไม่ใช่ฟีเจอร์ทดลองอีกต่อไป แต่กลายเป็นรากฐานหลักสำหรับการสร้างแอปพลิเคชัน React ที่มีประสิทธิภาพสูง บทความนี้จะอธิบายกลไกการทำงานของ Suspense, การใช้งาน streaming SSR และคำถามสัมภาษณ์ที่นักพัฒนาต้องเข้าใจในปี 2026

Suspense ใน React 19 ไม่ได้มีไว้สำหรับ lazy loading component เท่านั้น ฟีเจอร์นี้ได้รวมเข้ากับ data fetching, server components และ streaming SSR อย่างสมบูรณ์เพื่อมอบประสบการณ์ผู้ใช้ที่ตอบสนองได้ดียิ่งขึ้น

ทำความเข้าใจ Suspense ใน React 19

Suspense ช่วยให้ component สามารถ "รอ" บางอย่างก่อนที่จะถูก render แนวคิดนี้เปลี่ยนรูปแบบการจัดการสถานะ loading จาก imperative เป็น declarative แทนที่จะจัดการ state isLoading ด้วยตัวเอง นักพัฒนาเพียงแค่ห่อ component ด้วย Suspense boundary

tsx
import { Suspense } from 'react';
import { UserProfile } from './UserProfile';
import { LoadingSkeleton } from './LoadingSkeleton';

function ProfilePage({ userId }: { userId: string }) {
  return (
    <Suspense fallback={<LoadingSkeleton />}>
      <UserProfile userId={userId} />
    </Suspense>
  );
}

เมื่อ UserProfile ทำการ fetch ข้อมูล React จะแสดง LoadingSkeleton โดยอัตโนมัติจนกว่าข้อมูลจะพร้อม วิธีการนี้ลดโค้ดซ้ำซ้อนที่มักจำเป็นสำหรับการจัดการ loading state

Concurrent Rendering: รากฐานของประสิทธิภาพสมัยใหม่

Concurrent rendering ช่วยให้ React สามารถเตรียม UI หลายเวอร์ชันพร้อมกันได้ ฟีเจอร์นี้ไม่ได้เปลี่ยนวิธีการเขียนโค้ด แต่เปลี่ยนวิธีที่ React ประมวลผลการ render เบื้องหลัง

tsx
import { useTransition, useState } from 'react';

function SearchResults() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const value = e.target.value;
    setQuery(value);
    
    startTransition(() => {
      // Update ที่ไม่เร่งด่วน
      setSearchResults(filterResults(value));
    });
  }

  return (
    <div>
      <input value={query} onChange={handleChange} />
      {isPending && <span>กำลังโหลด...</span>}
      <ResultsList />
    </div>
  );
}

ด้วย useTransition React สามารถขัดจังหวะการ render ที่กำลังดำเนินอยู่หากมี update ที่มีความสำคัญสูงกว่า การ input ของผู้ใช้ยังคงตอบสนองได้แม้จะมีการ render หนักทำงานอยู่เบื้องหลัง

Streaming SSR กับ React 19

Streaming SSR เปลี่ยนวิธีที่ server ส่ง HTML ไปยัง browser แทนที่จะรอให้ทั้งหน้าถูก render เสร็จ server จะส่ง HTML ทีละส่วนเมื่อ component แต่ละตัวถูกประมวลผลเสร็จ

app/page.tsx (Next.js App Router)tsx
import { Suspense } from 'react';
import { ProductList } from './ProductList';
import { RecommendationEngine } from './RecommendationEngine';

export default function HomePage() {
  return (
    <main>
      <h1>ยินดีต้อนรับ</h1>
      
      <Suspense fallback={<ProductSkeleton />}>
        <ProductList />
      </Suspense>
      
      <Suspense fallback={<RecommendationSkeleton />}>
        <RecommendationEngine />
      </Suspense>
    </main>
  );
}

ด้วยการตั้งค่านี้ browser จะได้รับ HTML shell เริ่มต้นอย่างรวดเร็ว เนื้อหาภายใน Suspense boundary แต่ละตัวจะถูกส่งแบบ streaming ทันทีที่พร้อม โดยไม่บล็อกการ render ของ component อื่น

การใช้งาน Data Fetching กับ Suspense

React 19 แนะนำ pattern ใหม่สำหรับ data fetching ที่รวมเข้ากับ Suspense ไลบรารีอย่าง TanStack Query และ SWR รองรับ pattern นี้โดยตรง

tsx
import { use } from 'react';

interface User {
  id: string;
  name: string;
  email: string;
}

async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error('ไม่สามารถโหลดข้อมูลผู้ใช้ได้');
  return res.json();
}

function UserDetails({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise);
  
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

// การใช้งาน
function UserPage({ userId }: { userId: string }) {
  const userPromise = fetchUser(userId);
  
  return (
    <Suspense fallback={<UserSkeleton />}>
      <UserDetails userPromise={userPromise} />
    </Suspense>
  );
}

Hook use เป็นการเพิ่มเติมใหม่ใน React 19 ที่ช่วยให้อ่าน promise โดยตรงภายใน component เมื่อ promise ยังไม่ resolve React จะโยนไปยัง Suspense boundary ที่ใกล้ที่สุด

Error Boundary และ Suspense

การรวม Error Boundary กับ Suspense สร้างประสบการณ์ผู้ใช้ที่แข็งแกร่ง Error Boundary จับข้อผิดพลาดที่เกิดขึ้นระหว่างการ render ในขณะที่ Suspense จัดการสถานะ loading

tsx
import { Component, Suspense, ReactNode } from 'react';

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends Component<
  { children: ReactNode; fallback: ReactNode },
  ErrorBoundaryState
> {
  state: ErrorBoundaryState = { hasError: false, error: null };

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}

function DataSection() {
  return (
    <ErrorBoundary fallback={<ErrorMessage />}>
      <Suspense fallback={<LoadingSpinner />}>
        <AsyncDataComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

Nested Suspense สำหรับ Loading แบบละเอียด

Suspense boundary สามารถซ้อนกันได้เพื่อสร้างประสบการณ์ loading ที่ละเอียดยิ่งขึ้น แต่ละ boundary จัดการสถานะ loading ของ component ภายในอย่างอิสระ

tsx
function DashboardPage() {
  return (
    <div className="dashboard">
      <Suspense fallback={<HeaderSkeleton />}>
        <DashboardHeader />
      </Suspense>
      
      <div className="dashboard-content">
        <Suspense fallback={<StatsSkeleton />}>
          <StatsPanel />
        </Suspense>
        
        <Suspense fallback={<ChartSkeleton />}>
          <AnalyticsChart />
        </Suspense>
        
        <Suspense fallback={<TableSkeleton />}>
          <RecentActivityTable />
        </Suspense>
      </div>
    </div>
  );
}

วิธีการนี้ทำให้แต่ละส่วนของ dashboard แสดงผลทันทีที่ข้อมูลพร้อม โดยไม่ต้องรอให้ทั้งหน้าโหลดเสร็จ

useDeferredValue สำหรับการเพิ่มประสิทธิภาพการ Render

Hook useDeferredValue ช่วยให้เลื่อนการ update ค่าที่ไม่เร่งด่วนได้ สิ่งนี้มีประโยชน์มากสำหรับสถานการณ์เช่น การกรองรายการขนาดใหญ่หรือการค้นหาแบบ real-time

tsx
import { useDeferredValue, useState, useMemo } from 'react';

function ProductSearch({ products }: { products: Product[] }) {
  const [searchTerm, setSearchTerm] = useState('');
  const deferredSearchTerm = useDeferredValue(searchTerm);
  
  const filteredProducts = useMemo(() => {
    return products.filter(product =>
      product.name.toLowerCase().includes(deferredSearchTerm.toLowerCase())
    );
  }, [products, deferredSearchTerm]);

  const isStale = searchTerm !== deferredSearchTerm;

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="ค้นหาสินค้า..."
      />
      
      <div style={{ opacity: isStale ? 0.7 : 1 }}>
        {filteredProducts.map(product => (
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
    </div>
  );
}

Server Components และ Suspense

React Server Components ทำงานร่วมกับ Suspense ได้โดยตรง Server component สามารถ fetch ข้อมูลได้โดยตรงโดยไม่ต้องส่ง JavaScript ไปยัง client

app/products/page.tsxtsx
import { Suspense } from 'react';
import { ProductGrid } from './ProductGrid';
import { FilterPanel } from './FilterPanel';

// Server Component
async function ProductGridServer() {
  const products = await db.products.findMany({
    take: 20,
    orderBy: { createdAt: 'desc' }
  });
  
  return <ProductGrid products={products} />;
}

export default function ProductsPage() {
  return (
    <div className="products-layout">
      <FilterPanel />
      
      <Suspense fallback={<ProductGridSkeleton />}>
        <ProductGridServer />
      </Suspense>
    </div>
  );
}

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

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

คำถามสัมภาษณ์งาน React Suspense ปี 2026

ต่อไปนี้คือคำถามที่พบบ่อยในการสัมภาษณ์งาน React ปี 2026:

คำถามเชิงแนวคิด

1. ความแตกต่างระหว่าง Suspense และ loading state แบบดั้งเดิมคืออะไร?

Suspense เปลี่ยนวิธีการจาก imperative เป็น declarative ด้วย loading state แบบดั้งเดิม นักพัฒนาต้องจัดการ state isLoading ด้วยตัวเองและกำหนดเงื่อนไขการ render ตาม state นั้น Suspense โอนความรับผิดชอบนี้ให้ React ช่วยให้ component สามารถ "suspend" ขณะรอข้อมูล ในขณะที่ Suspense boundary แม่จัดการการแสดง fallback

2. Concurrent rendering ปรับปรุงประสิทธิภาพแอปพลิเคชันอย่างไร?

Concurrent rendering ช่วยให้ React สามารถขัดจังหวะการ render ที่กำลังดำเนินอยู่เพื่อจัดการ update ที่มีความสำคัญสูงกว่า สิ่งนี้ทำให้ UI ยังคงตอบสนองได้เพราะการ input ของผู้ใช้ไม่ต้องรอให้การ render ซับซ้อนเสร็จ React สามารถเตรียม UI หลายเวอร์ชันพร้อมกันและแสดงเวอร์ชันที่เหมาะสมที่สุด

3. ควรใช้ useTransition หรือ useDeferredValue เมื่อไร?

useTransition ใช้เมื่อมีการควบคุม state update โดยตรงที่ต้องการทำเครื่องหมายว่าไม่เร่งด่วน useDeferredValue ใช้เมื่อได้รับค่าจากภายนอก (เช่น props) และต้องการเลื่อน update UI ตามค่านั้น ทั้งสองมีวัตถุประสงค์คล้ายกันแต่ใช้ในบริบทต่างกัน

คำถามเชิงปฏิบัติ

4. จะใช้งาน streaming SSR กับ Next.js อย่างไร?

Streaming SSR ใน Next.js App Router เปิดใช้งานโดยอัตโนมัติเมื่อใช้ Suspense boundary แต่ละ component ที่ห่อด้วย Suspense จะถูก stream อย่างอิสระ Server ส่ง HTML shell เริ่มต้นพร้อม fallback จากนั้นส่งเนื้อหาจริงผ่าน streaming ทันทีที่ component render เสร็จ

5. บทบาทของ hook use() ใน React 19 คืออะไร?

Hook use() ช่วยให้อ่าน promise และ context โดยตรงภายใน component สำหรับ promise เมื่อ promise ยังไม่ resolve component จะ suspend และ React จะแสดง Suspense fallback ที่ใกล้ที่สุด Hook นี้ทำให้การรวม async operations กับ Suspense ง่ายขึ้น

แนวทางปฏิบัติที่ดีสำหรับการใช้งาน Suspense

แนวทางปฏิบัติที่ดีบางประการสำหรับการใช้งาน Suspense ในแอปพลิเคชัน production:

1. วาง Suspense boundary ในระดับที่เหมาะสมtsx
function OptimalSuspenseLayout() {
  return (
    <div>
      {/* Boundary แยกสำหรับเนื้อหาอิสระ */}
      <Suspense fallback={<NavSkeleton />}>
        <Navigation />
      </Suspense>
      
      {/* Boundary รวมสำหรับเนื้อหาที่เกี่ยวข้อง */}
      <Suspense fallback={<ContentSkeleton />}>
        <MainContent />
        <RelatedContent />
      </Suspense>
    </div>
  );
}

// 2. ใช้ skeleton ที่มีความหมาย
function ArticleSkeleton() {
  return (
    <article className="animate-pulse">
      <div className="h-8 bg-gray-200 rounded w-3/4 mb-4" />
      <div className="h-4 bg-gray-200 rounded w-full mb-2" />
      <div className="h-4 bg-gray-200 rounded w-5/6 mb-2" />
      <div className="h-4 bg-gray-200 rounded w-4/6" />
    </article>
  );
}

// 3. รวมกับ Error Boundary
function RobustDataSection() {
  return (
    <ErrorBoundary fallback={<ErrorRecovery />}>
      <Suspense fallback={<DataSkeleton />}>
        <DataComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

สรุป

React 19 Suspense และ concurrent rendering เป็นรากฐานสำคัญสำหรับการสร้างแอปพลิเคชันสมัยใหม่ที่มีประสิทธิภาพสูง ความเข้าใจอย่างลึกซึ้งเกี่ยวกับกลไกการทำงานของ Suspense, streaming SSR และฟีเจอร์ concurrent เช่น useTransition และ useDeferredValue กลายเป็นความสามารถที่จำเป็นสำหรับนักพัฒนา React ในปี 2026

กุญแจสู่ความสำเร็จในการใช้งานอยู่ที่การวาง Suspense boundary ในตำแหน่งที่เหมาะสม การใช้ skeleton ที่มีความหมาย และการรวมกับ Error Boundary เพื่อมอบประสบการณ์ผู้ใช้ที่แข็งแกร่ง เมื่อเข้าใจแนวคิดเหล่านี้อย่างถ่องแท้ นักพัฒนาสามารถสร้างแอปพลิเคชัน React ที่ไม่เพียงแค่รวดเร็ว แต่ยังมอบประสบการณ์ผู้ใช้ที่เหมาะสมที่สุด

ชาเลนจ์ประจำวัน

คุณหาบั๊กใน React / Next.js เจอไหม

โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

Anthony Fillion-Maillet

เขียนโดย

Anthony Fillion-Maillet

ผู้ก่อตั้ง SharpSkill

เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่

อัปเดตเมื่อ 21 สิงหาคม 2569

แท็ก

#react
#suspense
#concurrent-rendering
#streaming-ssr
#react-19
#interview

แชร์

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