React中的滚动到视图功能

7
我正在制作一个简单的 react 应用程序,其中有两个不同的 div
其中一个包含选择输入和所选列表,
  <div id="container">
    <div className="_2iA8p44d0WZ">
      <span className="chip _7ahQImy">Item One</span>
      <span className="chip _7ahQImy">Item Two</span>
      <span className="chip _7ahQImy">Item Three</span>
      <span className="chip _7ahQImy">Item Four</span>
      <span className="chip _7ahQImy">Item Five</span>
      <input
        type="text"
        className="searchBox"
        id="search_input"
        placeholder="Select"
        autoComplete="off"
        value=""
      />
    </div>
  </div>

另一个将把所选选项列为 fieldset

  <div>
    {selectedElements.map((item, i) => (
      <div key={i} className="selected-element" ref={scrollDiv}>
        <fieldset>
          <legend>{item}</legend>
        </fieldset>
      </div>
    ))}
  </div>

根据这个解决方案,我已经将createRef添加到选定的元素中,如下所示:

<div key={i} className="selected-element" ref={scrollDiv}>
</div>

然后我使用了Javascript的查询方法来获取DOM元素,例如:

  const chipsArray = document.querySelectorAll("#container > div > .chip");

为所有元素添加了点击事件监听器,例如:

  chipsArray.forEach((elem, index) => {
    elem.addEventListener("click", scrollSmoothHandler);
  });

那么像 scrollSmoothHandler 这样的函数:

const scrollDiv = createRef();

  const scrollSmoothHandler = () => {
    console.log(scrollDiv.current);
    if (scrollDiv.current) {
      scrollDiv.current.scrollIntoView({ behavior: "smooth" });
    }
  };

但是这并没有按照预期的方式工作。

要求:

点击第一个div中的任何项目时,另一个div中相关的fieldset需要平滑滚动

例如: 如果用户单击元素Item Four,则相关的fieldset需要滚动进入。这里的fieldset与标题为Item Four的内容相关。

我认为还要在React上使用js dom查询方法,并且它似乎不是一种React的实现方式。请问有谁可以帮助我在选择项目上单击以滚动到相关的fieldset呢?

Edit React Functional Component (forked)


抱歉,昨天我看到你的消息晚了,而且一直忙到刚才为止。下面是我认为可以解决你问题的答案。 - Drew Reese
@DrewReese,再次抱歉打扰您,但现在这里有一个不同的情景问题,请参考https://dev59.com/3b7pa4cB1Zd3GeqPv1Dd。 - Undefined
@DrewReese,请告诉我如何处理上面发布的问题。我收到的数据是一个数组,我将迭代并在每行上显示保存按钮。我需要将每行的数据提交为一个对象,例如{ ContactMode: 1 }... - Undefined
1个回答

9

问题

  1. React.createRef 只适用于基于类的组件。如果在函数式组件中使用,则每个渲染周期都会重新创建 ref。
  2. 不要使用 DOM 查询选择器将 onClick 侦听器附加到 DOM 元素。它们位于 React 之外,因此您需要记住清理它们(即删除它们),以避免内存泄漏。使用 React 的 onClick 属性。
  3. 当映射 selectedElements 时,您会将相同的 ref 附加到每个元素,因此 UI 得到的是最后设置的元素。

解决方案

  1. 在函数式组件体中使用 React.useRef 存储一个 React ref 数组,以附加到要滚动查看的每个元素。
  2. 直接将 scrollSmoothHandler 附加到每个 spanonClick 属性。
  3. 将第一步创建的 ref 数组中的每个 ref 附加到要滚动到的每个映射字段集。

代码

import React, { createRef, useRef } from "react";
import { render } from "react-dom";

const App = () => {
  const selectedElements = [
    "Item One",
    "Item Two",
    "Item Three",
    "Item Four",
    "Item Five"
  ];

  // React ref to store array of refs
  const scrollRefs = useRef([]);

  // Populate scrollable refs, only create them once
  // if the selectedElements array length is expected to change there is a workaround
  scrollRefs.current = [...Array(selectedElements.length).keys()].map(
    (_, i) => scrollRefs.current[i] ?? createRef()
  );

  // Curried handler to take index and return click handler
  const scrollSmoothHandler = (index) => () => {
    scrollRefs.current[index].current.scrollIntoView({ behavior: "smooth" });
  };

  return (
    <div>
      <div id="container">
        <div className="_2iA8p44d0WZ">
          {selectedElements.map((el, i) => (
            <span
              className="chip _7ahQImy"
              onClick={scrollSmoothHandler(i)} // <-- pass index to curried handler
            >
              {el}
            </span>
          ))}
          <input
            type="text"
            className="searchBox"
            id="search_input"
            placeholder="Select"
            autoComplete="off"
            value=""
          />
        </div>
      </div>
      <div>
        {selectedElements.map((item, i) => (
          <div
            key={i}
            className="selected-element"
            ref={scrollRefs.current[i]} // <-- pass scroll ref @ index i
          >
            <fieldset>
              <legend>{item}</legend>
            </fieldset>
          </div>
        ))}
      </div>
    </div>
  );
};

解决方案 #2

由于您无法更新 id="container"div 中的任何元素,并且所有的 onClick 处理程序需要通过查询 DOM 进行附加,因此您仍然可以使用柯里化的 scrollSmoothHandler 回调并在作用域中封装索引。您需要一个 useEffect 钩子来查询 DOM,在初始渲染后使 span 被挂载,并且需要一个 useState 钩子来存储“已加载”状态。该状态是必要的,以触发重新渲染并重新封装 scrollRefsscrollSmoothHandler 回调中。

const App = () => {
  const selectedElements = [
    "Item One",
    "Item Two",
    "Item Three",
    "Item Four",
    "Item Five"
  ];

  const [loaded, setLoaded] = useState(false);
  const scrollRefs = useRef([]);

  const scrollSmoothHandler = (index) => () => {
    scrollRefs.current[index].current.scrollIntoView({ behavior: "smooth" });
  };

  useEffect(() => {
    const chipsArray = document.querySelectorAll("#container > div > .chip");

    if (!loaded) {
      scrollRefs.current = [...Array(chipsArray.length).keys()].map(
        (_, i) => scrollRefs.current[i] ?? createRef()
      );

      chipsArray.forEach((elem, index) => {
        elem.addEventListener("click", scrollSmoothHandler(index));
      });
      setLoaded(true);
    }
  }, [loaded]);

  return (
    <div>
      <div id="container">
        <div className="_2iA8p44d0WZ">
          <span className="chip _7ahQImy">Item One</span>
          <span className="chip _7ahQImy">Item Two</span>
          <span className="chip _7ahQImy">Item Three</span>
          <span className="chip _7ahQImy">Item Four</span>
          <span className="chip _7ahQImy">Item Five</span>
          <input
            type="text"
            className="searchBox"
            id="search_input"
            placeholder="Select"
            autoComplete="off"
            value=""
          />
        </div>
      </div>
      <div>
        {selectedElements.map((item, i) => (
          <div key={i} className="selected-element" ref={scrollRefs.current[i]}>
            <fieldset>
              <legend>{item}</legend>
            </fieldset>
          </div>
        ))}
      </div>
    </div>
  );
};

Edit scroll-into-view-in-react


非常感谢你,瑞斯。请问您能否看一下上面的评论并回答这个问题吗?我在真正的应用程序中无法修改这行代码onClick={scrollSmoothHandler(i)},因为我在那里使用了一个库。 - Undefined
一般来说,我需要在 DOM 加载后添加点击事件,但是无论如何我都需要动态地进行操作,因为这些并不是真实应用程序中硬编码的 JSX。 - Undefined
谢谢Reese,在最后,要求是如果我点击库中选定芯片中的任何一个(关闭图标之外)时,所选项目的字段集需要滚动。 因此,我已经将那个库的简单代码放在了问题中的codesandbox中,我无法单独修改第一个div的代码.. 所以我也使用DOM在我的问题codesandbox中制作了点击事件处理程序.. 我很抱歉没有提到.. 请确保仔细阅读场景,并让我知道更新的解决方案.. - Undefined
嗨兄弟,我遇到的一个与此帖子相关的问题是,我在页面上有一个固定的标题,这使得滚动项在特定点/高度之前被隐藏。 工作示例:https://codesandbox.io/s/scroll-into-view-in-react-forked-kmjzh .. 我该如何获取固定标题的高度,并使滚动元素从其顶部可见(而不是从其他地方).. 如果您需要将其作为单独的问题,请告诉我,但我相信您可以通过给出沙箱链接来理解问题。 - Undefined
问题是我添加了一个固定的页眉,这不允许滚动元素从顶部可见。请帮帮我,兄弟。如果需要,我可以将其发布为单独的问题,但我谦恭地请求您的解决方案。 - Undefined
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接