React gom các cập nhật state trong cùng một sự kiện và render một lượt duy nhất, không đồng bộ. Ngay sau lời gọi setter, DOM vẫn là nội dung cũ nên phép đo trả về giá trị trước khi thêm dòng.
flushSync buộc React render và commit ngay lập tức trước khi trả quyền điều khiển:
jsx
function handleAdd(text) {
flushSync(() => {
setMessages((list) => [...list, text])
})
// DOM already contains the new row here
listRef.current.scrollTop = listRef.current.scrollHeight
}Vì sao hạn chế:
- Nó phá vỡ batching, ép một lượt render riêng — nhiều lần gọi trong một handler nghĩa là nhiều lần commit liên tiếp.
- Nó chặn concurrent rendering: lượt render này không thể bị ngắt để nhường việc gấp hơn.
Các lựa chọn thường tốt hơn: dùng useLayoutEffect để đo và chỉnh sau khi DOM cập nhật, dùng ref callback khi cần chạm vào node ngay lúc nó gắn vào cây, hoặc dùng CSS/scrollIntoView với overflow-anchor cho trường hợp cuộn. Chỉ dùng flushSync khi cần đo DOM ngay trong handler và không tách được sang effect — ví dụ tích hợp với thư viện bên ngoài yêu cầu DOM đã cập nhật.