Reputation: 1467
This is one of the first times I am actually using React Hooks properly in a project so bear with me if I am not quite there.
In the component below, my aim is to display the <HelperTooltip>
on load and when the scrolling div (not the window) scrolls I want to hide after it scrolls X amount of pixels.
My thought process is to create a useRef object on the scrolling <div/>
element, which then I can add an event listens with a callback function which then can toggle the state to hide the <HelperTooltip>
I have created a Codesandbox below to try and demonstrate what I am trying to do. As you can see in the demo the node.addEventListener('click')
is working fine, however when I try and call the node.addEventListener('scroll')
it is not firing.
I'm not sure if I taking the wrong approach or not, any help will greatly be appreciated. In the codesandbox demo it is the react image that I trying to hide on scroll, not the <HelperTooltip>
CodeSandbox link: 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"));
Upvotes: 9
Views: 19159
Reputation: 144
Here is the correct way to bind the addEventListener on div using useRef()
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(() => {
scrollContainer.current.addEventListener('scroll', onScroll);
return () => scrollContainer.current.removeEventListener('scroll', onScroll);
},[]);
return (
<div ref={scrollContainer}>
{props.children}
</div>
);
}
export default ScrollingWrapper;
Upvotes: 2
Reputation: 18770
Depending on your use case, it's usually also good to throttle scroll event listeners, so they don't run on every pixel change.
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>
);
};
The throttle
function is available in lodash.
Upvotes: 2
Reputation: 112867
An easier approach for this particular use case might be to use the onScroll
prop and use the scrollTop
property from the event target
to figure out if you should hide the image or not.
Example
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>
Upvotes: 8
Reputation: 495
In your example, the scroll is not triggered on the scrolling-element
but on the scrolling-container
so that's where you want to put your ref : https://codesandbox.io/s/ko4vm93moo :)
But as Throlle said, you could also use the onScroll prop !
Upvotes: 0