I'm trying to make a component that updates its source image on scroll when it gets referenced in its div tag ' infoBG 'in my main component. The function seems to work but I guess the return statement is overriding the function and just returns the original image without updating. What's the right way to do this so the function updates the first returned image?
function Infoimg () {
window.addEventListener("scroll", event => {
var scrollValue = window.scrollY;
var image = document.getElementById('infoBG');
if (scrollValue>100){
image.setAttribute = ('src','images/ccpLogo.svg');
}else{
image.setAttribute = ('src','images/Passport Stock Photo.svg');
}
console.log(image)
console.log(scrollValue);
});
return(
<img src={'images/Passport Stock Photo.svg'} alt='Info-Background'/>
)
}
CodePudding user response:
It is generally a bad idea to manipulate the DOM directly in React. Instead, rely on hooks and states to update variables that will be interpolated into your template instead.
In this case, it makes sense to create a new state using the useState
hook to allow reading and writing of your image URL, and then use the setter returned by useState
to update the value.
Moreover, some handful tips:
- You should use
useEffect()
and add the event listener on render - You should also return a function in
useEffect()
to ensure that your scroll event listener is properly removed to avoid potential memory leaks and allow for garbage collection - You can simplify your if/else statement into a simple ternary statement
- You might want to manually invoke
onScroll()
for the first time, especially if the user may arrive at the page with a cached scroll position (i.e. user does not start from top of page)
With all these points in mind, here is an improved version of your code:
const [image, setImage] = useState('images/Passport Stock Photo.svg');
useEffect(() => {
const onScroll = () => {
setImage(window.scrollY > 100 ? 'images/ccpLogo.svg' : 'images/Passport Stock Photo.svg');
};
// Note: Listen to scroll event
window.addEventListener('scroll', onScroll);
// Optional: Handle scenario where user arrives at a pre-scrolled position (non-zero scrollY on load)
onScroll();
// Note: Clean up to avoid memory leaks
return () => {
window.removeEventListener('scroll', onScroll);
};
}, []);
return (
<img src={image} alt="Info-Background" />
);
CodePudding user response:
You basically need to use useRef
, which could be better for performance. Remember that useState
will trigger a re-render. But in fact, you just need to update the source of your image. I would use something like this.
import { useCallback, useEffect, useRef } from "react";
import "./styles.css";
export default function App() {
const imgRef = useRef();
const middleRef = useRef();
const onScroll = useCallback(() => {
if (imgRef != null) {
var scrollValue = window.scrollY;
if (scrollValue > 100) {
imgRef.current.src = "https://picsum.photos/id/237/200/300";
} else {
imgRef.current.src = "https://picsum.photos/seed/picsum/200/300";
}
console.log(scrollValue);
}
}, []);
useEffect(() => {
window.addEventListener("scroll", onScroll, true);
return () => {
window.removeEventListener("scroll", onScroll, true);
};
}, [onScroll]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div ref={middleRef} className="middle" />
<img
ref={imgRef}
src={"https://picsum.photos/seed/picsum/200/300"}
alt="Info-Background"
/>
</div>
);
}
Here is the link. You can check it working on codesandbox. https://codesandbox.io/s/ecstatic-https-hzi5n