使用useRef将addEventListener('scroll')滚动至可滚动的<div>-React

时间:2019-03-26 11:35:00

标签: javascript reactjs typescript react-hooks

这是我第一次真正在项目中正确使用React Hooks之一,所以如果我不在那儿请多多包涵。

在下面的组件中,我的目标是在加载时显示<HelperTooltip>,并且当滚动div(而非窗口)滚动时,在滚动X像素后我想隐藏。

我的思维过程是在滚动的<div/>元素上创建一个useRef对象,然后可以使用回调函数添加事件侦听,然后该回调函数可以切换状态以隐藏<HelperTooltip>

我在下面创建了一个Codesandbox来尝试演示我正在尝试做的事情。如您在演示中所看到的,node.addEventListener('click')正常工作,但是当我尝试调用node.addEventListener('scroll')时却没有触发。

我不确定是否采用错误的方法,将不胜感激。在codesandbox演示中,它是我试图在滚动中隐藏的反应图像,而不是<HelperTooltip>

CodeSandbox链接:https://codesandbox.io/s/zxj322ln24

import React, { useRef, useCallback, useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const App = props => {
  const [isLogoActive, toggleLogo] = useState(true);

  const scrollElementRef = useCallback(node => {
    node.addEventListener("click", event => {
      console.log("clicked", event);
    });

    /* 
      I want to add the scroll event listener 
      here and the set the state isLogoActive to 
      false like the event listener above but the 'scroll' event
      is firing --- see below on line 21
    */

    // node.addEventListener("scroll", event => {
    //   console.log("scrolled", event);
    //   toggle log
    // });
  });

  return (
    <div className="scrolling-container">
      <div ref={scrollElementRef} className="scrolling-element">
        <p>top</p>

        {isLogoActive && (
          <div className="element-to-hide-after-scroll">
            <img
              style={{ width: "100px", height: "100px" }}
              src="https://arcweb.co/wp-content/uploads/2016/10/react-logo-1000-transparent-768x768.png"
            />
          </div>
        )}

        <p>bottom</p>
      </div>
    </div>
  );
};

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

4 个答案:

答案 0 :(得分:2)

对此特定用例更简单的方法可能是使用scrollTop道具并使用事件target中的const { useState } = React; const App = props => { const [isLogoActive, setLogoActive] = useState(true); const onScroll = e => { setLogoActive(e.target.scrollTop < 100); }; return ( <div onScroll={onScroll} style={{ height: 300, overflowY: "scroll" }}> <p style={{ marginBottom: 200 }}>top</p> <img style={{ width: 100, height: 100, visibility: isLogoActive ? "visible" : "hidden" }} src="https://arcweb.co/wp-content/uploads/2016/10/react-logo-1000-transparent-768x768.png" /> <p style={{ marginTop: 200 }}>bottom</p> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));属性来确定是否应该隐藏图像

示例

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
<button class="btn btn-primary follow">
    <span class="fa fa-user-plus text-muted"></span>
</button>

答案 1 :(得分:2)

根据您的使用情况,限制滚动事件侦听器通常也很好,这样它们就不会在每次像素更改时都运行。

const App = props => {
  const [isLogoActive, setLogoActive] = useState(true);

  const onScroll = useMemo(() => {
    const throttled = throttle(e => setLogoActive(e.target.scrollTop < 100), 300);
    return e => {
      e.persist();
      return throttled(e);
    };
  }, []);

  return (
    <div onScroll={onScroll}>
      <img
        style={{ visibility: isLogoActive ? 'visible' : 'hidden' }}
        src="https://arcweb.co/wp-content/uploads/2016/10/react-logo-1000-transparent-768x768.png"
      />
    </div>
  );
};

lodash中提供了throttle功能。

答案 2 :(得分:0)

在您的示例中,滚动不是在scrolling-element上触发的,而是在scrolling-container上触发的,因此您想在此放置引用:https://codesandbox.io/s/ko4vm93moo:)

但是正如Throlle所说,您也可以使用onScroll道具!

答案 3 :(得分:0)

这是使用useRef()在div上绑定addEventListener的正确方法

import React, { useState, useEffect, useCallback, useRef } from 'react';

function ScrollingWrapper(props) {
  const [hasScrolledDiv, setScrolled] = useState(false);
  const scrollContainer = useRef(null);
  const onScroll = useCallback((event) => {
    if(event.target.scrollTop > 125){
       setScrolled(true);
    } else if(event.target.scrollTop < 125) {
       setScrolled(false);
    }
}, []);

useEffect(() => {
    scrollContainerWrapper.current.addEventListener('scroll', onScroll);
    return () => scrollContainerWrapper.current.removeEventListener('scroll', onScroll);
},[]);

return (
    <div ref={scrollContainerWrapper}>
        {props.children}
    </div>
  );
 }

 export default ScrollingWrapper;