Ahmed Gaafer
Ahmed Gaafer

Reputation: 1661

_.throttle executing API more than once

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:

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

enter image description here

Upvotes: 0

Views: 127

Answers (1)

Emiel Zuurbier
Emiel Zuurbier

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

Related Questions