I am using the Free Meal API with flatlist. I have Category component, Categories page, useFetch hook. I can't see Flatlist on screen. I can get console log of data but I can't reach datas with flatlist.
.env folder:
API_URL_CATEGORIES="https://www.themealdb.com/api/json/v1/1/categories.php"
API_URL_FILTER="https://www.themealdb.com/api/json/v1/1/filter.php?"
useFetch hook for getting the data in URL and returning Loading icon, Error if URL doesn't work and data for data in URL.
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const fetchData = async () => {
try {
const {data: responseData} = await axios.get(url);
setData(responseData);
setLoading(false); }
catch (error) {
setError(error.message);
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {error, loading, data};
};
export default useFetch;
Category component:
const Category= ({category, onSelect}) => {
return(
<TouchableOpacity style={styles.container} onPress={onSelect}>
<Image
style={styles.image}
source={{uri:category.strCategoryThumb}} />
<Text style={styles.title}>{category.strCategory}</Text>
</TouchableOpacity>
)
}
export default Category;
Categories page:
const Categories = ({navigation}) => {
const { error, loading, data } = useFetch(config.API_URL_CATEGORIES);
console.log(data)
const handleCategorySelect = strCategory => {
navigation.navigate("Detail", {strCategory})
}
const renderCategory = ({item}) => <Category category={item} onSelect={() => handleCategorySelect(item.strCategory)}/>;
if(loading) {
return <Loading/>;
}
if(error) {
return <Error/>;
}
return(
<View style={styles.container}>
<FlatList data={data} renderItem={renderCategory}/>
<Text>Categorises</Text>
</View>
)
}
export default Categories;
I think data is actually object that contains a property categories , which holds an array.
try data.categories and I believe this should work fine.
Related
In my React native page
Im navigating from one page to another page with parameters
so those parameters has id
which will be used to fetch data from endpoint and display that in flat list
function Assessments ({route,navigation}) {
useEffect(()=>{
fetchData(file)
},[]);
const { file } = route.params;
const [data,setData] = useState([]);
file consists of route params(Id)
and fetchdata function triggers that function with the id and fetches data
const fetchData = async (file) => {
axios.get(`endpoint`)
.then(function (response) {
console.log(response.data)
setData(response.data)
})
.catch(function (error) {
console.log(error);
})
}
and im returning this
return (
<View>
<Text>okay</Text>
<FlatList
flexGrow= {0}
minHeight= '20%'
maxHeight='80%'
data={data}
renderItem={showdata}>
</FlatList>
</View>
)
and renderitem is
const showdata = ({item}) => {
<View>
sdfdsfsdf
</View>
}
but that part isnt even being rendered
not sure where is the issue !
console.log()
{
"id": 19,
"name": "test1",
}
this is how the data from the endpoint is
Your showdata is not returning anything. Please add return to it like this.
Here is the full code.
function Assessments ({route, navigation}) {
const { file } = route.params;
const [data, setData] = useState([]);
useEffect(()=>{
fetchData(file)
},[]);
const fetchData = async (file) => {
axios.get(`endpoint`)
.then(function (response) {
console.log(response.data)
setData(response.data)
})
.catch(function (error) {
console.log(error);
})
}
const showdata = ({ item }) => {
//Add return here
return (
<View>
<Text>
sdfdsfsdf
</Text>
</View>
)
}
return (
<View>
<Text>okay</Text>
<FlatList
//Put all the style within style prop
style={{flexGrow: 0, minHeight: '20%', maxHeight: '80%'}}
data={data}
renderItem={showdata}
>
</FlatList>
</View>
)
}
i have my json received from api call and is saved in the state "data"
i want to show a loading screen while api is being fetched like i have a state for that too "Loading"
Loading ? Then render data on elements : Loading..
const App = () => {
const [data, setData] = useState([]);
const [Loading, setLoading] = useState(false);
useEffect(() => {
Fetchapi();
}, []);
const Fetchapi = async () => {
try {
await axios.get("http://localhost:8081/api").then((response) => {
const allData = response.data;
setData(allData);
});
setLoading(true);
} catch (e) {
console.log(e);
}
};
return (
<div>
i need my json object rendered here i tried map method on data and i am
getting errors and i have my json2ts interfaces imported in this
</div>
);
};
export default App;
I would camelCase your values/functions and move your fetchApi into the effect itself, as currently its a dependency.
Put setLoading(true) above your fetch request as currently it's not activating until the fetch goes through.
Then below it put setLoading(false), and also inside of your catch.
In your return statement you can now add something like this:
<div>
{loading ? "Loading..." : JSON.stringify(data)}
</div>
Edit
Example for the commented requests.
import { Clan } from "../clan.jsx"
// App
<div>
{loading ? "Loading..." : <Clan data={data}/>}
</div>
// New file clan.jsx
export const Clan = (props) => {
return (
<div>
<h1>{props.data.clan.name}</h1>
</div>
);
}
try this
interface ResponseData {
id: string
// other data ...
}
const App = () => {
const [data, setData] = useState<ResponseData | null>(null)
const [Loading, setLoading] = useState(true)
useEffect(() => {
Fetchapi()
}, [])
const Fetchapi = async () => {
try {
setLoading(true) // USE BEFORE FETCH
await axios.get("http://localhost:8081/api").then(response => {
setLoading(false) // SET LOADING FALSE AFTER GETTING DATA
const allData: ResponseData = response.data
setData(allData)
})
} catch (e) {
setLoading(false) // ALSO SET LOADING FALSE IF ERROR
console.log(e)
}
}
if (Loading) return <p>Loading...</p>
if (data?.length)
return (
<div>
{data.map(d => (
<div key={d.id}>{d.id}</div>
))}
</div>
)
return <div>no data found</div>
}
export default App
I am using the meal database.The data from the link is not being updated after search. But if I console.log the search input, I can see the new link.
That's my API for searching:
API_URL_SEARCH="https://www.themealdb.com/api/json/v1/1/search.php?s="
Thats search page:
function Meals({ navigation}) {
const [searchInput, setSearchInput] = useState('');
const handleChange = (inputText) => {
setSearchInput(inputText);
};
const { loading, error, data } = useFetch(config.API_URL_SEARCH + searchInput);
const handleMealSelect = idMeal => {
navigation.navigate("MealDetail", {idMeal})
}
const renderMeals = ({item}) => <Meal meal={item} onSelect={() => handleMealSelect(item.idMeal)}/>
if(loading) {
return <Loading/>;
}
if(error) {
return <Error/>;
}
return(
<View>
<SearchBar
placeholder="Type Here..."
onChangeText={handleChange}
value={searchInput} />
<FlatList keyExtractor={(meals) => meals.id} data={data.meals} renderItem={renderMeals}/>
</View>
)
}
Thats meal component:
const Meal= ({meal, onSelect}) => {
return(
<TouchableOpacity style={styles.container} onPress={onSelect}>
<ImageBackground
style={styles.image}
source={{uri: meal.strMealThumb}}
imageStyle={{borderTopLeftRadius:10, borderTopRightRadius:10}} />
<Text style={styles.title}>{meal.strMeal}</Text>
</TouchableOpacity>
)
}
Here is useFetch for getting data and getting loading and error situations just in case of.
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const fetchData = async () => {
try {
const {data: responseData} = await axios.get(url);
setData(responseData);
setLoading(false); }
catch (error) {
setError(error.message);
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {error, loading, data};
};
I am trying to render the API data onto the page but get this error
TypeError: undefined is not a function (near "...dataSource.map...")
Unrelated Question: Also I am new to mobile app development and am wondering when you get data from an API is best practice to store it in a database or render it directly onto a page?
import React, { Component, useState, useEffect } from "react";
import { View, Text, StyleSheet, ActivityIndicator } from "react-native";
import { ScreenContainer } from "react-native-screens";
export const Home = () => {
const [isLoading, setisLoading] = useState(true);
const [dataSource, setdataSource] = useState(null);
useEffect(async () => {
const response = await fetch(
"https://facebook.github.io/react-native/movies.json"
);
const data = await response.json();
const movies = data.title;
setdataSource(movies);
setisLoading(false);
// .catch((error)) => {
// console.log(error)
// }
}, []);
if (isLoading) {
return (
<View>
<ActivityIndicator />
</View>
);
} else {
let moviest = dataSource.map((val, key) => {
return (
<View key={key}>
<Text>{val}</Text>
</View>
);
});
return (
<ScreenContainer style={styles.container}>
<View>{moviest}</View>
</ScreenContainer>
);
}
You have to check dataSource is null or undefined before map.
let moviest = dataSource && dataSource.map((val, key) => {
Please check type of dataSource.
When I check https://facebook.github.io/react-native/movies.json data, type of data.title is string("The Basics - Networking")
Maybe you should use movies.
const data = await response.json();
const movies = data.movies;
setdataSource(movies);
setisLoading(false);
I am creating a Reat Native app which connects to an API from which it gets data.
I am using React Navigation to handle navigation. The app has a Stack Navigator and a Bottom Tab Navigator. The StackNavigator has 4 screens:
SignupScreen which handles creating account;
LoginScreen for handlong log in;
SplashScreen that checks for a local token and logs in the user automatically;
A LoadingScreen that triggers the initial fetch call to the API, stores the response in state and navigates to the MainFlow screen;
A MainFlow screen that contains the TabNavigator.
The TabNavigator has two screens, FeedScreen, Account and More where the initial screen is FeedScreen.
The signup/login/local flows are all working fine.
The issue: Once the user is logged in successfully the LoadingScreen is triggering the API call but the MainFlow components are being rendered before the data is in state. Because the components in MainFlow need the data, an error is thrown. How can I render the FeedScreen components only once the data is there?
In the LoadingScreen I am triggering an API call on useEffect from a context object, QuestionContext:
const LoadingScreen = ({ navigation }) => {
const [loading, setLoading] = useState(true);
const { state: authState } = useContext(AuthContext);
const { getQuestionsForUser, getAllQuestions } = useContext(QuestionContext);
useEffect(() => {
getAllQuestions();
}, []);
return (
<View style={styles.container}>
<YonStatusBar backgroundColor="#310B3B" />
<Image source={splashLogo} containerStyle={styles.splashLogo} />
<ActivityIndicator />
</View>
);
};
export default LoadingScreen;
getAllQuestions is a function in QuestionContext which makes the API call and navigates to FeedScreen:
const getAllQuestions = (dispatch) => {
return async () => {
try {
const token = await AsyncStorage.getItem('token');
const config = { headers: { Authorization: `Bearer ${token}` } };
const response = await yonyonApi.get(`/questions`, config);
dispatch({ type: 'GET_ALL_QUESTIONS', payload: response.data });
RootNavigation.navigate('MainFlow');
} catch (e) {
console.log(e);
}
};
};
getAllQuestions is working fine: the API call is successful and I can see that the response is stored in state. However, it navigates to MainFlow before that happens.
Finally, this is the FeedScreen:
const FeedScreen = () => {
const { state: questionState } = useContext(QuestionContext);
return (
<ScrollView style={styles.container}>
{console.log(questionState.questions)}
<View style={styles.listContainer}>
<QuestionCard />
</View>
</ScrollView>
);
};
export default FeedScreen;
The FeedScreen renders a QuestionCard which needs the data in questionState. This is what throwing the error: the QuestionCard is being rendered before the data is in state.
How can I make the navigation only navigate to FeedScreen once the necessary data is in state? Or alternatively, render something else than the QuestionCard while the data is not there and once the data is in questionState render the QuestionCard?
For me i will use screen instead of two screens as follows :
const FeedScreen = () => {
const [loading, setLoading] = useState(true);
const { state: authState } = useContext(AuthContext);
const [data, setData] = useState([]);
const getAllQuestions = (dispatch) => {
return async () => {
try {
const token = await AsyncStorage.getItem('token');
const config = { headers: { Authorization: `Bearer ${token}` } };
const response = await yonyonApi.get(`/questions`, config);
setData(response.data)
setLoading(false)
} catch (e) {
console.log(e);
}
};
};
useEffect(() => {
getAllQuestions();
}, []);
return (
<ScrollView style={styles.container}>
{
(loading)?
<ActivityIndicator/>
:
<View style={styles.listContainer}>
<QuestionCard data={data}/>
</View>
}
</ScrollView>
);
};
export default FeedScreen;
Why don't you set the initial state of your context to null and render your component if it is not null ?
const [questionState, setQuestionState] = useState(null);
...
const FeedScreen = () => {
const { state: questionState } = useContext(QuestionContext);
return (
<ScrollView style={styles.container}>
{!!questionState?.questions && console.log(questionState.questions)}
<View style={styles.listContainer}>
<QuestionCard />
</View>
</ScrollView>
);
};
export default FeedScreen;