Unable to render data to ReactNative FlatList on Scroll down using Http POST request

I want to render a list of items in a ReactNative FlatList, initially I am loading 15 items and then load more when user scrolls down. I am using FlatList’s onEndReached prop to get new items on scroll. I am consuming an api with POST method, which takes an object containing pageNumber, pageSize etc., as input and returns an object { success: true, message: 'success.', data: [ items : [...] ] } containing list items.

To achieve this I am using apisauce npm package and written a custom hook(useSearch.js) which handles forming api request object and then invoke api and and get items.

I am getting below error when I scroll down to the screen. When the component loads first time I am not getting any error but when I scroll I am getting below error. After trying some time I realized that same code is working for any other Http GET requests, but when I use Http POST method its not working.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function, 
    in App (created by ExpoRoot)
    in RCTView (at NativeAppearance.tsx:4)
    in FallbackAppearanceProvider (at src/index.tsx:70)
    in AppearanceProvider (created by ExpoRoot)
    in RootErrorBoundary (created by ExpoRoot)
    in ExpoRoot (at renderApplication.js:45)
    in RCTView (at AppContainer.js:109)
    in RCTView (at AppContainer.js:135)
    in AppContainer (at renderApplication.js:39)
TypeError: undefined is not an object (evaluating 'item.listingId.toString')

Below is the code I am using

client.js

import { create } from "apisauce";

const apiClient = create({
  baseURL: 'http://localhost:9000',
});

export default apiClient;

useSearch.js (Custom Hook)

const useSearch = () => {
  const searchList = { pageNumber: 1, pageSize: 15, sortField: "createddate", sortOrder: "desc", isActive: true};

  const [items, setItems] = useState([]);
  const [error, setError] = useState(false);
  const [pageNumber, setPageNumber] = useState(1);
  const [loading, setLoading] = useState(false);

  const endpoint = "/listings";

  const getItems = async (categoryId) => {
    setLoading(true);
    setPageNumber((prevPageNumber) => prevPageNumber + 1);

    const response = await apiClient.post(`${endpoint}/search`, {
      ...searchList,
      pageNumber,
      categoryId,
    });

    setLoading(false);
    if (!response.ok) {
      if (response.data && response.data.message) return setError(true);
      else return setError(true);
    }

    if (items && items.length > 0)
      setItems([...items, response.data?.data?.items]);
    else setItems(response.data?.data?.items);

    return response;
  };

  return {error, getItems, items, loading };
};

ListingsScreen.js (Component)

const ListingsScreen = ({ route }) => {
  const categoryId = route.params;

  const { items, getItems, loading } = useSearch();

  useEffect(() => {
    getItems(categoryId);
  }, []);

  return (
    <>
      <ActivityIndicator visible={loading} />
      <Screen style={styles.screen}>
        <FlatList
          showsVerticalScrollIndicator={false}
          data={items}
          keyExtractor={(item) => item.listingId.toString()}
          renderItem={({ item }) => (
            <CustomListItem onPress={() => console.log("On Press Item")} title={item.name} subTitle={item.category} />
          )}
          onEndReached={() => getItems(categoryId)}
          onEndReachedThreshold={0.5}
        />
      </Screen>
    </>
  );
};

How to solve this problem.? Any help is appreciated.

console.log categoryId and getItems(categoryId), what do you get?

Try using the full localhost URL instead of ‘http://localhost:9000’ in Client.js.

categoryId = 7
Sample API Response
``
{
“success”: true,
“message”: “Listings fetched successfully.”,
“statuscode”: 200,
“data”: {
“pageNumber”: 1,
“totalPages”: 1,
“items”: [
{
“listingId”: 1,
“name”: “Red Jacket”,
“category”: “Clothing”,
“categoryId”: 7,
“isActive”: true
},
{
“listingId”: 2,
“name”: “Black T-Shirt”,
“category”: “Clothing”,
“categoryId”: 7,
“isActive”: true
}
],
“count”: 2,
“hasPreviousPage”: false,
“hasNextPage”: false
}
}
`

After looking around it seems that the problem lies with useEffect,
At the moment I can’t do much then give you this link that solved a similar problem that you’re having: https://stackoverflow.com/questions/53949393/cant-perform-a-react-state-update-on-an-unmounted-component
hopes this helps.

Okey, some more info, so the problem is that when you pass an empty array as the second parameter for useEffect() it will behave as an onMount, basically it will only run once, in order for to update you need to pass a const to the array and only if it changes then the will useEffect() work and makes the api call.
So the simplest solution I can come up is this:

const ListingsScreen = ({ route }) => {
  const categoryId = route.params;

  const { items, getItems, loading } = useSearch();
  const [showMore, setShowMore] = useState(0);

  useEffect(() => {
    getItems(categoryId);
  }, [showMore]);


  return (
    <>
      <ActivityIndicator visible={loading} />
      <Screen style={styles.screen}>
        <FlatList
          showsVerticalScrollIndicator={false}
          data={items}
          keyExtractor={(item) => item.listingId.toString()}
          renderItem={({ item }) => (
            <CustomListItem onPress={() => console.log("On Press Item")} title={item.name} subTitle={item.category} />
          )}
          onEndReached={() => {
                      setShowMore( value => value +1)
                      getItems(categoryId)
         }}
          onEndReachedThreshold={0.5}
        />
      </Screen>
    </>
  );
};

remember to import useState for this to work and I hope this solves your problem.

Hi @JonattanD, Thank you for the solution you provided. I tried your solution but it didn’t worked for me. I created a snack project with the same code and implemented your solution, this time I am unable to render data into FlatList even when the component is loaded for first time.

Below is the URL of Snack Project, I am getting some mock data from an open source api which gives results with pagination implemented.

https://snack.expo.io/@venkateshp1231/flatlistrender

API URL
https://gorest.co.in/public-api/users?page=1

Could you please have a look?

okey, so the problem was that on
setItems([…items, response?.data?.data]);

you should have been doing this
setItems([…items, …response?.data?.data]);

You needed to use the spread operator to get the data inside of the response, please disregard my other comments and change only that line.

1 Like

Thank you very much @JonattanD, this change worked for me :slightly_smiling_face:

1 Like