【问题标题】:React: getBoundingClientRect get position of previous renderReact:getBoundingClientRect 获取先前渲染的位置
【发布时间】:2021-08-17 00:17:14
【问题描述】:

首先,code sandbox 演示问题。

我们有 2 个组件 - DraggableBoxreact-draggable 的包装器,SimpleArrowreact-xarrows 的简化版本(我是作者)。

我们可以看到一个视觉错误,这是因为 DraggableBoxSimpleArrow 都基于相同的 DOM 框架(以及相同的反应渲染阶段 - useEffect)更新它们的状态,而 SimpleArrow 应该基于DraggableBox的位置。

如果我们对“SimpleArrow”上的每个渲染强制执行 2 次渲染,然后在第 2 次渲染时“SimpleArrow”将读取更新后的“DraggableBox”位置,我们可以解决这个问题。我们可以通过编写自定义钩子来强制进行 2 次渲染:

export const useMultipleRenders = (renders = 2, effect = useEffect) => {
  const [, setRender] = useState({});
  const reRender = () => setRender({});
  const count = useRef(0);
  effect(() => {
    if (count.current != renders) {
      reRender();
      count.current += 1;
    } else {
      count.current = 0;
    }
  });
};

现在我们将在SimpleArrow 上消耗useMultipleRenders() 并且视觉故障将得到修复。

here's 带有修复程序的代码沙箱。你可以看到这个丑陋的解决方法有效。

这实际上在 React 中访问 DOM 时一直发生。 您在渲染期间使用 ref (useRef) 值访问 dom,在此渲染期间,您只能访问 dom 中当前的内容,这是上一次渲染的结果,但您实际上需要当前渲染!

例如,在SimpleArrow 中,我在svg 的内部使用getBoundingClientRect 来确定svg 的高度和宽度:

const SimpleArrow = (props) => {
  const svgInnersRef = useRef<SVGGElement>(null);
  const {
    width: gWidth,
    height: gHeight
  } = svgInnersRef.current?.getBoundingClientRect() ?? { width: 0, height: 0 };
  return (
    <svg
      width={gWidth}
      height={gHeight}
      // ...
    >
      <g ref={svgInnersRef}>
      {/* ... */}
      </g>
    </svg>
  );
};

但为了确保高度和宽度得到更新,我必须双重渲染,以便在最后一次渲染时获得正确的尺寸。

另一件事是useMultipleRenders 的实现并不安全,因为它会在渲染期间更改参考值。 React 核心成员 claims 在渲染期间设置 ref 值是不安全的。

我能做什么?有什么选择?

TLDR;

  1. 我能否在不手动重新渲染的情况下获得 DOM 元素的最新位置?
  2. 如何在渲染期间手动重新渲染而不更改 ref 值(因为它不安全 - 在 React 严格模式下它将被调用两次,通常只调用一次)?

【问题讨论】:

    标签: javascript reactjs getboundingclientrect


    【解决方案1】:

    这是一个有趣的问题,我不确定我是否完全理解,但这里有一个镜头。

    您可以尝试在SimpleArrow.js 中添加一个drag 事件侦听器。

    当有拖动时,您会检查它是否是您的可拖动框之一,如果是,则使用 setState 更新它们的位置,这应该会触发您的 svg 组件的重新渲染,并使用新的盒子的位置。

    【讨论】:

    • 感谢拍摄的朋友,但猜想您没有完全理解 - 请再次阅读问题和我提出的解决方案,看看您是否可以提出替代方案。另外 - 请不要考虑使用外部反应事件侦听器(如 elem.addEventListener),因为它在我的组件的生命周期之外工作
    猜你喜欢
    • 2015-01-04
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 2020-08-16
    相关资源
    最近更新 更多