Từ React 18, mọi cập nhật state trong cùng một tick đều được gộp thành một lần render — kể cả trong promise, setTimeout, hay native event handler.
Trước 18 chỉ event handler của React mới được gộp.
setTimeout(() => {
setCount(c => c + 1)
setFlag(f => !f)
// React 17: two renders. React 18: one render.
}, 0)flushSync buộc React commit ngay lập tức, phá batching cho phần code bên trong:
flushSync(() => {
setItems(next)
})
// DOM already contains the new row here
listRef.current.scrollTop = listRef.current.scrollHeightChỉ dùng khi cần đọc DOM đã cập nhật ngay trong cùng lượt xử lý: cuộn tới phần tử vừa thêm, đo kích thước sau khi đổi nội dung, hoặc phối hợp với thư viện bên thứ ba/print yêu cầu DOM đồng bộ.
Giá phải trả: React phải render + commit ngay, không gộp được với các cập nhật khác trong cùng tick và không nhường quyền cho việc ưu tiên hơn — dễ gây khựng khung hình nếu cây con lớn. Nó cũng buộc các effect liên quan chạy sớm. Trong hầu hết trường hợp, useEffect hoặc ref callback là giải pháp đúng hơn; flushSync là ngoại lệ, không phải công cụ thường dùng.