简体   繁体   中英

Stop Function being called multiple times when scrolling

I'm using infinite scrolling in my react/redux app and when the user nears the bottom of the page, more contents will load.

But if the user scrolls too fast and just keeps scrolling while content is being fetched from the server, the function responsible for the scroll effect fires multiple times and the API gets the same content, and this causes errors because same items with identical Keys will be loaded.

const [isFetching, setIsFetching] = useState(false);

  // Fire Upon Reaching the Bottom of the Page
  const handleScroll = () => {
    if (
      window.innerHeight +
        Math.max(
          window.pageYOffset,
          document.documentElement.scrollTop,
          document.body.scrollTop
        ) >
      document.documentElement.offsetHeight - 100
    ) {
      setIsFetching(true);
    } else {
      return;
    }
  };

  // Debounce the Scroll Event Function and Cancel it When Called
  const debounceHandleScroll = debounce(handleScroll, 500);

  useEffect(() => {
    window.addEventListener("scroll", debounceHandleScroll);
    return () => window.removeEventListener("scroll", debounceHandleScroll);
  }, [debounceHandleScroll]);

  debounceHandleScroll.cancel();

  // Get More Posts
  const loadMoreItems = useCallback(() => {
    dispatch(getMorePosts(moreApiAddress));
    setIsFetching(false);
  }, [dispatch, moreApiAddress]);

  useEffect(() => {
    if (!isFetching) return;
    loadMoreItems();
  }, [isFetching, loadMoreItems]);

Is there any way I can stop that function to fire multiple times when new content is being fetched and/or the user is scrolling too fast?

Your handleScroll function updates state, which causes component's re-render.

That means that you will have new instance of handleScroll (and debounceHandleScroll) on each render.

Since you have debounceHandleScroll in useEffect dependency array, and it changes on each render - you are getting kind of a "endless loop".

Try to use useCallback hook for memoizing functions or re-write component and try to move these functions outside of the component.

I mean something like that:

const handleScroll = useCallback(() => {
    if (
      window.innerHeight +
        Math.max(
          window.pageYOffset,
          document.documentElement.scrollTop,
          document.body.scrollTop
        ) >
      document.documentElement.offsetHeight - 100
    ) {
      setIsFetching(true);
    } else {
      return;
    }
  }, []);

  // Debounce the Scroll Event Function and Cancel it When Called
  const debounceHandleScroll = useCallback(() => debounce(handleScroll, 500), [handleScroll]);

Read more about useCallback : https://reactjs.org/docs/hooks-reference.html#usecallback

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM