You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调整react-hook-form中useFieldArray append的滚动速度实现平滑滚动?

解决useFieldArray append后平滑滚动的问题

useFieldArray 本身并没有内置的滚动速度配置项,要实现平滑滚动得手动处理,下面是两种可行方案:

方案一:利用浏览器原生的平滑滚动(简单快捷)

浏览器的 Element.scrollIntoView() 方法支持 behavior: 'smooth' 参数,能实现基础的平滑滚动,步骤如下:

  1. 给每个列表项设置唯一标识(比如基于数组索引的ID)
  2. 在 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 18:50:06