0
0
Lập trình
Admin Team
Admin Teamtechmely

Giải Quyết Vấn Đề Prop Fallback Trong React Suspense

Đăng vào 7 tháng trước

• 4 phút đọc

Giới Thiệu

Khi React giới thiệu Suspense, nó đã cung cấp cho các nhà phát triển một cách tiếp cận rõ ràng để xử lý trạng thái UI không đồng bộ. Tuy nhiên, tính năng nổi bật nhất của nó — prop fallback — cũng trở thành vấn đề khó khăn nhất. Trong bài viết này, chúng ta sẽ cùng tìm hiểu về các thách thức khi sử dụng fallback và các giải pháp để khắc phục.

Mục Lục

Vấn Đề Với Fallback

Những Khó Khăn Chính

  • UI Tất Cả Hoặc Không: Các phần lớn của UI biến mất khi một thành phần đơn lẻ bị treo.
  • Mất Ngữ Cảnh: Các tiêu đề, thanh bên hoặc bố cục cố định cũng biến mất cùng với nội dung chính.
  • Quản Lý Quá Phức Tạp: Các nhà phát triển thường phải bọc mọi thứ trong các Suspense lồng nhau để tránh tình trạng “màn hình trống”.
  • Sự Lạ Lùng Khi SSR Streaming: Fallback có thể gây ra hiện tượng nhấp nháy, không nhất quán và hành vi không thể đoán trước giữa việc render trên máy chủ và máy khách.

Giải Quyết Vấn Đề Prop Fallback

Cách Khắc Phục Hiệu Quả

React Suspense thường được giới thiệu với một lời hứa thần kỳ:

jsx Copy
<Suspense fallback={<Spinner />}> <MyComponent /> </Suspense>

Tuy nhiên, thực tế là prop fallback là một trong những phần khó khăn nhất của Suspense. Nếu sử dụng không cẩn thận, nó có thể dẫn đến:

  • Màn hình trống 🔲
  • Bố cục biến mất 😬
  • Trải nghiệm người dùng khó hiểu ⚠️

Bài viết này sẽ chỉ cho bạn tại sao fallback lại có vấn đề và cách khắc phục nó với các mẫu thực tế.

1. Thiết Lập: Một Ứng Dụng Suspense Đơn Giản

Chúng ta sẽ tạo một thành phần bất đồng bộ giả để minh họa:

jsx Copy
// Giả lập một fetch mất 2 giây
function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Hello from Suspense!"), 2000);
  });
}

// Bọc fetch trong một resource (Suspense mong đợi các promise bị ném ra)
const resource = {
  read() {
    const result = fetchData();
    throw result; // tạm ngưng cho đến khi được giải quyết
  },
};

function AsyncMessage() {
  const data = resource.read();
  return <p>{data}</p>;
}

Bây giờ hãy render nó bên trong Suspense:

jsx Copy
import { Suspense } from "react";

export default function App() {
  return (
    <Suspense fallback={<div>Đang tải...</div>}>
      <AsyncMessage />
    </Suspense>
  );
}

👉 Vấn đề: Mọi thứ bên trong <Suspense> biến mất trong khi đang tải.

2. Khắc Phục #1: Giữ Bố Cục Liên Tục

Ví Dụ xấu (bố cục biến mất):

jsx Copy
<Suspense fallback={<div>Đang tải toàn bộ ứng dụng...</div>}>
  <Header />
  <MainContent />
</Suspense>

Ví Dụ tốt (giữ tiêu đề và thanh bên):

jsx Copy
<Header />
<Suspense fallback={<div>Đang tải nội dung chính...</div>}>
  <MainContent />
</Suspense>

✅ Người dùng luôn thấy tiêu đề trong khi phần chính đang tải.

3. Khắc Phục #2: Ranh Giới Lồng Nhau

Thay vì một Suspense lớn, hãy thêm các ranh giới nhỏ hơn để độc lập:

jsx Copy
<Layout>
  <Suspense fallback={<ProfileSkeleton />}> <Profile /> </Suspense>
  <Suspense fallback={<FeedSkeleton />}> <Feed /> </Suspense>
</Layout>

✅ Nếu Profile bị treo, feed vẫn sẽ render. Nếu Feed bị treo, profile vẫn sẽ render. Người dùng thấy nội dung một phần nhanh hơn.

4. Khắc Phục #3: Skeleton Thay Vì Spinners

Spinners có thể gây ra “nhảy” trong UI. Skeleton giữ cho cấu trúc vẫn hiển thị.

jsx Copy
function ArticleSkeleton() {
  return (
    <div>
      <div className="skeleton h-6 w-1/3 mb-2"></div>
      <div className="skeleton h-4 w-2/3"></div>
    </div>
  );
}

<Suspense fallback={<ArticleSkeleton />}> <Article /> </Suspense>

✅ Người dùng biết những gì sắp tới, giảm bớt sự khó chịu.

5. Khắc Phục #4: Ranh Giới Song Song So Với Tuần Tự

  • Song Song: (tải nhanh hơn, độc lập):
jsx Copy
<Suspense fallback={<SidebarSkeleton />}> <Sidebar /> </Suspense>
<Suspense fallback={<ContentSkeleton />}> <Content /> </Suspense>
  • Tuần Tự: (tải thanh bên trước, sau đó là nội dung):
jsx Copy
<Suspense fallback={<PageLoader />}> <Sidebar /> <Suspense fallback={<ContentSkeleton />}> <Content /> </Suspense> </Suspense>

✅ Chọn song song khi độc lập, tuần tự khi phụ thuộc.

6. Khắc Phục #5: Sử Dụng Thư Viện Dữ Liệu

Suspense thô là cấp thấp. Các thư viện như React Query (TanStack Query) xử lý caching và retries, giúp các ranh giới Suspense hoạt động tốt hơn.

jsx Copy
import { useSuspenseQuery } from "@tanstack/react-query";

function Profile() {
  const { data } = useSuspenseQuery({
    queryKey: ["profile"],
    queryFn: () => fetch("/api/profile").then((res) => res.json()),
  });

  return <div>{data.name}</div>;
}

<Suspense fallback={<ProfileSkeleton />}> <Profile /> </Suspense>

✅ Kiểm soát thông minh hơn với ít mã hơn.

7. Khắc Phục #6: Streaming Với React 18 SSR

Trên máy chủ, Suspense có thể phát trực tuyến phần UI theo từng phần:

jsx Copy
<Suspense fallback={<HeaderSkeleton />}> <Header /> </Suspense>
<Suspense fallback={<MainSkeleton />}> <MainContent /> </Suspense>

✅ Người dùng thấy nội dung một phần ngay khi nó sẵn sàng, thay vì phải chờ toàn bộ trang.

🏁 Kết Luận

Prop fallback rất mạnh nhưng cũng rất khó lưu dụng. Nếu không cẩn thận, nó có thể gây ra nhấp nháy và trải nghiệm người dùng kém. Nhưng với các mẫu đúng đắn, bạn có thể làm cho Suspense trở nên tuyệt vời:

  • Giữ cho bố cục luôn liên tục
  • Lồng các ranh giới để quản lý tốt hơn
  • Ưu tiên skeleton thay vì spinners
  • Kiểm soát tải song song và tuần tự
  • Sử dụng React Query hoặc Relay để lấy dữ liệu thông minh hơn
  • Phát trực tuyến từng phần trên máy chủ

Suspense chưa phải là phép thuật hoàn hảo, nhưng với các mẫu này, nó có thể trở nên sẵn sàng cho sản xuất.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào