Tên SWR là viết tắt của 3 chữ stale-while-revalidate có nghĩa là sử dụng trạng thái cũ nhưng vẫn update lại nó để có được dữ liệu cập nhật mới nhất và đảm bảo data vẫn được cache, được phổ biến bởi HTTP RFC 5861. SWR hoạt động như sau, đầu tiên nó sẽ trả về dữ liệu được cache (gọi là stale) sau đó nó sẽ gửi yêu cầu làm mới (gọi là revalidate) và cuối cùng chúng ta sẽ có được dữ liệu được cập nhật mới nhất.
Với SWR, component sẽ có được dữ liệu 1 cách liên tục và tự động, và UI sẽ luôn luôn được bảo đảm trạng thái nhanh chóng phản ứng lại hoạt động của user
Tổng quan
Chúng ta sẽ xem qua ví dụ bên dưới:
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetcher);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}
Trong ví dụ trên, useSWR sẽ chấp nhận 2 đối số key kiểu string và 1 fetcher là function. key là 1 định danh duy nhất cho data (thông thường nó sẽ là URL của API) và nó sẽ được truyền vào hàm fetcher, fetcher có thể là 1 hàm asyn.
Hook sẽ return 2 giá trị là data và error, dựa trên trạng thái của request.
Các cách sử dụng
Với chỉ 1 dòng code đơn giãn bạn có thể đơn giãn hóa việc lấy dữ liệu cho dự án của mình, SWR cung cấp cho bạn khá nhiều các tính năng để dễ dàng xây dựng nhưng tính năng như bên dưới:
- Điều hướng trang nhanh chóng
- Thường xuyên thăm dò
- Dữ liệu phụ thuộc
- Cập nhật khi focus
- Cập nhật khi phục hồi mạng
- Trạng thái cục bộ (tối ưu UI)
- Thử lại khi có lỗi 1 cách thông minh
- phân trang và cuộn phục hồi vị trí
- Tương tác chậm
...
Khả năng tái sử dụng
Khi chúng ta tạo ra 1 webapp thì khả năng chúng ta sẽ sử dụng lại cùng 1 loại data ở nhiều nơi khá cao.
Đối với sử dụng thông thường, thường thì chúng ta sẽ get data ở component cao nhất sau đó sẽ truyền nó vào các component con dưới dạng thuộc tính, khi đó source code sẽ trở nên phức tạp khi bảo trì.
// page component
function Page() {
const [user, setUser] = useState(null);
// fetch data
useEffect(() => {
fetch('/api/user')
.then((res) => res.json())
.then((data) => setUser(data));
}, []);
// global loading state
if (!user) return <Spinner />;
return (
<div>
<Navbar user={user} />
<Content user={user} />
</div>
);
}
// child components
function Navbar({ user }) {
return (
<div>
...
<Avatar user={user} />
</div>
);
}
function Content({ user }) {
return <h1>Welcome back, {user.name}</h1>;
}
function Avatar({ user }) {
return <img src={user.avatar} alt={user.name} />;
}
Khi đó chúng ta có thể nghĩ đến sử dụng Context, tuy nhiên cho dù sử dụng context chúng ta vẫn gập phải vấn đề về dữ liệu động vì bản thân component cha cũng ko thể biết được các component con cần dữ liệu gì khi chúng được sinh ra động.
Trong trường hợp đó, SWR sẽ giải quyết bài toán này khá hoàn hảo.
// page component
function Page() {
return (
<div>
<Navbar />
<Content />
</div>
);
}
// child components
function Navbar() {
return (
<div>
...
<Avatar />
</div>
);
}
function Content() {
const { user, isLoading } = useUser();
if (isLoading) return <Spinner />;
return <h1>Welcome back, {user.name}</h1>;
}
function Avatar() {
const { user, isLoading } = useUser();
if (isLoading) return <Spinner />;
return <img src={user.avatar} alt={user.name} />;
}
Data được sử dụng ở Content và Avatar gọi đến cùng 1 hàm useUser, khi đó do chúng sử dụng cùng 1 định danh hay nói cách khác là sử dụng cùng 1 key cho SWR nên dữ liệu sẽ được cache lại nhưng vẫn được cập nhật mới nhất, trường hợp tốt nhất sẽ chỉ có 1 request được gọi thực sự đó là cách sử lý hoàn hảo của SWR.
Ngoài ra, dữ liệu cũng sẽ được tự động làm mới khi có ngắt kết nối internet và kết nối lại.
Tìm hiểu thêm
Chi tiết về SWR còn khá nhiều thứ thú vị, bạn nên truy cập website chính thức để đọc chi tiết hơn