你在 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>
);
}
该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>
);
};
现在我们已经了解了代码的工作原理,假设我们想要添加一项功能,每次添加新的待办事项时,容器都会滚动到其底部,以便用户可以看到新添加的待办事项。
想一想并弄清楚如何实现此功能。
使用useEffect
钩子
你可能正在考虑使用效果钩子。这样,每次todos
更改时只需将容器滚动到底部即可。
useEffect(() => {
listRef.current.scrollTop = listRef.current.scrollHeight;
// listRef is simply a ref attached to the ul
}, [todos]);
或者
useEffect(() => {
const lastTodo = listRef.current.lastElementChild;
lastTodo.scrollIntoView();
}, [todos]);
上述两种滚动逻辑都可以正常工作(在这种情况下,如果您观察到滚动中的任何抖动,您甚至可能想要使用useLayoutEffect
钩子)。
但是,我不想把它放在这两个钩子中,让我解释一下原因。
我们在这里尝试执行的DOM 操作(在本例中为滚动)是一种副作用(在渲染期间不会发生的事情),并且在 React 中副作用通常发生在事件处理程序内部,因此在我看来,放置它的最佳位置是在onAdd
处理程序内部。
useEffect
此外,当您用尽所有其他选项但仍未找到正确的事件处理程序时,按照文档操作应该是您的最后手段。
如果您已经用尽了所有其他选项,但仍无法找到合适的事件处理程序来处理副作用,您仍然可以通过
useEffect
在组件中调用它来将其附加到返回的 JSX 中。这会告诉 React 稍后(渲染后,当副作用允许时)执行它。但是,这种方法应该是您的最后手段。文档
处理程序内的滚动逻辑
如果您只是将滚动逻辑放入处理程序中(如下所示),您会注意到您并没有完全获得所需的结果。
const onAdd = (newTask) => {
setTodos([...todos, { id: uuid(), task: newTask }]);
listRef.current.scrollTop = listRef.current.scrollHeight;
};
因为setTodos
不是同步的,所以你先滚动,然后才会todos
真正更新。所以,你看到的不是最后一个待办事项,而是倒数第二个。
因此,为了使其按预期工作,我们必须确保滚动逻辑仅在todos
状态更新后运行。这就是它flushSync
派上用场的地方。
使用flushSync
要使用flushSync
,我们需要从以下位置导入它react-dom
:import { flushSync } from "react-dom";
现在我们可以将setTodos
调用包装在处理程序中flushSync
(如下所示)。
const onAdd = (newTask) => {
flushSync(() => {
setTodos([...todos, { id: uuid(), task: newTask }]);
});
listRef.current.scrollTop = listRef.current.scrollHeight;
};
现在我们已经确保状态更新同步发生,并且只有在状态更新后才执行滚动逻辑。
这就是这篇文章的全部内容,让我知道您想要使用的情况flushSync
。
和平✌
鏂囩珷鏉ユ簮锛�https://dev.to/somshekhar/have-you-used-flushsync-in-react-4cpo