如何调整react-hook-form中useFieldArray append的滚动速度实现平滑滚动?
解决useFieldArray append后平滑滚动的问题
useFieldArray 本身并没有内置的滚动速度配置项,要实现平滑滚动得手动处理,下面是两种可行方案:
方案一:利用浏览器原生的平滑滚动(简单快捷)
浏览器的 Element.scrollIntoView() 方法支持 behavior: 'smooth' 参数,能实现基础的平滑滚动,步骤如下:
- 给每个列表项设置唯一标识(比如基于数组索引的ID)
- 在
append操作后,获取新增的列表项DOM元素,调用scrollIntoView并配置平滑行为
示例代码:
import { useForm, useFieldArray } from "react-hook-form"; function App() { const { control, register } = useForm({ defaultValues: { items: [] } }); const { fields, append } = useFieldArray({ control, name: "items" }); const handleAppend = () => { append({ name: "" }); // 等待DOM更新后执行滚动 setTimeout(() => { const lastItemIndex = fields.length; const newItem = document.getElementById(`item-${lastItemIndex}`); if (newItem) { newItem.scrollIntoView({ behavior: "smooth", // 平滑滚动 block: "start" // 滚动到元素顶部对齐 }); } }, 0); }; return ( <div> <button onClick={handleAppend}>添加项</button> <div style={{ maxHeight: "300px", overflow: "auto" }}> {fields.map((item, index) => ( <div key={item.id} id={`item-${index}`} style={{ margin: "10px 0", padding: "10px", border: "1px solid #eee" }}> <input {...register(`items[${index}].name`)} placeholder="输入内容" /> </div> ))} </div> </div> ); } export default App;
也可以用 useEffect 监听 fields 数组的变化,自动触发滚动,避免手动处理时机:
import { useEffect } from "react"; // ... 其他代码 useEffect(() => { if (fields.length === 0) return; const lastItem = document.getElementById(`item-${fields.length - 1}`); if (lastItem) { lastItem.scrollIntoView({ behavior: "smooth", block: "start" }); } }, [fields]);
方案二:自定义滚动动画(精细控制速度)
如果需要自定义滚动时长、缓动效果,可以用 requestAnimationFrame 手动实现滚动动画,完全控制滚动速度:
示例代码:
// 自定义平滑滚动函数,duration为滚动时长(毫秒) const scrollToSmoothly = (element, duration = 500) => { const targetTop = element.getBoundingClientRect().top + window.pageYOffset; const startTop = window.pageYOffset; const distance = targetTop - startTop; let startTime = null; const animateScroll = (currentTime) => { if (!startTime) startTime = currentTime; const timeElapsed = currentTime - startTime; const progress = Math.min(timeElapsed / duration, 1); // 使用缓动函数让滚动更自然(这里用的是ease-in-out) const easedProgress = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2; window.scrollTo(0, startTop + distance * easedProgress); if (timeElapsed < duration) { requestAnimationFrame(animateScroll); } }; requestAnimationFrame(animateScroll); }; // 在append后调用 const handleAppend = () => { append({ name: "" }); setTimeout(() => { const lastItemIndex = fields.length; const newItem = document.getElementById(`item-${lastItemIndex}`); if (newItem) { scrollToSmoothly(newItem, 800); // 800ms完成滚动 } }, 0); };
注意事项
- 用
setTimeout是为了等待React完成DOM更新,确保能获取到新增的元素;也可以用useLayoutEffect替代,在DOM更新后立即执行。 - 如果列表容器是滚动元素(不是window),需要调用容器的
scrollTo方法,而不是window.scrollTo,逻辑类似。
内容的提问来源于stack exchange,提问作者IanOro
相关产品推荐
相关产品推荐

