如何将framer-motion的Reorder与react-hook-form的useFieldArray联动?
解决framer-motion Reorder与react-hook-form useFieldArray的排序同步问题
问题背景
在ReactJS项目中使用framer-motion的<Reorder>组件实现列表拖拽排序时,无法与react-hook-form的useFieldArray同步表单字段顺序:
<Reorder>通过内置逻辑直接修改列表顺序useFieldArray未提供直接修改字段顺序的API,仅支持append/prepend/insert等操作
尝试过重建字段列表、监听dragStart/dragEnd事件、结合useEffect监听排序变化等方案,均未达到预期效果。
解决方案
核心思路是利用useFieldArray提供的replace方法,在<Reorder>的排序回调中直接将排序后的新数组同步到表单字段,确保fields变量与表单状态实时更新。
完整代码示例
import { useForm, useFieldArray, register } from "react-hook-form"; import { Reorder, ReorderItem } from "framer-motion"; export default function SortableFormList() { const { control, handleSubmit } = useForm({ defaultValues: { items: [ { id: "item-1", content: "第一项" }, { id: "item-2", content: "第二项" }, { id: "item-3", content: "第三项" } ] } }); const { fields, replace } = useFieldArray({ control, name: "items" }); // 处理排序完成后的回调 const handleReorder = (reorderedItems) => { // 使用replace同步排序后的数组到useFieldArray replace(reorderedItems); }; const onSubmit = (formData) => { console.log("排序后的表单数据:", formData.items); // 这里可以提交表单数据到后端 }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Reorder.Group axis="y" values={fields} onReorder={handleReorder}> {fields.map((item, idx) => ( <ReorderItem key={item.id} value={item} style={{ padding: "8px", margin: "4px 0", border: "1px solid #eee" }}> <input {...register(`items.${idx}.content`)} defaultValue={item.content} style={{ width: "100%" }} /> </ReorderItem> ))} </Reorder.Group> <button type="submit" style={{ marginTop: "16px", padding: "8px 16px" }}> 提交表单 </button> </form> ); }
关键注意事项
- 绑定正确的数据源:
Reorder.Group的values属性直接传入useFieldArray的fields数组,确保排序操作基于表单字段数据进行。 - 使用
replace同步数据:onReorder回调返回的reorderedItems是排序后的完整数组,直接传给replace方法,useFieldArray会自动更新fields变量及表单内部状态。 - 避免使用index作为key:每个
ReorderItem的key必须使用列表项的唯一标识(如item.id),防止排序后因index变化导致React渲染异常。
兼容依赖版本
- react: ^17.x / ^18.x
- react-hook-form: ^7.x
- framer-motion: ^9.x / ^10.x
内容的提问来源于stack exchange,提问作者Pax
相关产品推荐
相关产品推荐

