React TypeScript拖拽Resize组件报错:string无法赋值给Element类型
React拖拽调整大小组件TypeScript适配方案
问题背景
开发可通过拖拽边角栏调整大小的React组件时,参考无依赖JavaScript实现方案转TypeScript后,遇到两个类型错误:
Argument of type 'string' is not assignable to parameter of type 'Element'Argument of type 'null' is not assignable to parameter of type 'Element'
错误原因
- useRef初始值错误:原代码用选择器字符串(如
"#box")作为useRef初始值,但React的useRef是用来绑定DOM元素的,不能直接存储选择器;若设为null,TypeScript因默认类型推断规则会报错,需显式指定泛型类型。 - 未处理null安全:组件挂载前
ref.current为null,直接调用DOM方法会触发类型错误。
修复后的完整代码
TypeScript组件代码
import React, { useRef, useEffect } from "react"; import styles from "./Resize.module.css"; // 定义组件Props类型 interface ResizeableComponentsProps { children?: React.ReactNode; } const ResizeableComponents = ({ children }: ResizeableComponentsProps) => { // 给useRef指定泛型,明确绑定HTMLDivElement,初始值设为null const ref = useRef<HTMLDivElement>(null); const refLeft = useRef<HTMLDivElement>(null); const refTop = useRef<HTMLDivElement>(null); const refRight = useRef<HTMLDivElement>(null); const refBottom = useRef<HTMLDivElement>(null); useEffect(() => { // 先检查所有ref是否已绑定DOM元素,避免null错误 const resizeableEle = ref.current; const resizerRight = refRight.current; const resizerTop = refTop.current; const resizerBottom = refBottom.current; const resizerLeft = refLeft.current; if (!resizeableEle || !resizerRight || !resizerTop || !resizerBottom || !resizerLeft) { return; } const computedStyles = window.getComputedStyle(resizeableEle); let width = parseInt(computedStyles.width, 10); let height = parseInt(computedStyles.height, 10); let x = 0; let y = 0; resizeableEle.style.top = "150px"; resizeableEle.style.left = "150px"; // 右侧拖拽逻辑 const onMouseMoveRightResize = (event: MouseEvent) => { const dx = event.clientX - x; x = event.clientX; width += dx; resizeableEle.style.width = `${width}px`; }; const onMouseUpRightResize = () => { document.removeEventListener("mousemove", onMouseMoveRightResize); }; const onMouseDownRightResize = (event: MouseEvent) => { x = event.clientX; resizeableEle.style.left = computedStyles.left; resizeableEle.style.right = "auto"; document.addEventListener("mousemove", onMouseMoveRightResize); document.addEventListener("mouseup", onMouseUpRightResize); }; // 顶部拖拽逻辑 const onMouseMoveTopResize = (event: MouseEvent) => { const dy = event.clientY - y; height -= dy; y = event.clientY; resizeableEle.style.height = `${height}px`; }; const onMouseUpTopResize = () => { document.removeEventListener("mousemove", onMouseMoveTopResize); }; const onMouseDownTopResize = (event: MouseEvent) => { y = event.clientY; const styles = window.getComputedStyle(resizeableEle); resizeableEle.style.bottom = styles.bottom; resizeableEle.style.top = "auto"; document.addEventListener("mousemove", onMouseMoveTopResize); document.addEventListener("mouseup", onMouseUpTopResize); }; // 底部拖拽逻辑 const onMouseMoveBottomResize = (event: MouseEvent) => { const dy = event.clientY - y; height += dy; y = event.clientY; resizeableEle.style.height = `${height}px`; }; const onMouseUpBottomResize = () => { document.removeEventListener("mousemove", onMouseMoveBottomResize); }; const onMouseDownBottomResize = (event: MouseEvent) => { y = event.clientY; const styles = window.getComputedStyle(resizeableEle); resizeableEle.style.top = styles.top; resizeableEle.style.bottom = "auto"; document.addEventListener("mousemove", onMouseMoveBottomResize); document.addEventListener("mouseup", onMouseUpBottomResize); }; // 左侧拖拽逻辑 const onMouseMoveLeftResize = (event: MouseEvent) => { const dx = event.clientX - x; x = event.clientX; width -= dx; resizeableEle.style.width = `${width}px`; }; const onMouseUpLeftResize = () => { document.removeEventListener("mousemove", onMouseMoveLeftResize); }; const onMouseDownLeftResize = (event: MouseEvent) => { x = event.clientX; resizeableEle.style.right = computedStyles.right; resizeableEle.style.left = "auto"; document.addEventListener("mousemove", onMouseMoveLeftResize); document.addEventListener("mouseup", onMouseUpLeftResize); }; // 绑定拖拽触发事件 resizerRight.addEventListener("mousedown", onMouseDownRightResize); resizerTop.addEventListener("mousedown", onMouseDownTopResize); resizerBottom.addEventListener("mousedown", onMouseDownBottomResize); resizerLeft.addEventListener("mousedown", onMouseDownLeftResize); // 组件卸载时清理所有事件监听,防止内存泄漏 return () => { resizerRight.removeEventListener("mousedown", onMouseDownRightResize); resizerTop.removeEventListener("mousedown", onMouseDownTopResize); resizerBottom.removeEventListener("mousedown", onMouseDownBottomResize); resizerLeft.removeEventListener("mousedown", onMouseDownLeftResize); document.removeEventListener("mousemove", onMouseMoveRightResize); document.removeEventListener("mouseup", onMouseUpRightResize); document.removeEventListener("mousemove", onMouseMoveTopResize); document.removeEventListener("mouseup", onMouseUpTopResize); document.removeEventListener("mousemove", onMouseMoveBottomResize); document.removeEventListener("mouseup", onMouseUpBottomResize); document.removeEventListener("mousemove", onMouseMoveLeftResize); document.removeEventListener("mouseup", onMouseUpLeftResize); }; }, []); return ( <div className={styles.container}> <div ref={ref} className={styles.resizeable}> <div ref={refLeft} className={`${styles.resizer} ${styles.resizerl}`}></div> <div ref={refTop} className={`${styles.resizer} ${styles.resizert}`}></div> <div ref={refRight} className={`${styles.resizer} ${styles.resizerr}`}></div> <div ref={refBottom} className={`${styles.resizer} ${styles.resizerb}`}></div> {children} </div> </div> ); }; export default ResizeableComponents;
CSS样式文件(Resize.module.css)
.container { border-radius: 5px; width: 100vw; height: 100vh; background: #FFBC97; position: relative; } .resizeable { position: absolute; border: 2px solid #533535; width: 100px; height: 100px; border-radius: 3px; display: flex; justify-content: center; align-items: center; min-width: 15px; min-height: 15px; } .resizer { position: absolute; background: black; } .resizerr { cursor: col-resize; height: 100%; right: 0; top: 0; width: 5px; } .resizert { cursor: row-resize; height: 5px; left: 0; top: 0; width: 100%; } .resizerb { cursor: row-resize; height: 5px; left: 0; bottom: 0; width: 100%; } .resizerl { cursor: col-resize; height: 100%; left: 0; top: 0; width: 5px; }
内容的提问来源于stack exchange,提问作者Kartik Fulara
相关产品推荐
相关产品推荐

