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

React中用.map()与内联事件实现可调整大小组件的问题

解决循环渲染可调整大小组件的平滑缩放问题

核心问题分析

你之前的实现存在两个关键问题导致无法平滑调整:

  • 内联onMouseMove仅在resizer元素范围内触发,鼠标移出后就停止响应
  • 没有维护每个独立item的缩放状态(初始宽度、鼠标起始位置),无法精准计算宽度变化

解决方案代码

React 逻辑代码

const App = () => {
  const data = ['item1', 'item2', 'item3', 'item4', 'item5'];
  // 存储每个item的宽度,初始值300px
  const [itemWidths, setItemWidths] = React.useState(
    data.reduce((acc, _, idx) => ({ ...acc, [idx]: 300 }), {})
  );
  // 记录当前正在调整的item索引
  const [resizingIdx, setResizingIdx] = React.useState(null);
  // 记录鼠标按下时的初始位置和对应item的宽度
  const [resizeStart, setResizeStart] = React.useState({ x: 0, width: 0 });

  // 鼠标按下时初始化缩放参数并绑定全局事件
  const handleMouseDown = (e, idx) => {
    setResizingIdx(idx);
    setResizeStart({
      x: e.clientX,
      width: itemWidths[idx]
    });
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
  };

  // 鼠标移动时计算宽度变化
  const handleMouseMove = (e) => {
    if (resizingIdx === null) return;
    // 计算鼠标偏移量,限制最小宽度避免组件过小
    const dx = e.clientX - resizeStart.x;
    const newWidth = Math.max(100, resizeStart.width + dx);
    setItemWidths(prev => ({ ...prev, [resizingIdx]: newWidth }));
  };

  // 鼠标松开时清理事件监听并重置状态
  const handleMouseUp = () => {
    setResizingIdx(null);
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  };

  return (
    <div className="App">
      {data.map((x, idx) => (
        <div 
          className="item" 
          key={idx}
          style={{ width: `${itemWidths[idx]}px` }}
        >
          {x}
          <div 
            className="resizer" 
            onMouseDown={(e) => handleMouseDown(e, idx)}
          ></div>
        </div>
      ))}
    </div>
  );
};

ReactDOM.render(
    <App />,
    document.getElementById("root")
);

CSS 样式代码

.App {
  text-align: center;
  padding: 20px;
}

.item{
  position: relative;
  border: 1px solid black;
  margin: 10px auto;
  border-radius: 3px;
  padding: 10px;
  box-sizing: border-box; /* 保证padding不影响宽度计算 */
}

.resizer {
  position: absolute;
  background: #333;
  cursor: col-resize;
  height: 100%;
  right: 0;
  top: 0;
  width: 8px;
  border-radius: 0 3px 3px 0;
}

.resizer:hover {
  background: #555;
}

HTML 基础结构

<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>

关键实现要点

  • 状态管理:用itemWidths对象存储每个item的宽度,通过索引关联,规避循环中使用ref的React规范问题
  • 全局事件绑定:在mousedown时绑定document级别的mousemove和mouseup,确保鼠标移出resizer后仍能持续响应,实现平滑拖动
  • 初始参数记录:按下鼠标时记录当前鼠标位置和item宽度,后续通过偏移量计算新宽度,避免直接读取DOM样式导致的延迟
  • 边界限制:用Math.max设置最小宽度,防止组件缩至不可用状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 20:15:43