Logo
KangTLee's blog

โหลดหน้าแอพ NextJS ให้เร็วขึ้นด้วย Suspense

Kang T Lee
อัพเดทล่าสุดวันที่ 24 สิงหาคม 2567
โหลดหน้าแอพ NextJS ให้เร็วขึ้นด้วย Suspense

ดึงข้อมูลจาก Database ช้า หน้าต่างโหลดนาน

หากใครดึงข้อมูลจาก Database ใน React Server Component จะพบว่าเมื่อข้อมูลที่ดึงมาจาก Database มีขนาดใหญ่ หรือบางครั้ง Database อาจจะกำลังทำงานหนัก จะพบว่าหน้าต่างจะเกิดการค้างเมื่อเราโหลดไปยังหน้านั้นๆ สาเหตุก็มาจากการที่ตัว React Server จะทำการรอข้อมูลจาก Database ก่อน ถึงจะทำการ Render หน้าต่างให้ User ได้เห็น เมื่อ User ใช้งานก็จะเห็นว่าหน้าจอเกิดการค้าง ไม่มีอะไรเกิดขึ้น ต้องรอสักพักถึงจะเห็นหน้าต่างใหม่

ตัวอย่าง Code ข้างล่างจะเป็นการจำลองการดึงข้อมูลจาก Database ด้วยฟังก์ชั่น delay ที่จะทำการรอเป็นเวลา 1 วินาที

// ./data/page.tsx
const DataPage = async () => {
  const { data } = await fetchData();
  return (
    <div className="space-y-4">
      <h1 className="text-lg font-semibold">Data Page</h1>
      <div>data = {data}</div>
    </div>
  );
};

export default DataPage;

const fetchData = async () => {
  await delay(1000);
  return { data: "fetched!!" };
};

function delay(delayInms: number) {
  return new Promise((resolve) => setTimeout(resolve, delayInms));
}

จาก code ในหน้า Home เราจะทำการดึงข้อมูลด้วยฟังก์ชั่น fetchData และแสดงผลที่ได้ใน <div>

เราจะเห็นว่าเมื่อเราทำการ Refresh หน้า Home ตัวหน้าจะไม่เกิดอะไรขึ้น จนเมื่อเวลาผ่านไป 1 วินาที จึงจะเห็นว่าตัวหน้าต่างโหลดสำเร็จ

โหลด data ใน react server component

อาการค้างของหน้าจอนี้จะเป็นผลเสียทำให้เกิด Bad user experience และอาจทำให้ User เลิกใช้งาน App ของเรา แล้วเราจะทำยังไงเพื่อทำให้ประสบการณ์การใช้งานของ User ดีขึ้นได้บ้าง...

ใช้ Suspense เพื่อใส่ Loading State

เราสามารถแก้ด้วยวิธีง่ายๆคือการใช้ Suspense จาก react library ดัง code ด้านล่างนี้

// ./data/page.tsx
import { Suspense } from "react";

const DataPage = () => {
  return (
    <div className="space-y-4">
      <h1 className="text-lg font-semibold">Data Page</h1>
      <div>
        <span>data =</span>
        <Suspense fallback={<span>loading...</span>}>
          <AsyncDataUi />
        </Suspense>
      </div>
    </div>
  );
};

export default DataPage;

async function AsyncDataUi() {
  const { data } = await fetchData();
  return <span>{data}</span>;
}

จาก code เราจะทำการแยก Ui ออกเป็นสองส่วน คือส่วนที่ไม่ต้องการใช้ข้อมูล <h1>Data Page</h1> และส่วนที่ต้องการดึงข้อมูล <AsyncDataUi/> โดยเราจะครอบส่วน Ui ที่ต้องการแสดงข้อมูลด้วย <Suspense> และมีการแสดงผลด้วย <span>loading...</span> เมื่อตัว Ui กำลังรอข้อมูลจาก Database

แค่นี้เราก็จะสามารถโหลดหน้าต่างได้อย่างรวดเร็ว และ User ก็จะสามารถเห็นว่าหน้าต่างกำลังรอข้อมูล ก่อนจะแสดงผล

นอกจากนี้ หากเรามีการดึงข้อมูลที่ไม่เกี่ยวข้องกัน หลายๆชุด เรายังสามารถแยก Server component ออกจากกันได้อีกด้วย เช่น

// ./data/page.tsx
import { Suspense } from "react";

const DataPage = () => {
  return (
    <div className="space-y-4">
      <h1 className="text-lg font-semibold">Data Page</h1>
      <div>
        <span>book =</span>
        <Suspense fallback={<span>loading...</span>}>
          <AsyncBookUi />
        </Suspense>
      </div>
      <div>
        <span>pencil =</span>
        <Suspense fallback={<span>loading...</span>}>
          <AsyncPencilUi />
        </Suspense>
      </div>
    </div>
  );
};

export default DataPage;

async function AsyncBookUi() {
  const { data } = await fetchBook();
  return <span>{data}</span>;
}
async function AsyncPencilUi() {
  const { data } = await fetchPencil();
  return <span>{data}</span>;
}
ดึงข้อมูลสองอย่างพร้อมกัน

สรุป

หากเราต้องการดึงข้อมูลจาก Database แล้วไม่อยากให้หน้าเพจเกิดอาการค้างเพราะกำลังรอการดึงข้อมูล เราสามารถใช้ Suspense จาก react ได้ ซึ่งจะช่วยเพิ่มประสบการณ์การใช้งานของ User ให้ดียิ่งขึ้น หากใครมีคำถามเพิ่มเติมสามารถคอมเม้นท์กันได้ หรือส่งข้อความหาผมได้ที่ kangtlee.com/contact

Kang T Lee

Kang T Lee

ผม Kang T Lee ผมเขียนบทความเกี่ยวกับ Web development, Lifestyle

Tags:

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