I am trying to have useEffect run every time it changes a variable, but changes to the variable are lost on every re-render.
If I try to store the variable between renders using useRef, useEffect doesn't run because it cannot detect changes to a ref. I found an article that shows how to effectively pass a ref to useEffect but I am not sure how to use this in the context of my problem.
here is my code where a ref is passed to useEffect:
import { useRef, useEffect, useLayoutEffect } from "react";
import "./styles.css";
export default function App() {
const divOffset = useRef(0);
let scrollPosition = 0;
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
});
useLayoutEffect(() => {
divOffset.current = linearInterpolation(
divOffset.current,
scrollPosition,
0.07
);
divOffset.current = Math.floor(divOffset.current * 100) / 100;
document.querySelector(
"#main"
).style.transform = `translateX(-${divOffset.current}px);`;
}, [scrollPosition]);
const handleScroll = (event) => {
scrollPosition = window.scrollY;
};
function linearInterpolation(x1, x2, easingValue) {
return (1 - easingValue) * x1 + easingValue * x2;
}
return (
<div id="main">
<div className="layer1" />
<div className="layer2" />
<div className="layer3" />
</div>
);
}
In React,
useEffectruns when the dependencies specified in its dependency array change. However, as you've noticed, changes to variables stored withuseRefare not considered dependencies by default, souseEffectwon't run when they change. To makeuseEffectrun every time a variable stored withuseRefchanges, you can use a combination ofuseEffectanduseRefto capture the previous value and compare it to the current value. Here's how you can modify your code to achieve this:In this modified code:
Add a
scrollPositionRefusinguseRefto store thescrollPosition. This allows us to track changes toscrollPositionacross re-renders.In the
useLayoutEffect, change the dependency array to[divOffset]. This means that the effect will run wheneverdivOffsetchanges. Since we're updatingdivOffset.currentwithin the effect based onscrollPositionRef.current, it will run wheneverscrollPositionRef.currentchanges.In the
handleScrollfunction, updatescrollPositionRef.currentwith the new scroll position.This way, the
useLayoutEffectwill run every timescrollPositionRef.currentchanges, effectively achieving your goal of running the effect whenever thescrollPositionchanges.