Reputation: 1661
I have a TimeLine component that renders posts 5 at a time
and when the user scrolls to the end of the page the throttle function is called.
I am trying to throttle the request to a maximum of once per second but The API is called multiple times
I tried several solutions like:
making a global flag that won't let you use the function unless a certain timeout has passed "It doesn't make sense because I am using the throttle to do that but I tried to check if my implementation is wrong"
declaring the throttle function alone outside the component
TimeLine.js
export default function TimeLine() {
const dispatch = useDispatch();
const posts = useSelector(state => state.postReducer.posts);
const loaded = useSelector(state => state.postReducer.loaded);
const TIMEOUT = 1000;
// Loaded keeps track of loaded posts
if(loaded === 0){
API.Post.getAllPosts(loaded)(dispatch);
}
if(loaded > 5){
if(loaded % 5 === 0) window.scrollTo(0, document.body.scrollHeight * 0.6)
}
window.addEventListener('scroll', () => throttledReq(loaded, TIMEOUT, dispatch));
const classes = useStyle();
return (
<div>
<NewPost />
{(posts && posts.length > 0)?posts.map((post, index) => (
<div key={post._id} className={classes.post}>
<Post
currentIndex={index}
{...post.user}
dispatch={dispatch}
postID={post._id}
postContent={post.text}
/>
</div>
)): false}
</div>
);
};
the Throttle function
const throttledReq = (loaded, TIMEOUT, dispatch) => _.throttle(e => {
const bottomLimit = document.documentElement.offsetHeight - window.innerHeight;
if(document.documentElement.scrollTop === bottomLimit){
API.Post.getAllPosts(loaded)(dispatch);
}
}, TIMEOUT)();
My requests
Upvotes: 0
Views: 127
Reputation: 20934
Every time you scroll, you fire dispatch
, which in turn re-runs the component, adding another event listener. This will continue infinitely.
You'll only want the event listener to be added the first time the component is rendered. You can do this with the useEffect
hook.
useEffect(() => {
window.addEventListener('scroll', () => throttledReq(loaded, TIMEOUT, dispatch));
}, []);
The empty array at the end means that it doesn't have any dependencies, so it will only run once whenever the component is rendered. Every rerender won't call the callback.
Your loaded
value should use a useEffect
as well, but in this case with the loaded
variable as a dependency. Every time loaded
is changed (and on the first render), the callback is fired and the logic assessed.
useEffect(() => {
if (loaded === 0) {
API.Post.getAllPosts(loaded)(dispatch);
}
if (loaded % 5 === 0) {
window.scrollTo(0, document.body.scrollHeight * 0.6)
}
), [loaded]);
Since you're trying to call a certain logic whenever a scroll position is reached, or some element is in view, I recommend you take a look at the Intersection Observer API which eliminates the requirement of a throttled onscroll
function.
Upvotes: 1