Created at

Flushing state updates synchronously with flushSync

In React, state updates are queued. Usually, this is what you want. But sometimes it will be an issue. For example, you want to always scroll the screen down to the last child of the list when adding a new todo, so you have following codes:

function handleAdd() {
  const newTodo = { id: nextId++, text: text }
  setTodos([...todos, newTodo])
  listRef.current.lastChild.scrollIntoView({
    behavior: 'smooth',
    block: 'nearest',
  })
}

But you will find it always scrolls to the todo that was just before the last added one. This is because the time you scroll the list to its last element, the todo has not yet been added. This is why scrolling always "lags behind" by one item.

To fix this issue, you can force React to update (“flush”) the DOM synchronously. To do this, import flushSync from react-dom and wrap the state update into a flushSync call:

flushSync(() => {
  setTodos([...todos, newTodo])
})
listRef.current.lastChild.scrollIntoView()

This will instruct React to update the DOM synchronously right after the code wrapped in flushSync executes.