กลับไปหน้าบทความ
#React#Skeleton Loading#Optimistic UI#User Experience#TanStack Query

ทำให้เว็บ React รู้สึกเร็วด้วย Skeleton Loading และ Optimistic UI

Skeleton Loading และ Optimistic UI ช่วยให้เว็บ React ตอบสนองต่อผู้ใช้ได้อย่างเป็นธรรมชาติ แม้ไม่ได้ลดเวลาเรียก API โดยตรง เทคนิคทั้งสองเหมาะกับคนละจังหวะ แต่มีเป้าหมายร่วมกันคือทำให้ระบบรู้สึกเร็วและมีชีวิต

8 สิงหาคม 2569อ่านประมาณ 2 นาที

แชร์บทความ

ทำให้เว็บ React รู้สึกเร็วด้วย Skeleton Loading และ Optimistic UI

ทำให้เว็บ React รู้สึกเร็วด้วย Skeleton Loading และ Optimistic UI

เว็บที่เร็วไม่ได้ชนะกันด้วยตัวเลขระดับมิลลิวินาทีเพียงอย่างเดียว แต่ยังขึ้นอยู่กับว่าผู้ใช้รับรู้การตอบสนองของระบบอย่างไรด้วย ต่อให้ backend ทำงานเร็วพอสมควร หากผู้ใช้กดปุ่มแล้วหน้าจอนิ่งหรือเห็นเพียง spinner หมุนอยู่ เว็บก็ยังให้ความรู้สึกช้าได้

Skeleton Loading และ Optimistic UI เป็นสองเทคนิคที่ช่วยแก้ปัญหานี้ในแอป React ทั้งคู่ไม่ได้ทำให้ API ประมวลผลเร็วขึ้นโดยตรง แต่ทำให้หน้าจอสื่อสารกับผู้ใช้ทันทีว่าระบบกำลังทำงานหรือรับคำสั่งแล้ว

ความเร็วที่ผู้ใช้รู้สึกสำคัญอย่างไร

ระหว่างรอข้อมูล ผู้ใช้ต้องการสัญญาณที่ช่วยยืนยันว่าหน้าเว็บไม่ได้ค้าง ส่วนหลังจากกดปุ่ม ผู้ใช้ต้องการเห็นว่าการกระทำนั้นเกิดผลแล้ว หากระบบปล่อยให้หน้าจอนิ่งในสองช่วงนี้ ความไม่แน่ใจจะทำให้เวลารอสั้น ๆ รู้สึกยาวกว่าความเป็นจริง

การออกแบบ UI ที่ดีจึงไม่ควรถามเพียงว่า “ข้อมูลโหลดเสร็จหรือยัง” แต่ควรถามด้วยว่า “ผู้ใช้รู้หรือไม่ว่าระบบกำลังตอบสนอง” การแสดงโครงหน้าอย่างรวดเร็วและการสะท้อนผลของคำสั่งทันทีช่วยให้ประสบการณ์โดยรวมลื่นไหลขึ้นอย่างชัดเจน

Skeleton Loading สื่อสารสิ่งที่จะปรากฏระหว่างรอ

Skeleton Loading คือ placeholder ที่เลียนแบบโครงสร้างของเนื้อหาจริง เช่น กล่องสีเทาแทนรูปภาพ เส้นสีเทาแทนชื่อบทความ และแถบยาวแทนรายละเอียด วิธีนี้ให้ข้อมูลกับผู้ใช้มากกว่า spinner เพราะช่วยบอกทั้งสถานะการโหลดและรูปร่างคร่าว ๆ ของสิ่งที่กำลังจะปรากฏ

ใน React เราสามารถตรวจสอบสถานะ isLoading แล้วแสดงคอมโพเนนต์ Skeleton แทนข้อมูลจริงได้ ตัวอย่างเช่น หน้ารายการสินค้าอาจแสดง CardSkeleton จำนวนหกใบ ก่อนเปลี่ยนเป็น ProductCard เมื่อได้รับข้อมูลจาก API แล้ว

function ProductList({ products, isLoading }) {
  if (isLoading) {
    return (
      <div className="grid grid-cols-1 gap-6 md:grid-cols-3">
        {Array.from({ length: 6 }).map((_, index) => (
          <CardSkeleton key={index} />
        ))}
      </div>
    );
  }

  return (
    <div className="grid grid-cols-1 gap-6 md:grid-cols-3">
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Skeleton ที่ออกแบบใกล้เคียงกับเนื้อหาจริงจะช่วยลดการเปลี่ยนแปลงของ layout เมื่อข้อมูลมาถึง ผู้ใช้สามารถมองเห็นโครงสร้างหน้าและเริ่มทำความเข้าใจตำแหน่งของข้อมูลได้ตั้งแต่ยังโหลดไม่เสร็จ จึงเกิดความรู้สึกว่าเว็บเริ่มทำงานแล้ว

อย่างไรก็ตาม Skeleton ไม่ควรเป็นเพียงแท่งสีเทาทั่วไปที่วางไว้กลางหน้าจอ หากหน้าจริงแสดงการ์ดสามคอลัมน์ Skeleton ก็ควรมีสามคอลัมน์และมีสัดส่วนใกล้เคียงกัน ยิ่ง placeholder สอดคล้องกับเนื้อหาจริงมากเท่าไร การเปลี่ยนผ่านหลังโหลดเสร็จก็ยิ่งเป็นธรรมชาติมากขึ้น

Optimistic UI แสดงผลก่อนเซิร์ฟเวอร์ตอบกลับ

Optimistic UI ใช้กับช่วงเวลาหลังผู้ใช้ลงมือทำบางอย่าง โดยระบบจะสมมติไว้ก่อนว่าคำขอน่าจะสำเร็จและอัปเดตหน้าจอทันที จากนั้นจึงส่ง request ไปยังเซิร์ฟเวอร์อยู่เบื้องหลัง

ตัวอย่างที่เห็นได้ชัดคือปุ่ม Like เมื่อผู้ใช้กดหัวใจ หน้าจอควรเปลี่ยนหัวใจเป็นสีแดงและเพิ่มจำนวน Like ทันที แทนที่จะรอ API ตอบกลับประมาณครึ่งวินาทีแล้วจึงค่อยเปลี่ยน เพราะแม้เวลารอจะไม่มาก แต่ความหน่วงหลังการกดสามารถทำให้ระบบดูไม่ตอบสนองได้

หัวใจของเทคนิคนี้คือการบันทึก snapshot ของสถานะเดิมก่อนเปลี่ยน UI สมมติจำนวน Like เดิมคือ 10 ระบบสามารถแสดงเป็น 11 ได้ทันที แต่หาก request ล้มเหลวจะต้องย้อนค่ากลับเป็น 10 พร้อมแจ้งผู้ใช้อย่างสุภาพว่าบันทึกไม่สำเร็จและควรลองใหม่

async function handleLike() {
  const previousCount = likeCount;

  setLiked(true);
  setLikeCount(previousCount + 1);

  try {
    await sendLikeRequest();
  } catch {
    setLiked(false);
    setLikeCount(previousCount);
    showMessage("บันทึกไม่สำเร็จ กรุณาลองใหม่อีกครั้ง");
  }
}

แนวคิดนี้ทำได้ด้วย state ปกติของ React หรือใช้เครื่องมืออย่าง React Query และ TanStack Query เพื่อจัดการ cache ล่วงหน้า การทำงานมักแบ่งเป็นช่วงเตรียมข้อมูลเดิมก่อนเปลี่ยน ย้อนกลับเมื่อเกิดข้อผิดพลาด และปรับข้อมูลให้ตรงกับเซิร์ฟเวอร์หลัง request สิ้นสุด

เลือกใช้ Optimistic UI กับการกระทำที่เหมาะสม

Optimistic UI เหมาะกับการกระทำที่เกิดบ่อย ย้อนกลับได้ และมีโอกาสสำเร็จสูง เพราะผู้ใช้จะได้รับประโยชน์จากการตอบสนองทันทีโดยมีความเสี่ยงต่ำ ตัวอย่างที่เหมาะสมประกอบด้วย:

  • กด Like หรือเพิ่ม reaction
  • บันทึกรายการไว้ดูภายหลัง
  • เปิดหรือปิดสถานะของงานในรายการ Todo
  • จัดลำดับรายการใหม่
  • สลับค่าตัวเลือกที่สามารถย้อนกลับได้

ในทางกลับกัน การกระทำที่มีผลกระทบสูงไม่ควรแสดงว่าสำเร็จก่อนระบบยืนยัน เช่น การโอนเงิน การชำระเงิน หรือการลบข้อมูลอย่างถาวร งานประเภทนี้ควรแสดงสถานะกำลังดำเนินการให้ชัดเจนและรอผลยืนยันจากเซิร์ฟเวอร์ก่อนแจ้งความสำเร็จ

Skeleton Loading และ Optimistic UI ทำงานคนละจังหวะ

แม้ทั้งสองเทคนิคจะช่วยให้เว็บรู้สึกเร็วขึ้นเหมือนกัน แต่ตอบโจทย์คนละช่วงของการใช้งาน Skeleton Loading ดูแลความรู้สึกระหว่างที่ระบบกำลังดึงข้อมูล ส่วน Optimistic UI ดูแลความรู้สึกหลังผู้ใช้ส่งการกระทำไปยังระบบ

จังหวะการใช้งานเทคนิคที่เหมาะสมสิ่งที่ผู้ใช้รับรู้
กำลังรอข้อมูลปรากฏSkeleton Loadingระบบกำลังโหลดและเนื้อหาจะมีโครงสร้างแบบใด
เพิ่งกดหรือเปลี่ยนแปลงข้อมูลOptimistic UIระบบรับการกระทำและแสดงผลให้แล้ว
การกระทำมีความเสี่ยงสูงรอการยืนยันจากเซิร์ฟเวอร์ผลลัพธ์ยังไม่ถือว่าสำเร็จจนกว่าจะได้รับการยืนยัน

แอปหนึ่งสามารถใช้ทั้งสองแนวทางร่วมกันได้ เช่น หน้าแรกแสดง Skeleton ระหว่างโหลดรายการบทความ และใช้ Optimistic UI เมื่อผู้ใช้กดบันทึกบทความไว้ภายหลัง สิ่งสำคัญคือเลือกเทคนิคให้ตรงกับจังหวะและเตรียมสถานะข้อผิดพลาดไว้เสมอ

สรุป

Skeleton Loading คือคำตอบระหว่างรอข้อมูล โดยแสดงโครงสร้างที่ใกล้เคียงกับเนื้อหาจริงเพื่อให้ผู้ใช้รู้ว่าระบบกำลังทำงาน ส่วน Optimistic UI คือคำตอบหลังผู้ใช้ลงมือ โดยแสดงผลล่วงหน้าและย้อนกลับได้หากเซิร์ฟเวอร์ปฏิเสธคำขอ

เมื่อใช้ทั้งสองเทคนิคอย่างเหมาะสม เว็บ React จะไม่ได้เพียงดูเร็วขึ้น แต่จะตอบสนองอย่างเป็นธรรมชาติและสร้างความมั่นใจให้ผู้ใช้มากขึ้น ความเร็วที่ดีจึงไม่ใช่แค่เรื่องของ latency แต่คือการทำให้ทุกช่วงเวลารอมีคำอธิบายและทุกการกระทำได้รับการตอบสนองอย่างชัดเจน