Giới thiệu
Là một lập trình viên React, tôi luôn chú ý đến cách quản lý state, đặc biệt khi tích hợp với các nguồn dữ liệu bên ngoài hoặc các kho toàn cục. Trong suốt những năm qua, tôi đã sử dụng nhiều mẫu khác nhau—các hook useEffect, các subscriptions tùy chỉnh, và các thư viện bên thứ ba như Redux hay Zustand. Nhưng gần đây, khi làm việc trên một dự án React 18+, tôi đã phát hiện ra useSyncExternalStore, và nó thật sự đã thay đổi góc nhìn của tôi về việc đồng bộ hóa state bên ngoài với các component React.
Những Vấn Đề Ban Đầu Với Subscription từ External Store
Trong một dự án, tôi cần xây dựng một bảng điều khiển thời gian thực lắng nghe dữ liệu từ một kho lưu trữ trong bộ nhớ tùy chỉnh. Ban đầu, tôi đã viết logic subscription bằng cách sử dụng useEffect và useState. Tuy nhiên, tôi đã gặp phải một số lỗi khó chịu—đôi khi UI không phản ánh đúng state mới nhất trong quá trình cập nhật nhanh, và trong các tình huống SSR, nội dung được render từ máy chủ không luôn khớp với UI đã được hydrate ở phía client.
Vấn đề này rất tinh vi nhưng lại gây ra sự thất vọng lớn. Việc render đồng thời của React có nghĩa là các component có thể render nhiều lần trước khi cam kết, và các mẫu subscription cũ không được thiết kế cho điều đó.
Khám Phá useSyncExternalStore
Khi tôi phát hiện ra hook useSyncExternalStore của React, đó là một khoảnh khắc sáng tạo. Hook này được tạo ra đặc biệt để xử lý các subscriptions đến các "store" bên ngoài một cách an toàn với các tính năng đồng thời của React 18 và hỗ trợ server-side rendering.
Nó cho phép React đọc một snapshot của state bên ngoài và đăng ký các cập nhật, giúp UI luôn nhất quán mà không gặp phải các điều kiện đua hay sự không khớp trong quá trình hydrate.
Cách Tôi Sử Dụng Trong Dự Án
Tôi đã tái cấu trúc kho lưu trữ tùy chỉnh và component React của mình để sử dụng useSyncExternalStore như sau:
javascript
// My custom store
let count = 0;
const listeners = new Set();
export const counterStore = {
getSnapshot() {
return count;
},
subscribe(callback) {
listeners.add(callback);
return () => listeners.delete(callback);
},
increment() {
count++;
listeners.forEach(listener => listener());
},
};
Và trong React:
javascript
import React, { useSyncExternalStore } from 'react';
import { counterStore } from './counterStore';
function Counter() {
const count = useSyncExternalStore(
counterStore.subscribe,
counterStore.getSnapshot,
counterStore.getSnapshot // SSR fallback
);
return (
<div>
<p>Count: {count}</p>
<button onClick={counterStore.increment}>Increment</button>
</div>
);
}
Sự chuyển đổi này đã giúp cập nhật UI trở nên hoàn hảo—không còn render cũ hay sự không khớp nào nữa. Các cập nhật diễn ra nhất quán và hoàn toàn phù hợp với vòng đời render của React 18.
Tại Sao Điều Này Quan Trọng Với Các Lập Trình Viên React
Trước đó, tôi đã bị cám dỗ để tiếp tục sử dụng các mẫu quen thuộc với useEffect cho các subscriptions, nhưng những mẫu này không giúp các ứng dụng tương lai thích ứng với các tính năng mới nhất của React. SSR đặc biệt phức tạp mà không có các fallback snapshot.
useSyncExternalStore giải quyết những vấn đề này bằng cách:
- Cung cấp cho React snapshot hiện tại trong quá trình render.
- Đăng ký các subscriptions an toàn cho các render đồng thời.
- Cung cấp một snapshot cho server để đảm bảo SSR nhất quán.
Các Trường Hợp Sử Dụng Khác Tôi Đã Thử
Tôi cũng đã sử dụng useSyncExternalStore để theo dõi chiều rộng cửa sổ thông qua một hook tùy chỉnh. Nó đã thay thế mẫu listener sự kiện dựa trên useEffect cũ của tôi và xử lý SSR với một chiều rộng fallback mặc định hợp lý.
javascript
function useWindowWidth() {
return useSyncExternalStore(
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
() => window.innerWidth,
() => 1024 // default width for SSR
);
}
Những Lưu Ý và Thực Hành Tốt
- Thực hành tốt: Hãy luôn kiểm tra kỹ snapshot khi sử dụng
useSyncExternalStoređể đảm bảo rằng state luôn được đồng bộ. - Cảnh báo: Tránh sử dụng các kỹ thuật cũ trong các ứng dụng mới để đảm bảo khả năng tương thích và hiệu suất tốt nhất.
- Mẹo hiệu suất: Sử dụng các subscriptions một cách có chọn lọc để giảm tải cho hệ thống và cải thiện trải nghiệm người dùng.
Kết Luận
Việc chuyển sang useSyncExternalStore là một thay đổi nhỏ nhưng có tác động lớn đến độ tin cậy và khả năng bảo trì của các ứng dụng React của tôi, đặc biệt với các tính năng đồng thời và SSR trở thành tiêu chuẩn.
Nếu bạn đang quản lý các subscriptions bên ngoài hoặc state toàn cục tùy chỉnh ngày hôm nay, tôi rất khuyến nghị bạn nên khám phá hook này. Nó đã được sử dụng để cung cấp năng lượng cho các thư viện state phổ biến, vì vậy hãy tận dụng nó ngay!
Hy vọng những kinh nghiệm của tôi sẽ giúp người khác tránh được những cạm bẫy mà tôi đã gặp phải và xây dựng được các ứng dụng React mạnh mẽ hơn.
Câu Hỏi Thường Gặp (FAQ)
useSyncExternalStorelà gì?
Đây là một hook trong React được thiết kế để quản lý subscriptions đến các store bên ngoài một cách an toàn.- Tại sao nên sử dụng
useSyncExternalStore?
Nó giúp đồng bộ hóa state và tránh các vấn đề về render không chính xác trong các ứng dụng React hiện đại. - Có thể sử dụng
useSyncExternalStorevới SSR không?
Có, hook này hỗ trợ server-side rendering một cách hiệu quả.
Hy vọng bạn sẽ chia sẻ kinh nghiệm của mình khi sử dụng hook này hoặc những thách thức bạn đã gặp phải!