在 React 中使用 document.querySelector?我应该使用 refs 吗?如何?

IT技术 javascript reactjs ref use-effect js-scrollintoview
2021-04-01 18:56:46

我现在正在用 React 构建一个旋转木马。要滚动到我使用的单个幻灯片,document.querySelector如下所示:

useEffect(() => {
    document.querySelector(`#slide-${activeSlide}`).scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'nearest'
    });
  }, [activeSlide]);

这是不好的做法吗?毕竟,我在这里直接访问DOM?这样做的 React 方式是什么?

编辑:完整return方法

return (
    <>
      <button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
      <Wrapper id="test">
        {children.map((child, i) => {
          return (
            <Slide id={`slide-${i}`} key={`slide-${i}`}>
              {child}
            </Slide>
          );
        })}
      </Wrapper>

      <button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
    </>
  );
2个回答

我无法回答是否为此使用 refs 的“应该”部分,除非您这样做,id除非您将它们用于其他用途,否则您不需要这些值。

但这是你的方式:

  1. 使用useRef(null)创建裁判。

    const activeSlideRef = useRef(null);
    
  2. 把它放在Slide当前活动的

    <Slide ref={i === activeSlide ? activeSlideRef : null} ...>
    
  3. 在您的 中useEffect,使用 ref 的current属性

    useEffect(() => {
        if (activeSlideRef.current) {
            activeSlideRef.current.scrollIntoView({
              behavior: 'smooth',
              block: 'nearest',
              inline: 'nearest'
            });
        }
    }, [activeSlide]);
    

    (我认为这activeSlide是该效果的合理依赖。您不能使用 ref,ref 本身不会改变......)

现场示例,div为了方便起见,我已将您的一些组件转换为s:


在您问过的评论中:

您知道是否可以在useEffect此处禁用第一次渲染吗?

为了保留每个组件的非状态信息,有趣的是您使用useRef. 对文档useRef指出,这不只是对DOM元素的引用,它也是每个组件的非国有的数据。所以你可以有

const firstRenderRef = useRef(true);

然后在您的useEffect回调中,检查firstRenderRef.current &mndash; 如果是true,则设置它false,否则进行滚动:


作为一个思想实验,我写了一个钩子来简化人体工程学:

function useInstance(instance = {}) {
    // assertion: instance && typeof instance === "object"
    const ref = useRef(instance);
    return ref.current;
}

用法:

const inst = useInstance({first: true});

在 中useEffect,如果inst.first为真,则执行inst.first = false;否则,进行滚动。

居住:

这太神奇了,非常感谢。我希望我能更多地支持这个答案!!
2021-05-31 18:56:46
Refs 更可靠地获取您关心的特定 DOM 节点。如果有多个元素与查询匹配,使用querySelector或类似(getElementsByClassgetElementById)可能会产生意想不到的结果,特别是如果元素之外有一个元素与查询匹配。如果您正在编写一个会在许多地方多次出现的组件,那么 refs 就更像是一个明确的赢家。
2021-06-01 18:56:46
非常感谢,这有效!您知道是否可以在useEffect此处禁用第一次渲染吗?
2021-06-03 18:56:46
@R.Kohlisch - 我已经添加到答案的末尾来展示如何做到这一点。
2021-06-12 18:56:46
此答案中的第 2 步(有条件地分配参考)绝对是天才。我一直在使用自定义 typeahead 争取好几天,我只想为键盘事件调用一些自定义滚动逻辑,因为它会因鼠标滚动而出错。因此,需要在父级中对孩子进行引用,并且此解决方案非常有效。好东西。
2021-06-16 18:56:46

添加到已接受的答案并尝试回答问题的“应该”部分,使用 refs 进行 DOM 操作:

  • refs 更容易在线性时间内唯一标识 + 选择相应的元素(与 id 相比,多个元素可能错误地具有相同的 + 值,而 document.querySelector 需要扫描 DOM 以选择正确的元素)
  • refs 知道 react 组件的生命周期,因此 react 将确保在组件卸载时将 refs 更新为 null 并提供更多开箱即用的便利。
  • refs 作为一个概念 + 语法是平台无关的,所以你可以在 react native 和浏览器中使用相同的理解,而查询选择器是浏览器的东西
  • 对于没有 DOM 的 SSR,仍然可以使用 refs 来定位 react 元素

当然,使用查询选择器并没有错,如果您通常在react世界中使用它,它不会破坏您的功能,但最好使用框架提供的东西,因为它在大多数情况下具有一些默认优势。