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
If I understand your requirement correctly, all you have to do is remove the following lines from your context:
and then call retrieveSubCategories when you navigate from your home screen, or in the return of a focus effect:
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.
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 theroute.category
changes. I used a local state but you can obviously use the setState from the context for this.Pass the
id
to thefetchSubCategoryData
to make it more dynamic