如何在React中创建可调整大小的组件



如标题所述。我想创建一个React组件,让我可以通过拖动来调整其宽度,就像windows操作系统中的窗口一样。处理这个问题的最佳方法是什么?

编辑
我介绍了我目前对此事的处理方法:

首先,我在容器的右上角放置了一个"dragger"元素。当我在该元素上按下鼠标时,我想创建一个mousemove事件监听器,它将修改相对于容器边缘初始X位置的光标X坐标的containerWidth。我已经让事件监听器在按下鼠标按钮后启动并记录坐标,但不幸的是,由于某种原因,在取消按下鼠标后没有删除事件(mouseUp事件(,这不是我想要的。任何建议都很感激,还有那些关于我未来可能期待的与这个主题相关的一些问题的建议。谢谢

type Props = MasterProps & LinkStateToProps & LinkDispatchToProps;
const Test3 = (Props: Props) => {
const [containerWidth, setContainerWidth] = React.useState(640)
const [isBeingStretched, setIsBeingStretched] = React.useState(false);
const masterRef = React.useRef(null);

const logMousePosition = React.useCallback((event:MouseEvent)=>{
console.log(event.clientX);
},[])

const handleMouseDown=()=>{
document.addEventListener('mousemove', logMousePosition);
masterRef.current.addEventListener('mouseup', ()=>{
document.removeEventListener('mouseup', logMousePosition)
})
}
const handleMouseUp = () => { 
document.removeEventListener('mousemove', logMousePosition);
}
return (
<div className="master-wrapper" ref={masterRef}>
<div className="stretchable-div" style={{ width: `${containerWidth}px` }}>
<div className="dragger-wrapper">
<h2>This is supposed to change width</h2>
<div className="dragger"
id="dragger"
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}/>
</div>
</div>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Test3);

我以前从未做过这样的事情,所以我决定尝试一下,结果用React状态管理实现起来非常简单。我能理解为什么如果你是React的新手,你可能不知道从哪里开始,这没关系,尽管在我研究我的解决方案之前需要注意两件事:

  1. document.getElementByIddocument.addEventListener这样的语句将不再按预期运行。使用React,您正在操作一个虚拟DOM,它会为您更新实际的DOM,您应该尽可能让它做到这一点
  2. 使用refs来绕过这一事实是不好的做法。它们的行为方式可能与上述语句类似,但这不是它们的预期用例。阅读文档中关于ref良好用例的内容

以下是我的演示的JSX部分:

return (
<div className="container" onMouseMove={resizeFrame} onMouseUp={stopResize}>
<div className="box" style={boxStyle}>
<button className="dragger" onMouseDown={startResize}>
Size Me
</button>
</div>
</div>
);

我们将需要三个不同的事件——onMouseDownonMouseMoveonMouseUp——来跟踪调整大小的不同阶段。你已经在自己的代码中走了这么远。在React中,我们将所有这些声明为元素本身的属性,尽管它们实际上并不是内联函数。React在后台为我们添加了它们作为事件监听器。

const [drag, setDrag] = useState({
active: false,
x: "",
y: ""
});
const startResize = e => {
setDrag({
active: true,
x: e.clientX,
y: e.clientY
});
};

我们将使用一些状态来跟踪正在进行的调整大小。我将所有内容浓缩到一个对象中,以避免膨胀并使其更可读,尽管如果有其他挂钩(如useEffectuseMemo(依赖于该状态,这并不总是理想的。第一个事件只是保存用户鼠标的初始x和y位置,并将活动设置为true以供下一个事件参考。

const [dims, setDims] = useState({
w: 200,
h: 200
});
const resizeFrame = e => {
const { active, x, y } = drag;
if (active) {
const xDiff = Math.abs(x - e.clientX);
const yDiff = Math.abs(y - e.clientY);
const newW = x > e.clientX ? dims.w - xDiff : dims.w + xDiff;
const newH = y > e.clientY ? dims.h + yDiff : dims.h - yDiff;
setDrag({ ...drag, x: e.clientX, y: e.clientY });
setDims({ w: newW, h: newH });
}
};

第二个状态将初始化,然后随着元素值的变化更新元素的尺寸。这可以使用您想要的任何度量,尽管它必须与某些CSS属性相关。

resizeFrame功能执行以下操作:

  1. 通过析构函数赋值使drag的属性易于使用。这将使代码更可读,更易于键入
  2. 检查调整大小是否处于活动状态。onMouseMove将为鼠标在相关元素上移动的每一个像素激发,因此我们希望确保它得到适当的调节
  3. 使用Math.abs()可以将当前鼠标位置和保存的鼠标位置之间的差值作为正整数。这将使我们不必进行第二轮条件语句
  4. 根据新鼠标在任一轴上的位置是大于还是小于上一个位置,使用turnary语句添加或减去尺寸的差异
  5. 使用扩展运算符...drag的不相关部分保持原样,用新值设置状态
const stopResize = e => {
setDrag({ ...drag, active: false });
};
const boxStyle = {
width: `${dims.x}px`,
height: `${dims.y}px`
};

然后,我们只需在用户完成后将拖动状态的活动设置为false。JS样式的对象被传递给具有适当状态变量的元素,以便自动处理。

这是完成效果的代码沙盒。


这样做的缺点之一是,它基本上需要将mouseMove事件侦听器分配给最大的站点容器,因为在调整大小的过程中,鼠标不会停留在框的边界内。如果您希望有多个具有相同功能的元素,尽管没有什么是通过良好的状态管理无法解决的,但这可能是一个问题。您可能会对此进行微调,使鼠标始终停留在拖动元素上,尽管这需要更复杂的实现。

最新更新