Marco
Marco

Reputation: 867

Problem when using FlashList in React Native?

this code is supposed to load infinite data using pagination with RTK Query and Flash list. Data is coming normally without issues the only issue is it loads all data at once and ignores the onEndReached functionality. after monitoring the server once the page loads it loads all 5 pages at once without even touching the list

import React, { useState } from "react";
import { View, StyleSheet, FlatList } from "react-native";
import { useGetLatestArticlesQuery } from "../data/articleQuery";
import { ActivityIndicator } from "react-native";
import ArticleListItem from "./articleSingleItem";

const HomeArticlesList = ({ limit }) => {
  [page, setPage] = React.useState(1);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const { data, isLoading, refetch } = useGetLatestArticlesQuery(limit, page);
  if (isLoading) {
    // Render loading state or placeholder
    return <ActivityIndicator size="large" />;
  }
  const renderItem = ({ item }) => {
    // Check if item is defined before rendering
    if (!item) {
      console.warn("Item is null or undefined:", item);
      return null;
    }

    return <ArticleListItem item={item} />;
  };
  const keyExtractor = (item) =>
    item ? item.id.toString() : Math.random().toString();
  const renderSeparator = () => {
    return <View style={styles.separator} />;
  };
  if (isLoadingMore) {
    // Render loading state or placeholder
    return <ActivityIndicator size="large" />;
  }
  const onEndReached = () => {
    setIsLoadingMore(true);
    setPage(page + 1);
    if (page != 1 && page < 5) {
      refetch();
    }
    setIsLoadingMore(false);
  };
  return (
    <View>
      <View style={styles.container}>
        <FlatList
          disableVirtualization={true}
          removeClippedSubviews={true}
          maxToRenderPerBatch={60}
          windowSize={30}
          data={data}
          renderItem={renderItem}
          keyExtractor={keyExtractor}
          ItemSeparatorComponent={renderSeparator}
          onEndReached={onEndReached}
          onEndReachedThreshold={0.5}
          estimatedItemSize={100}
        />
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    height: "100%",
    padding: 12,
  },
  separator: {
    height: 10,
    backgroundColor: "transparent",
  },
});
export default HomeArticlesList;

RTK query functions

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import Config from "../utils/Config";

const apiUrl = Config.API_BASE_URL;

export const articlesApi = createApi({
  reducerPath: "articlesApi",
  baseQuery: fetchBaseQuery({
    baseUrl: apiUrl,
  }),
  refetchOnFocus: true,
  refetchOnReconnect: true,
  tagTypes: [
    "articlesByCategory",
    "featuredArticles",
    "latestArticles",
    "relatedArticles",
  ],
  endpoints: (builder) => ({
    getArticlesByCategory: builder.query({
      query: (category_id) => ({
        url: `/articles?category=${category_id}&limit=20&page=${page}`,
        method: "GET",
      }),
      // Always merge incoming data to the cache entry
      merge: (currentCache, newItems) => {
        currentCache.push(...newItems);
      },
      // Refetch when the page arg changes
      forceRefetch({ currentArg, previousArg }) {
        return currentArg !== previousArg;
      },
      providesTags: (result, error, category_id, page) =>
        result
          ? [{ type: "articlesByCategory", id: category_id, page: page }]
          : [],
    }),
    getFeaturedArticles: builder.query({
      query: () => ({
        url: "/articles/featured",
        method: "Get",
      }),
      providesTags: ["featuredArticles"],
    }),
    getLatestArticles: builder.query({
      query: ({ limit, page }) => ({
        url: `/articles?limit=${limit}&page=${page}`,
        method: "Get",
      }),
      // Always merge incoming data to the cache entry
      merge: (currentCache, newItems) => {
        currentCache.push(...newItems);
      },
      // Refetch when the page arg changes
      forceRefetch({ currentArg, previousArg }) {
        return currentArg !== previousArg;
      },
      providesTags: (result, error, limit, page) =>
        result ? [{ type: "latestArticles", limit: limit, page: page }] : [],
    }),
    getRelatedArticles: builder.query({
      query: (article_id) => ({
        url: `/articles/related/${article_id}?limit=10&page=${page}`,
        method: "Get",
      }),
      // Always merge incoming data to the cache entry
      merge: (currentCache, newItems) => {
        currentCache.push(...newItems);
      },
      // Refetch when the page arg changes
      forceRefetch({ currentArg, previousArg }) {
        return currentArg !== previousArg;
      },
      providesTags: (result, error, article_id, page) =>
        result
          ? [{ type: "relatedArticles", article_id: article_id, page: page }]
          : [],
    }),
  }),
});

export const {
  useGetArticlesByCategoryQuery,
  useGetFeaturedArticlesQuery,
  useGetLatestArticlesQuery,
  useGetRelatedArticlesQuery,
} = articlesApi;

i kept track of

const onEndReached = () => {
    setIsLoadingMore(true);
    setPage(page + 1);
    if (page != 1 && page < 5) {
      console.log(page);
      refetch();
    }

by logging anything just to watch how it works and unfortunately it prints all page numbers once the app loaded without even touching the list

Upvotes: -2

Views: 1018

Answers (3)

Appu
Appu

Reputation: 1

Try sending data such that the component rendering the list is filling up the space. Either reduce your list component container size or increase data coming through (use mock). You have to make sure the data coming on first call is filling the container. You can try sending too much data in the first call to see if on scroll the onEndReached is called or not.

Upvotes: 0

Sunday morning
Sunday morning

Reputation: 1

const [onEnd, setOnEnd] = useState<boolean>(true);

onMomentumScrollBegin={() => setOnEnd(false)}
onEndReached={() => {
          if (!onEnd) {
            onEndReached();
          }
 }}

Upvotes: 0

Michael Bahl
Michael Bahl

Reputation: 3649

I expect that onEndReached were instantly triggered due to value of onEndReachedThreshold which will call refetch which trigger a rerender and so on...

To test my assumption increase the row height to for instance to 300 so that
onEndReachedThreshold only get called 4 times

Upvotes: 0

Related Questions