skip to Main Content

I hava a react native app. And I have two api calls: one for the home page and one for navigating from the home page to a other component. But the problem is that if the app loads for the first time both api calls are called. What not has to be – only the api call responsible for building the home page has to be called when the app first time loads.

So I have a service:

export const fetchCategoryData = async () => {
    try {
        const response = await fetch("http://192.168.1.68:8000/api/categories/main_groups/", {
            method: "GET",
            headers: {
                Accept: "application/json",
                "Content-Type": "application/json",
            },
        });
        if (!response.ok) {
            throw new Error("Network response was not ok");
        }

        return await response.json();
    } catch (error) {
        console.error("There was a problem with the fetch operation:", error);
        throw error;
    }
};

export const fetchSubCategoryData = async () => {
    try {
        const response = await fetch("http://192.168.1.68:8000/api/categories/2/", {
            method: "GET",
            headers: {
                Accept: "application/json",
                "Content-Type": "application/json",
            },
        });
        if (!response.ok) {
            throw new Error("Network response was not ok");
        }

        return await response.json();
    } catch (error) {
        console.error("There was a problem with the fetch operation:", error);
        throw error;
    }
};

And a context:

export const CategoryContext = createContext();

export const CategoryContextProvider = ({ children }) => {
    const [categoryList, setCategoryList] = useState([]);
    const [subCategoryList, setSubCategoryList] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
    const [] = useState([]);

    const retrieveCategories = () => {
        setLoading(true);
        setTimeout(() => {
            fetchCategoryData()
                .then((results) => {
                    setLoading(false);
                    setCategoryList(results);
                })
                .catch((err) => {
                    setLoading(false);
                    setError(err);
                });
        });
    };

    const retrieveSubCategories = () => {
        setLoading(true);
        setTimeout(() => {
            fetchSubCategoryData()
                //.then(subCategoryTransform)
                .then((results) => {
                    setLoading(false);
                    setSubCategoryList(results.subcategories);
                })
                .catch((err) => {
                    setLoading(false);
                    setError(err);
                });
        });
    };

    useEffect(() => {
        retrieveCategories();
    }, []);

    useEffect(() => {
        retrieveSubCategories();
    }, []);

    return (
        <CategoryContext.Provider
            value={{
                lue={{
                categoryList,
                subCategoryList,
                loading,
                error,
            }}>
            {children}
        </CategoryContext.Provider>
    );
};

So when the app loads first time then this function has to be only loaded: retrieveCategories. But when I look at the backend. I also see that this function is called: retrieveSubCategories.

and this is the component where the user can navigate to a component where the api call: fetchSubCategoryData has to be triggered:

export const CategoryScreen = ({ navigation }) => {
    const { loading, error, categoryList } = useContext(CategoryContext);
    return (
        <SafeArea>
            {loading && (
                <LoadingContainer>
                    <ActivityIndicator animating={true} color={MD2Colors.green200} />
                </LoadingContainer>
            )}
            <Search />
            <CategoryList
                data={categoryList}
                renderItem={({ item }) => {
                    return (
                        <TouchableOpacity onPress={() => navigation.navigate("groepen", { category: item.id })}>
                            <Spacer position="bottom" size="large">
                                <CategoryInfoCard category={item} />
                            </Spacer>
                        </TouchableOpacity>
                    );
                }}
                keyExtractor={(item) => item.id}
            />
        </SafeArea>
    );
};

And this is the App.js file:

export default function App() {
    const [oswaldLoaded] = useOswald({
        Oswald_400Regular,
    });

    const [latoLoaded] = useLato({
        Lato_400Regular,
        Lato_900Black,
    });

    if (!oswaldLoaded || !latoLoaded) {
        return null;
    }

    return (
        <>
            <ThemeProvider theme={theme}>
                <CategoryContextProvider>
                    <Navigation />
                </CategoryContextProvider>
            </ThemeProvider>

            <ExpoStatusBar style="auto" />
        </>
    );
}

Question: how to load only the api call: fetchCategoryData when the app first time loads.

oke, If I do it like:

/* eslint-disable prettier/prettier */

import React, { useEffect, useState } from "react";
import { Text, View } from "react-native";
import { CategoryList } from "./category.screen";
import { SafeArea } from "../../../components/utility/safe-area.component";
import { Spacer } from "../../../components/spacer/spacer.component";
import { SubCategoryInfoCard } from "../components/subcategory-info-card.component";
import { TouchableOpacity } from "react-native-gesture-handler";
import { fetchSubCategoryData } from "../../../services/category/category.service";

export const SubCategoryScreen = ({ route }) => {
    const [subCatgoryList, setSubCategoryList] = useState([]);

    useEffect(() => {
        fetchSubCategoryData(route.category).then((data) => {
            setSubCategoryList(data);
        });
    }, [route.category]);

    return (
        <SafeArea>
            <CategoryList
                data={subCatgoryList}
                renderItem={({ item }) => {
                    return (
                        <TouchableOpacity onPress={() => console.log(item)}>
                            <Spacer position="bottom" size="large">
                                <SubCategoryInfoCard category={item} />
                            </Spacer>
                        </TouchableOpacity>
                    );
                }}
                keyExtractor={(item) => item.id}
            />
        </SafeArea>
    );
};

I get this error:

Invariant Violation: TaskQueue: Error with task : Tried to get frame
for out of range index NaN

2

Answers


  1. If I understand your requirement correctly, all you have to do is remove the following lines from your context:

        // remove
        useEffect(() => {
            retrieveSubCategories();
        }, []);
    

    and then call retrieveSubCategories when you navigate from your home screen, or in the return of a focus effect:

      useFocusEffect(useCallback(() => {
        return retrieveSubcategories;
      }, []));
    

    This would call the function when the home screen is blurred. Removing it from the context would prevent it from being called when the context mounts.

    Login or Signup to reply.
  2. A possible solution is to only call the api for the subcategories when you navigate to the screen for the subcategories / detail page. By using a useEffect to fetch the categories when the route.category changes. I used a local state but you can obviously use the setState from the context for this.

    export const SubCategoryScreen = ({ route }) => {
      const [subCatgoryList, setSubCategoryList] = useState([]);
    
      useEffect(() => {
        fetchSubCategoryData(route.category).then((data) => {
          setSubCategoryList(data);
        });
      }, [route.category]);
    
      return (
        <View>
          <Text>Sub Catgories</Text>
          <CategoryList
            data={subCatgoryList}
            renderItem={({ item }) => {
              return (
                <TouchableOpacity onPress={() => console.log(item)}>
                  <Spacer position="bottom" size="large">
                    <CategoryInfoCard category={item} />
                  </Spacer>
                </TouchableOpacity>
              );
            }}
            keyExtractor={(item) => item.id}
          />
        </View>
      );
    };
    

    Pass the id to the fetchSubCategoryData to make it more dynamic

    export const fetchSubCategoryData = async (id) => {
      try {
        const response = await fetch(
          `http://192.168.1.68:8000/api/categories/${id}`,
          {
            method: "GET",
            headers: {
              Accept: "application/json",
              "Content-Type": "application/json",
            },
          }
        );
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
    
        const data = await response.json();
        return data;
      } catch (error) {
        console.error("There was a problem with the fetch operation:", error);
        throw error;
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search