React项目中react-dnd拖拽后props传递的answers数组更新异常如何解决
问题原因
核心是闭包陷阱问题:useDrop的配置函数仅在子组件首次渲染时执行一次,内部捕获的props.answers永远是子组件首次挂载时接收的初始值(比如你示例中长度为4的初始数组)。后续父组件更新answers后,子组件props虽然变化,但useDrop的配置函数不会重新执行,drop回调里读取的始终是旧的answers值,所以每次追加都是基于初始数组拼接,导致新值覆盖上一次追加的内容。
额外说明:你当前的父组件逻辑是answers数组长度增加时,会自动渲染更多的DraggableGameSlot组件,如果你的需求是固定numberOfAnswers个放置槽,仅填充槽内内容而非新增槽位,那当前的追加数组的逻辑本身就不符合需求。
解决方案
方案1:保留追加数组新增槽位的需求
最简单的修复方式是使用useState的函数式更新写法,不需要依赖外部的props.answers,直接拿到最新的state值进行更新,修改DraggableGameSlot.tsx的useDrop逻辑即可:
const [{isOver}, drop] = useDrop(() => ({ accept: "image", drop(item: {id: string}) { // 函数式更新直接获取最新的answers状态,避免闭包问题 props.setAnswers(prevAnswers => [...prevAnswers, item.id]); }, collect: (monitor) => ({ isOver: !!monitor.isOver(), }) // useState返回的setter是稳定值,依赖项正常填写即可 }), [props.setAnswers])
方案2:固定槽位仅填充内容(更常见的拖拽答题场景逻辑)
如果你的实际需求是固定numberOfAnswers个槽位,每个槽位可以单独填充内容,而非新增槽位,需要调整整体逻辑:
- 父组件渲染slot时传递当前索引、当前值给子组件
- drop回调里替换对应索引位置的内容,而非追加数组
父组件DraggableGameSlots.tsx修改:
{ answers.map((val, index) => ( <DraggableGameSlot className={props.slotFor === "text" ? "draggable-game-slot-for-text" : "draggable-game-slot-for-image"} key={index} slotIndex={index} currentValue={val} setAnswers={setAnswers} /> )) }
子组件DraggableGameSlot.tsx修改:
import { useDrop } from 'react-dnd'; import './css/DraggableGameSlot.css'; type DraggableGameSlotProps = { className: string, slotIndex: number, currentValue: string, setAnswers: React.Dispatch<React.SetStateAction<string[]>> } function DraggableGameSlot(props: DraggableGameSlotProps) { const [{isOver}, drop] = useDrop(() => ({ accept: "image", drop(item: {id: string}) { props.setAnswers(prev => { const newAnswers = [...prev] newAnswers[props.slotIndex] = item.id return newAnswers }); }, collect: (monitor) => ({ isOver: !!monitor.isOver(), }) }), [props.setAnswers, props.slotIndex]) return ( <div className={`draggable-game-slot ${props.className}`} ref={drop}> <span>{props.currentValue}</span> </div> ) } export default DraggableGameSlot;
内容的提问来源于stack exchange,提问作者Octavian Niculescu
相关产品推荐
相关产品推荐

