Wong
Wong

Reputation: 79

React: App requests same endpoint multiple times

I am using Infinite scroll component. It is working but it is requesting same endpoint multiple times ( 3 -4 times) . With my React knowledge, let me explain what is happening:

Because I am setting initialLoad={true}, so the loadMore function will be invoked right after component rendered. I can set initialLoad={false} but it doesn't fetch data in the initial render

  1. In 1st render, it call loadMore function ==> loading state changed ==> 2nd render
  2. In 2nd render, it call loadMore function ==> tracks state changed ==> 3nd render
  3. In 3rd render, it call loadMore function ==> something state changed ==> 4nd render

Index.js

export default function index() {
  const { tracks, error, loading, loadMore, hasMore } = usePublicTracks(
    myApiEndPoint.TRENDING_TRACKS,
    5,
  );

  return (
    <div>
      <Main>
        <InfiniteScroll
          initialLoad={true}  //call loadMore function right after component rendered
          pageStart={0}
          loadMore={loadMore}
          hasMore={hasMore}
        >
          <TrackList>
            {tracks.map((track, i) => (
              <TrackItem key={i}
                track={track}
              />
            ))}
          </TrackList>
          {loading && hasMore && (
            <div className="">
              <Spin />
            </div>
          )}
        </InfiniteScroll>
      </Main>
    </div>
  );
}

usePublicTracks.js

export const usePublicTracks = (endpoint, limit) => {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [offset, setOffset] = useState('');
  const [tracks, setTracks] = useState([])
  const [hasMore, setHasMore] = useState(true);

  console.log('tracks', tracks)
  const loadMore = async params => {
    try {
      console.log('hello22222222222222222222222222222222222222222')
      const res = await myApiAxiosInstance.get(endpoint, {
        params: {
          limit: limit,
          lastVisible: offset
        }
      });
      setLoading(false);
      setTracks([...tracks, ...(res.data.collection)])
      setOffset(res.data.lastVisible)
      if (res.data.lastVisible == "end") setHasMore(false);

    } catch (error) {
      setLoading(false);
      setError(error.message);
    }
  };



  return {
    error,
    loading,
    tracks,
    loadMore,
    hasMore
  };
};

When I wrap loadMore func in useEffect, it show error Hooks can only be called inside of the body of a function component.:

const loadMore = async params => {
    useEffect(() => {
      const loadMoreOnce = async () => {
        //some logic
      };
      loadMoreOnce();
    }, []);
  };

My question is how to call the load more function once. Then call it every scrolling to the bottom of window ? How to prevent to request same endpoint multiple times ?

Upvotes: 1

Views: 1227

Answers (1)

Ivan Cherviakov
Ivan Cherviakov

Reputation: 538

const isLoading = false;
const myComponent = () => {
  useEffect(() => {
    loadMore(); // initial call, this useEffect equivalent to componentDidMount
  }, []);

  if (!isLoading && document.body.scrollTop >= document.body.offsetTop) { 
    console.log('loading, should see this once');
    isLoading = true;
    loadMore(); // here, after loadMore finished you need to resolve isLoading to false
  }
  return (<div>My component</div>);
}

Try to find all calls of loadMore function calls, maybe it is called in some other places as well, change offset from beginning of page, check hasMore condition to be chaged correctly, because several calls can also come from InfiniteScroll itself, when it thinks you hit bottom several times.

Upvotes: 1

Related Questions