React 列表删除后自动滚动到顶部
FreeGuideOnline
最新
2026-07-05
jsx import { useRef } from 'react';
const listContainerRef = useRef(null);
// JSX 中:
{/* 列表项渲染 */}
```
步骤二:在删除函数中执行滚动
在删除项的点击事件处理函数里,完成状态更新后,通过 ref.current 调用 scrollTo 方法。务必在状态更新之后触发滚动,因为 React 的状态更新是异步的,我们需要确保 DOM 已经更新(即列表已经变短)。可以使用 requestAnimationFrame 或 setTimeout 来保证滚动发生在渲染之后,但更简洁的方式是使用 useLayoutEffect 监听列表长度,或直接在事件处理函数的微任务中执行。
这里提供一个稳妥的做法:利用 setTimeout(零延迟)将滚动操作推迟到当前宏任务之后,此时 React 已完成渲染。
const handleDelete = (id) => {
// 假设 setItems 是 useState 返回的更新函数
setItems(prev => prev.filter(item => item.id !== id));
// 延迟滚动,确保 DOM 已更新
setTimeout(() => {
if (listContainerRef.current) {
listContainerRef.current.scrollTo({
top: 0,
behavior: 'smooth', // 平滑滚动
});
}
}, 0);
};
步骤三:使用 useLayoutEffect 监听列表变化(可选)
如果你希望删除任何一项都自动滚动到顶部,而不需要在每个删除函数中编写滚动代码,可以通过 useLayoutEffect 监听列表长度(或其他依赖),当列表变化时自动触发滚动。useLayoutEffect 会在 DOM 更新后、浏览器绘制前同步执行,避免了闪烁。
import { useLayoutEffect } from 'react';
useLayoutEffect(() => {
if (listContainerRef.current) {
listContainerRef.current.scrollTop = 0; // 或使用 scrollTo
}
}, [items]); // items 是列表状态
但要注意:如果列表新增项也会触发此 effect,可能导致每次添加都跳到顶部,不符合预期。因此,这个方案仅适用于“任何数据变化都回归顶部”的场景。更精确的控制还是推荐在删除函数中手动调用滚动。
完整示例代码
下面是一个包含添加和删除功能的待办事项列表,删除后会平滑滚回顶部。
import { useState, useRef } from 'react';
export default function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: '学习 React' },
{ id: 2, text: '实现自动滚动' },
{ id: 3, text: '完成项目' },
// ... 更多列表项
]);
const listRef = useRef(null);
const handleDelete = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
// 删除后滚回顶部
setTimeout(() => {
listRef.current?.scrollTo({
top: 0,
behavior: 'smooth',
});
}, 0);
};
const addTodo = () => {
const newId = Date.now();
setTodos(prev => [...prev, { id: newId, text: `新任务 ${newId}` }]);
// 如果希望在添加时也自动滚到底部,可以在这里处理
};
return (
<div>
<button onClick={addTodo}>添加任务</button>
<div
ref={listRef}
style={{
maxHeight: '300px',
overflowY: 'auto',
border: '1px solid #ccc',
padding: '8px',
}}
>
{todos.map(todo => (
<div key={todo.id} style={{ padding: '12px 0', borderBottom: '1px solid #eee' }}>
{todo.text}
<button onClick={() => handleDelete(todo.id)} style={{ marginLeft: 16 }}>
删除
</button>
</div>
))}
</div>
</div>
);
}