skip to Main Content

I currently have a function that loads information from the backend when I scroll to the bottom of a screen. The problem is that when I scroll to the bottom, it’ll call the function multiple times when I only want it to run once.

The function I want to run:

const loadProgramList = () => {
    refreshToken()
      .then((refresh) => {
        return fetch("http://localhost:5000/program/load-program-list", {
          method: "POST",
          headers: {
            "Content-type": "application/json",
            Authorization: `Bearer ${Cookies.get("accessToken")}`,
          },
          body: JSON.stringify({ index: index }),
        });
      })
      .then((response) => {
        if (!response.ok) {
          return response.json().then((data) => {
            throw { error: data.error, status: response.status };
          });
        }
        return response.json();
      })
      .then((data) => {
        if (data.success) {
          setPrograms((prevPrograms) => {
            return prevPrograms.concat(data.programs);
          });
          setIndex((prevIndex) => {
            return prevIndex + 10;
          });
        }
      })
      .catch((error) => {
        if (error.response === 401) {
          Cookies.remove("accessToken");
          Cookies.remove("refreshToken");
          window.location.reload();
          ctx.setLoginModal(true);
          ctx.setStatus("Session timed out: You have been logged out");
        } else {
          navigate("/error", {
            state: { error: error.error, status: error.status },
          });
        }
      });
  };

I’m using useEffect() to check when the user has reached the bottom of the screen:

useEffect(() => {
    loadProgramList();

    const handleScroll = () => {
      if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
        loadProgramList();
      }
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [index]);

How can I make sure the function only runs once?

2

Answers


  1. loadProgramList will run every time when the index has changed.

    When the scroll reach to the bottom your scroll event callback function will be fired and run loadProgramList. And it has setIndex logic. So this function will change your index state which is a trigger of your useEffect so the useEffect will be fired but you have loadProgramList in your effect callback function. So it will execute loadProgramList again.

    useEffect(() => {
        loadProgramList(); // This will run every time when index has changed.
    
        const handleScroll = () => {
          if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
            loadProgramList(); // This will run when the scroll reach the bottom.
          }
        };
    
        window.addEventListener("scroll", handleScroll);
    
        return () => {
          window.removeEventListener("scroll", handleScroll);
        };
      }, [index]);
    

    My suggestion is, get rid of the first loadProgramList in your effect. If you need to call this function in initial rendering you might want to execute this function only when the first time rendering.
    You could use useRef for this. Something like this.

    const firstRenderRef = useRef<boolean>(true)
    ...
    useEffect(() => {
        if(firstRenderRef.current) {
          loadProgramList();
          firstRenderRef.current = false
        }
    ...
      }, [index]);
    
    Login or Signup to reply.
  2. So, if I understand correctly, you want to implement an infinite scroll that loads data when you reach the bottom of the screen.

    I don’t understand why you put the scroll event inside useEffect for index. You need to separate these two things.

    My suggestion is to add the scroll event handler one time when the component mounts:

    useEffect(() => {
      // scroll event handler
    }, []);
    

    And load the new content whenever index changes:

    useEffect(() => {
      // load new content
    }, [index]);
    

    Also, it might be a good idea to debounce or throttle your scroll event handler for a better performance.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search