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

TypeScript下React Draggable的draggableRef类型定义问题

问题解答

1. 类型报错的核心原因

你当前给draggableRef标注的DraggableCore类型是错误的——你在JSX中渲染的是Draggable组件而非DraggableCore,更关键的是react-draggable的TS类型定义没有对外暴露组件实例的内部state属性,这个state属于组件内部私有属性,不管你把ref类型设置成Draggable还是DraggableCore,直接访问.state.x都会报属性不存在的错误。

如果你非要强行访问内部state,只能把ref类型设为any绕过TS校验,但这种做法完全不推荐:

// 不推荐:丢失类型安全,且依赖库的内部实现,版本迭代极易出问题
const draggableRef = useRef<any>(null);

2. 推荐的标准实现方案

直接修改第三方组件的内部state违反React单向数据流原则,库升级时内部属性名/结构随时可能变化,会导致线上故障。实现「窗口resize时自动调整拖拽边界、模态框不溢出视口」的需求,用Draggable的受控模式即可,完全不需要操作组件ref:

  • 自行维护模态框的坐标state,作为受控属性传给Draggable
  • 窗口resize时先计算新的合法边界,再把当前坐标校正到合法区间内
  • 用标准的事件监听方法绑定resize事件,组件卸载时做好清理,避免覆盖其他监听逻辑、造成内存泄漏

修正后的完整代码

import Draggable, { DraggableData, DraggableEvent } from 'react-draggable';
import { useState, useRef, useEffect, useCallback } from 'react';

export const DraggableModal: React.FC<DraggableModalProps> = ({
  draggableProps,
  onClose,
}) => {
  // 自主控制模态框位置,初始坐标可按需调整
  const [position, setPosition] = useState({ x: 100, y: 100 });
  const [bounds, setBounds] = useState({
    right: 0,
    bottom: 0,
  });
  const modalRef = useRef<HTMLDivElement>(null);

  // 计算边界+校正位置逻辑
  const updateBoundsAndPosition = useCallback(() => {
    const rootEl = document.getElementById('root') as HTMLDivElement | null;
    if (!modalRef.current || !rootEl) return;

    const { offsetWidth: modalWidth, offsetHeight: modalHeight } = modalRef.current;
    const { clientWidth: rootWidth, clientHeight: rootHeight } = rootEl;

    // 计算拖拽的右、下边界值
    const maxX = Math.max(0, rootWidth - modalWidth);
    const maxY = Math.max(0, rootHeight - modalHeight);

    setBounds({ right: maxX, bottom: maxY });
    // 校正当前位置,确保模态框始终在可视范围内
    setPosition(prev => ({
      x: Math.min(Math.max(0, prev.x), maxX),
      y: Math.min(Math.max(0, prev.y), maxY),
    }));
  }, []);

  // 拖拽时同步更新位置
  const handleDrag = useCallback((e: DraggableEvent, data: DraggableData) => {
    setPosition({ x: data.x, y: data.y });
    draggableProps?.onDrag?.(e, data);
  }, [draggableProps]);

  useEffect(() => {
    // 组件挂载后首次计算
    updateBoundsAndPosition();
    // 标准方式绑定resize事件,避免覆盖其他已绑定的回调
    const handleResize = () => updateBoundsAndPosition();
    window.addEventListener('resize', handleResize);

    // 组件卸载时清理事件监听
    return () => window.removeEventListener('resize', handleResize);
  }, [updateBoundsAndPosition]);

  return (
    <Draggable
      position={position}
      bounds={{ left: 0, top: 0, ...bounds }}
      handle="#handle"
      {...draggableProps}
      onDrag={handleDrag}
    >
      <div ref={modalRef}>
        {/* 原有模态框内容、DraggableWrapper等 */}
      </div>
    </Draggable>
  );
};

该实现完全在业务层管控所有状态,TS类型校验全部正常,不会依赖第三方组件的内部实现,稳定性远高于直接操作ref修改内部state的方案,同时修复了你原有代码中直接给parent.onresize赋值导致的监听覆盖、内存泄漏问题。


内容的提问来源于stack exchange,提问作者Alice

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 03:03:49