你在 React 中使用过 `flushSync` 吗?

2025-06-10

你在 React 中使用过 `flushSync` 吗?

在这篇文章中,我们将讨论flushSync所提供的实用程序react-dom

让我们尝试flushSync通过一个例子来理解它是什么以及它如何有用。

与往常一样,这是一个简单的待办事项示例,但这里要注意的是,待办事项容器具有固定高度并且可以滚动。

因此,我们的App组件具有todos状态并返回待办事项列表以及表单。

export default function App() {
  const [todos, setTodos] = useState(mockTodos);

  const onAdd = (newTask) => {
    setTodos([...todos, { id: uuid(), task: newTask }]);
  };

  return (
    <section className="app">
      <h1>Todos</h1>
      <ul style={{ height: 200, overflowY: "auto" }}>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.task}</li>
        ))}
      </ul>
      <AddTodo onAdd={onAdd} />
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

AddTodo组件也相当简单,它只管理输入状态,一旦表单提交,它就会onAdd使用新的待办事项调用 prop。

const AddTodo = ({ onAdd }) => {
  const [taskInput, setTaskInput] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();

    if (!taskInput.trim()) return;
    setTaskInput("");
    onAdd(taskInput.trim());
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Your Task"
        value={taskInput}
        onChange={(e) => setTaskInput(e.target.value)}
      />
      <button>Add Task</button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

现在我们已经了解了代码的工作原理,假设我们想要添加一项功能,每次添加新的待办事项时,容器都会滚动到其底部,以便用户可以看到新添加的待办事项。

想一想并弄清楚如何实现此功能。

使用useEffect钩子

你可能正在考虑使用效果钩子。这样,每次todos更改时只需将容器滚动到底部即可。

useEffect(() => {
  listRef.current.scrollTop = listRef.current.scrollHeight;
  // listRef is simply a ref attached to the ul
}, [todos]);
Enter fullscreen mode Exit fullscreen mode

或者

useEffect(() => {
  const lastTodo = listRef.current.lastElementChild;
  lastTodo.scrollIntoView();
}, [todos]);
Enter fullscreen mode Exit fullscreen mode

上述两种滚动逻辑都可以正常工作(在这种情况下,如果您观察到滚动中的任何抖动,您甚至可能想要使用useLayoutEffect钩子)。

但是,我不想把它放在这两个钩子中,让我解释一下原因。

我们在这里尝试执行的DOM 操作(在本例中为滚动)是一种副作用在渲染期间不会发生的事情),并且在 React 中副作用通常发生在事件处理程序内部,因此在我看来,放置它的最佳位置是在onAdd处理程序内部。

useEffect此外,当您用尽所有其他选项但仍未找到正确的事件处理程序时,按照文档操作应该是您的最后手段。

如果您已经用尽了所有其他选项,但仍无法找到合适的事件处理程序来处理副作用,您仍然可以通过useEffect在组件中调用它来将其附加到返回的 JSX 中。这会告诉 React 稍后(渲染后,当副作用允许时)执行它。但是,这种方法应该是您的最后手段文档

处理程序内的滚动逻辑

如果您只是将滚动逻辑放入处理程序中(如下所示),您会注意到您并没有完全获得所需的结果。

const onAdd = (newTask) => {
  setTodos([...todos, { id: uuid(), task: newTask }]);

  listRef.current.scrollTop = listRef.current.scrollHeight;
};
Enter fullscreen mode Exit fullscreen mode

因为setTodos不是同步的,所以你先滚动,然后才会todos真正更新。所以,你看到的不是最后一个待办事项,而是倒数第二个。

因此,为了使其按预期工作,我们必须确保滚动逻辑仅在todos状态更新后运行。这就是它flushSync派上用场的地方。

使用flushSync

要使用flushSync,我们需要从以下位置导入它react-domimport { flushSync } from "react-dom";

现在我们可以将setTodos调用包装在处理程序中flushSync(如下所示)。

const onAdd = (newTask) => {
  flushSync(() => {
    setTodos([...todos, { id: uuid(), task: newTask }]);
  });

  listRef.current.scrollTop = listRef.current.scrollHeight;
};
Enter fullscreen mode Exit fullscreen mode

现在我们已经确保状态更新同步发生,并且只有在状态更新后才执行滚动逻辑。

这就是这篇文章的全部内容,让我知道您想要使用的情况flushSync

和平✌

鏂囩珷鏉ユ簮锛�https://dev.to/somshekhar/have-you-used-flushsync-in-react-4cpo
PREV
使用 Hooks 和 Intersection Observer 在 React 中构建无限滚动
NEXT
“查看页面源代码”的未来 现代网络的现实 我的 Web 开发之旅 查看页面源代码 威胁因素 现代网络的困境