Search code examples
reactjsreact-hooksinfinite-scroll

stop/prevent scrolling to top on re render in React using Hooks (need to implement infinite scrolling)


I am trying to setup infinite scrolling using React Hooks, I am getting the data correctly from the node backend (3 dataset per request), and when I scroll new data is also added correctly in the array (in the loadedPlaces state), but the page is going back to top on re render, I need to prevent this behavior. How do I prevent this, and below is my code

import React, { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [page, setPage] = useState(1);
  const [loadedPlaces, setLoadedPlaces] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const getPlaces = async () => {
      try {
        setIsLoading(true);
        const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
        const responseData = await fetch(url);
        const data = await responseData.json();
        console.log(data);
        setLoadedPlaces((prev) => [...prev, ...data.places]);
        setIsLoading(false);
      } catch (error) {}
    };
    getPlaces();
  }, [page]);

  window.onscroll = function (e) {
    if (
      window.innerHeight + document.documentElement.scrollTop ===
      document.documentElement.offsetHeight
    ) {
      setPage(page + 1);
    }
  };

  return (
    <div className='App'>
      <h1>Infinite Scroll</h1>
      {!isLoading &&
        loadedPlaces.length > 0 &&
        loadedPlaces.map((place, index) => (
          <div key={index} className={"container"}>
            <h1>{place.location.lat}</h1>
            <h1>{place.location.lng}</h1>
            <h1>{place.title}</h1>
            <h1>{place.description}</h1>
            <h1>{place.address}</h1>
            <hr></hr>
          </div>
        ))}
    </div>
  );
}

export default App;

Any help is highly appreciated


Solution

  • This is happening because whenever you scroll you are calling

    window.onscroll = function (e) {
        if (
          window.innerHeight + document.documentElement.scrollTop ===
          document.documentElement.offsetHeight
        ) {
          setPage(page + 1);
        }
      };
    

    And it's changing the page count and that changed page count leads to again run the

     useEffect(() => {
        const getPlaces = async () => {
          try {
            setIsLoading(true);
            const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
            const responseData = await fetch(url);
            const data = await responseData.json();
            console.log(data);
            setLoadedPlaces((prev) => [...prev, ...data.places]);
            setIsLoading(false);
          } catch (error) {}
        };
        getPlaces();
      }, [page]);
    

    and in that function, you are doing setIsLoading(true) so that it is again rendering this because of

    
    {!isLoading && <-----
            loadedPlaces.length > 0 &&
            loadedPlaces.map((place, index) => (
              <div key={index} className={"container"}>
                <h1>{place.location.lat}</h1>
                <h1>{place.location.lng}</h1>
                <h1>{place.title}</h1>
                <h1>{place.description}</h1>
                <h1>{place.address}</h1>
                <hr></hr>
              </div>
            ))}
    
    

    And that leads you to the top of the page

    You can try this approach:

    import React, { useEffect, useState } from "react";
    import "./App.css";
    
    function App() {
      const [page, setPage] = useState(1);
      const [loadedPlaces, setLoadedPlaces] = useState([]);
      const [isLoading, setIsLoading] = useState(false);
    
      useEffect(() => {
        const getPlaces = async () => {
          try {
            setIsLoading(true);
            const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
            const responseData = await fetch(url);
            const data = await responseData.json();
            console.log(data);
            setLoadedPlaces((prev) => [...prev, ...data.places]);
            setIsLoading(false);
          } catch (error) {}
        };
        getPlaces();
      }, [page]);
    
      window.onscroll = function (e) {
        if (
          window.innerHeight + document.documentElement.scrollTop ===
          document.documentElement.offsetHeight
        ) {
          setPage(page + 1);
        }
      };
    
      return (
        <div className='App'>
          <h1>Infinite Scroll</h1>
           {
            loadedPlaces.length > 0 &&
            loadedPlaces.map((place, index) => (
              <div key={index} className={"container"}>
                <h1>{place.location.lat}</h1>
                <h1>{place.location.lng}</h1>
                <h1>{place.title}</h1>
                <h1>{place.description}</h1>
                <h1>{place.address}</h1>
                <hr></hr>
              </div>
            ))}
        </div>
      );
    }
    
    export default App;