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

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个槽位,每个槽位可以单独填充内容,而非新增槽位,需要调整整体逻辑:

  1. 父组件渲染slot时传递当前索引、当前值给子组件
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 05:15:06