滚动到视图以做出反应 [英] Scroll into view in react

查看:58
本文介绍了滚动到视图以做出反应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个简单的react应用,其中有两个不同的 div's ..

具有选择输入和选定列表的一个,

 < div id ="container">< div className ="_ 2iA8p44d0WZ">< span className =芯片_7ahQImy">项目One</span>< span className =芯片_7ahQImy">项目2</span>< span className =芯片_7ahQImy">项目3</span>< span className =芯片_7ahQImy">项目四</span>< span className =芯片_7ahQImy">项目5</span><输入type ="text"className =" searchBox"id ="search_input";占位符=选择"autoComplete =关闭".值="/></div></div> 

另一个人将选择的选项列为 fieldset

 < div>{selectedElements.map((item,i)=>(< div键= {i} className =选定元素";ref = {scrollDiv}>< fieldset>< legend> {item}</legend></fieldset></div>))}</div> 

基于

解决方案

问题

  1. React.createRef 实际上仅在基于类的组件中有效.如果在功能组件主体中使用,则将在每个渲染周期重新创建引用.
  2. 不要使用DOM查询选择器将 onClick 侦听器附加到DOM元素.这些住在外面的生物会做出反应,您需要记住将它们清理干净(即删除它们),以免发生内存泄漏.使用React的 onClick 道具.
  3. 映射 selectedElements 时,您将 same 引用附加到每个元素,因此最后一组是用户界面获得的一组.

解决方案

  1. 在功能组件主体中使用 React.useRef 来存储一组反应引用,以附加到要滚动到视图中的每个元素.
  2. scrollSmoothHandler 直接附加到每个 span onClick 道具上.
  3. 将1.中创建的ref数组中的每个ref附加到要滚动到的每个映射字段集.

代码

 从"react"导入React,{createRef,useRef};从"react-dom"导入{render};const App =()=>{const selectedElements = [项目一",第二项",第3项",第四项",<项目五>];//反应参考以存储参考数组const scrollRefs = useRef([]);//填充可滚动引用,仅创建一次//如果selectedElements数组的长度预计会发生变化,则有一种解决方法scrollRefs.current = [... Array(selectedElements.length).keys()].map((_,i)=>scrollRefs.current [i]?createRef());//咖喱句柄获取索引并返回click句柄const scrollSmoothHandler =(索引)=>()=>{scrollRefs.current [index] .current.scrollIntoView({behavior:"smooth"});};返回 (< div>< div id =容器">< div className ="_ 2iA8p44d0WZ">{selectedElements.map((el,i)=>(< spanclassName =芯片_7ahQImy";onClick = {scrollSmoothHandler(i)}//<-将索引传递给已处理的处理程序>{el}</span>))}<输入type ="text"className =" searchBox"id ="search_input";占位符=选择"autoComplete =关闭".值="/></div></div>< div>{selectedElements.map((item,i)=>(÷ div键= {i}className =选定元素";ref = {scrollRefs.current [i]}//<-通过滚动ref @ index i>< fieldset>< legend> {item}</legend></fieldset></div>))}</div></div>);}; 

解决方案2

由于您无法使用 id ="container" 更新 div 中的任何元素,因此所有 onClick 处理程序都需要通过查询DOM附加,您仍然可以使用咖喱的 scrollSmoothHandler 回调并将索引包含在作用域中.您需要一个 useEffect 钩子来查询DOM,然后在初始渲染后查询DOM,以便跨度已被挂载,还需要一个 useState 钩子来存储DOM.已加载"状态.该状态是触发重新渲染并重新封装在 scrollSmoothHandler 回调中的 scrollRefs 上所必需的.

  const App =()=>{const selectedElements = [项目一",第二项",第3项",第四项",<项目五>];const [loaded,setLoaded] = useState(false);const scrollRefs = useRef([]);const scrollSmoothHandler =(索引)=>()=>{scrollRefs.current [index] .current.scrollIntoView({behavior:"smooth"});};useEffect(()=> {const chipArray = document.querySelectorAll(#container> div>..chip");如果(!已加载){scrollRefs.current = [... Array(chipsArray.length).keys()].map((_,i)=>scrollRefs.current [i]?createRef());chipArray.forEach((elem,index)=> {elem.addEventListener("click",scrollSmoothHandler(index));});setLoaded(true);}},[已加载]);返回 (< div>< div id =容器">< div className ="_ 2iA8p44d0WZ">< span className =芯片_7ahQImy">项目One</span>< span className =芯片_7ahQImy">项目2</span>< span className =芯片_7ahQImy">项目3</span>< span className =芯片_7ahQImy">项目四</span>< span className =芯片_7ahQImy">项目5</span><输入type ="text"className =" searchBox"id ="search_input";占位符=选择"autoComplete =关闭".值="/></div></div>< div>{selectedElements.map((item,i)=>(< div键= {i} className =选定元素";ref = {scrollRefs.current [i]}>< fieldset>< legend> {item}</legend></fieldset></div>))}</div></div>);}; 

I am making a simple react app where there are two different div's..

One with select input and selected list,

  <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>

Another will list down the selected option as fieldset,

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

Based on this solution, I have added createRef to the selected element like,

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

Then I took Javascript query methods to get DOM elements like,

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

Added click event listener to all the elements like,

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

Then scrollSmoothHandler like,

const scrollDiv = createRef();

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

But this doesn't work the way as expected.

Requirement:

On click over any item in first div, then its related fieldset needs to get smooth scrolled in another div..

Eg: If user click on the element Item Four under <div id="container"> ... <span className="chip _7ahQImy">Item Four</span> ... </div>

then the related fieldset needs to get scrolled into. Here the fieldset with legend as Item Four ..

I think also making the js dom query methods on react and it seems not a react way of implementation. Can anyone please kindly help me to achieve the result of scrolling to a related fieldset on click over the selected item..

解决方案

Issue

  1. React.createRef is really only valid in class-based components. If used in a functional component body then the ref would be recreated each render cycle.
  2. Don't use a DOM query selector to attach onClick listeners to DOM elements. These live outside react and you'd need to remember to clean them up (i.e. remove them) so you don't have a memory leak. Use React's onClick prop.
  3. When the selectedElements are mapped you attach the same ref to each element, so the last one set is the one your UI gets.

Solution

  1. Use React.useRef in the functional component body to store an array of react refs to attach to each element you want to scroll into view.
  2. Attach the scrollSmoothHandler directly to each span's onClick prop.
  3. Attach each ref from the ref array created in 1. to each mapped field set you want to scroll to.

Code

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>
  );
};

Solution #2

Since you can't update any elements in the div with id="container" and all the onClick handlers need to be attached via querying the DOM, you can still use a curried scrollSmoothHandler callback and enclose an index in scope. You'll need an useEffect hook to query the DOM after the initial render so the spans have been mounted, and an useState hook to store a "loaded" state. The state is necessary to trigger a rerender and re-enclose over the scrollRefs in the scrollSmoothHandler callback.

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>
  );
};

这篇关于滚动到视图以做出反应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆