Reputation: 1
This is a first time, when I use react hooks. I try to disable fetching, using a state (boolean). But there is a problem, because a function still see's initial state and execute function dozen times instead once. I can't see the point. In the react console the state is marked as true, and still pass my if statement
I noticed fact, that without listener on window it is working. So I tried to use event build in React - onScroll, but thats not resolve my issue - nothing happened. My version of React: 16.8.6
const [isFetchedData, setIsFetchedData] = useState(false);
const fetchBookData = async () => {
await setisFetchedData(true);
... - fetchedData
setTimeout(() => {
setIsFetchedData(false);
}, 5000); --- I would like to make available to fetch data again after 5 seconds.
};
useEffect(() => {
window.addEventListener("scroll", scrollHandler);
});
const scrollHandler = () => {
if (!isFetchedData) {
fetchBookData(); --- always executing
}
};
Fleischpflanzerl - I put that function in body of useEffect. The function scrollHandler in not executing now. Can you give me some of example how to fix that?
useEffect(() => {
return () => {
window.addEventListener("scroll", scrollHandler, true);
const scrollHandler = () => {
if (!isFetchedData) {
// fetchBookData();
console.log("fetch");
}
};
};
}, [isFetchedData]);
Upvotes: 0
Views: 4868
Reputation: 1637
I think you should use useRef
instead of useState
in this situation.
What's wrong when you call useState?
If you call setIsFetchedData
to set isFetchedData
, you trigger a re-render in your component. So every time you scroll, you trigger a component re-render.
The main problem is that isFetchedData
is just a boolean in your hook. Not a magical variable which instantly change it's value. (React only enqueues the re-render, it won't re-render immediately.)
But when scroll events occur multiple times the event handler function will see isFetchedData
as a static false
. And if it is false
you call fetch
once again.
The point is isFetchedData
won't change until the next re-render calls useState
once again.
Another problem in your code, you forgot to define a clean-up function and dependencies. In every render, React calls useEffect
so if you don't return a clean-up function and don't define the dependencies which tell React when to run this effect, you call the addEventListener
every time the component re-renders and attach the handler multiple times.
I made a working example with useRef, maybe is worth more than thousand words.
const useRef = React.useRef;
const useEffect = React.useEffect;
const useScrollFetch = () => {
const isFetchedData = useRef(false);
function fakeFetchBookData() {
console.log("fakeFetchBookData start...");
setTimeout(() => {
console.log("fakeFetchBookData end...");
}, 2000);
}
function fetchBookData() {
isFetchedData.current = true;
fakeFetchBookData();
setTimeout(() => {
console.log("isFetchedData reset");
isFetchedData.current = false;
}, 5000); // I would like to make available to fetch data again after 5 seconds.
}
const scrollHandler = event => {
if (!isFetchedData.current) {
console.log(
"scrollHandler: ",
event.type,
"isFetchedData: ",
isFetchedData
);
fetchBookData();
}
};
useEffect(() => {
window.addEventListener("scroll", scrollHandler, true);
return () => {
window.removeEventListener("scroll", scrollHandler, true);
};
}, []);
};
function App() {
console.log("App render...");
useScrollFetch();
return (
<div className="App" style={{ width: '10%' }}>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
dictum, enim vitae porttitor vulputate, sem nulla faucibus
felis, a faucibus ex lacus at mauris. Mauris scelerisque rhoncus
nisi vel commodo. Suspendisse varius lectus porta lectus
commodo, consectetur condimentum nibh euismod. Aenean cursus
nibh erat, non rhoncus arcu porta vel. Pellentesque maximus
mauris nec neque porttitor commodo. In condimentum hendrerit
augue, vitae lobortis est iaculis quis. Fusce vehicula sit amet
dui vel dignissim. Donec tempus hendrerit tristique. Nam
suscipit aliquet nisl eu malesuada.
</p>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
(I had to remove async/await because I can't embed the code here with these syntax, but this isn't an issue.)
I really suggest to read A Complete Guide to useEffect from Dan Abramov. It helped me a lot to better understand how useEffect works. :)
Upvotes: 2