Fetch data from API using React Native - javascript

I'm trying to fetch data from the url below, but when I launch the code it returns the error: TypeError: undefined is not an object (evaluating 'res.data.hints'), and consequentially nothing happens, I've followed various tutorials and they seem to come up with this code.
States
constructor(props) {
super(props);
this.updateSearch();
this.state = {
data: [],
};
}
Function
updateSearch = () => {
const url = `https://api.edamam.com/api/food-database/v2/parser?ingr=b&app_id=000&app_key=00000&page=20`;
fetch(url)
.then((res) => res.json())
.then((res) => {
this.setState({
data: res.data.hints
});
})
.catch(error => {
console.log("error : "+ error)
});
};
Flatlist
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem>
<TouchableOpacity>
<View>
<Text>{item.label}</Text>
<Text>{item.brand}</Text>
</View>
</TouchableOpacity>
</ListItem>
)}
keyExtractor={(item) => item.foodId}
/>

this.setState({
data: res.hints
});
Try this

Related

Connecting REST API in React Native

I am trying to learn how to connect APIs in React Native. I am using a sample API: https://reactnative.dev/movies.json
This is my code:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
dataSource: [],
};
}
componentDidMount() {
return fetch("https://reactnative.dev/movies.json")
.then((response) => response.json())
.then((responseJson) => {
this.setState({
loading: false,
dataSource: responseJson.movies,
});
})
.catch((error) => console.log(error)); //to catch the errors if any
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#0c9" />
</View>
);
} else {
let products = this.state.dataSource.map((val, key) => {
return (
<View key={key} style={styles.item}>
<Text>{val}</Text>
</View>
);
});
return (
<View style={styles.container}>
<Text>{products.title}</Text>
</View>
);
}
}
}
The problem occurs with my "products" variable. In debug mode, I was able to see the key and value pairs which were correct from the API. However, the products array is populated with objects rather than strings which are structured like this:
Object {$$typeof: Symbol(react.element), type: "RCTView", key: "0", …}
My code returns the following error: this.state.dataSource.map is not a function
EDIT:
The answer below worked for the API I was using. Now I am trying a different API structured like this:
{"prods":
{
"86400":{"slug":"86400","url":"/86400"},
"23andme":{"slug":"23andme","url":"/23andme"}
}}
I am having trouble with the mapping again. This returns an error:
return dataSource.map((val, key) => (
<View key={key} style={styles.item}>
<Text>{val.slug}</Text>
</View>
));
First, there is a small typo in your example. In your component's constructor you specify a loading state variable, but in your render function you're using isLoading. Second, you're not mapping over your data correctly. It just looks like you need to specify what aspects of each movie you care about in your render function. JSX can't handle displaying a full javascript object which is what <Text>{val}</Text> ends up being in your code. There are a few ways you can fix this. It's very common to just map over your results and display them directly.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
dataSource: []
};
}
componentDidMount() {
return fetch("https://reactnative.dev/movies.json")
.then(response => response.json())
.then(responseJson => {
this.setState({
loading: false,
dataSource: responseJson.movies
});
})
.catch(error => console.log(error));
}
render() {
const { loading, dataSource } = this.state;
if (loading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#0c9" />
</View>
);
}
return dataSource.map((movie, index) => (
<View key={movie.id} style={styles.item}>
<Text>{movie.title}</Text>
</View>
));
}
}
You could also pull this out to a renderMovies method, which might help since you are trying to display these in a styled container.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
dataSource: []
};
}
componentDidMount() {
return fetch("https://reactnative.dev/movies.json")
.then(response => response.json())
.then(responseJson => {
this.setState({
loading: false,
dataSource: responseJson.movies
});
})
.catch(error => console.log(error));
}
renderMovies() {
const { dataSource } = this.state;
return dataSource.map((movie, index) => (
<View key={movie.id} style={styles.item}>
<Text>{movie.title}</Text>
</View>
));
}
render() {
const { loading } = this.state;
if (loading) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#0c9" />
</View>
);
}
return (
<View style={styles.container}>
{this.renderMovies()}
</View>
);
}
}
I have used Object.values() to restructure the object into an array
componentDidMount() {
return fetch("https://reactnative.dev/movies.json")
.then((response) => response.json())
.then((responseJson) => {
this.setState({
loading: false,
dataSource: Object.values(responseJson.movies), //changed this
});
})
.catch((error) => console.log(error));
}
Try simple way. This code uses modern React practice and helps you to brush up your React skills in general. Give a try.
import React, {useState, useEffect} from 'react';
import { Text, View, StyleSheet } from 'react-native';
import axios from 'axios'; //for fetching data
export default function App() {
//React Hook for state
const [ data, setData ] = useState ([]);
//React Hook Instead Of ComponentDidMount
useEffect(() => {
const fetchData = async () => {
const result = await axios(
"https://reactnative.dev/movies.json",
);
setData(result.data.movies);
};
fetchData();
}, []);
return (
<View>
<Text>{JSON.stringify(data)}</Text>
</View>
);
}

React Native FlatList: Toggle State Value

I am trying toggle text state from ACTIVE to INACTIVE (and vice versa) for each individual item in the FlatList. In the code below, the status toggles from true to false (and false to true) but the text in the app shows inactive and doesn't change.
import NameActionBar from '../components/NameActionBar';
export default class MasterScreen extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
status: false,
};
}
componentDidMount() {
this.getFreelancerList();
}
//Calling API to get list
getFreelancerList() {
let self = this;
AsyncStorage.getItem('my_token').then((keyValue) => {
console.log('Master Screen (keyValue): ', keyValue); //Display key value
axios({
method: 'get',
url: Constants.API_URL + 'user_m/freelancer_list/',
responseType: 'json',
headers: {
'X-API-KEY': Constants.API_KEY,
'Authorization': keyValue,
},
})
.then(function (response) {
console.log('Response.Data: ===> ', response.data.data);
console.log('Response: ', response);
self.setState({
dataSource: response.data.data,
});
})
.catch(function (error) {
console.log('Error: ', error);
});
}, (error) => {
console.log('error error!', error) //Display error
});
}
//Show the list using FlatList
viewFreelancerList() {
const { dataSource } = this.state;
return (
<View>
{<FlatList
data={dataSource}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => {
return (
<View style={styles.containerFreelancer}>
<TouchableOpacity
style={{ flex: 1 }}
onPress={() => console.log(item.freelancer_name)}
>
<Text style={styles.textFreelancer}>{item.freelancer_name}</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
const newStatus = !this.state.status;
this.setState({
status: newStatus,
});
console.log('status: ', this.state.status);
}}
>
<Text>{this.state.status ? "ACTIVE" : "INACTIVE"}</Text>
</TouchableOpacity>
</View>
);
}}
/>}
</View>
);
}
render() {
return (
<>
<NameActionBar />
<ScrollView>
{this.viewFreelancerList()}
</ScrollView>
</>
);
}
}
My issues are:
How can I make the text toggle between active to inactive?
How can I make the text toggle separately for each item in the FlatList? for example: Item 1: 'ACTIVE', Item 2: 'INACTIVE' etc.
Any help would be appreciated as I am still new to learning React Native.
Screenshot of the app below:
You need to create a child component with its own state.
class FlatListComponent extends Component {
state = {
status: false
}
render() {
<View style={styles.containerFreelancer}>
<TouchableOpacity style={{ flex: 1 }} onPress={() => console.log(this.props.freelancer_name)}>
<Text style={styles.textFreelancer}>{this.props.freelancer_name}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => {
const newStatus = !this.state.status;
this.setState({
status: newStatus,
});
console.log('status: ', this.state.status);
}}
>
<Text>{this.state.status ? "ACTIVE" : "INACTIVE"}</Text>
</TouchableOpacity>
</View>
}
}
Then you just need to add it inside your renderItem method.
<FlatList
data={dataSource}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => <FlatListComponent {...item}/>
/>}
Here's a working example
I hope it helps ! Feel free to add comments if you're still stuck

Can't get Flatlist pull to refresh working

The docs are pretty straight forward but somehow I can not get the pull to refresh working. The data is loaded correctly at the componentDidMount but _refreshis not called when I try to pull down the list. I tried it on a iPhone and Android device. On Android I can't even pull down the list (no rubber effect).
Here is my code:
export default class HomeScreen extends Component {
static navigationOptions = { header: null };
state = { data: [], isLoading: true };
_fetchData = async () => {
const data = [];
try {
const response = await fetch('https://randomuser.me/api/?results=10');
const responseJSON = await response.json();
this.setState({ data: responseJSON.results, isLoading: false });
} catch (error) {
alert('some error');
this.setState({ isLoading: false });
}
};
_refresh = () => {
alert('this is never be shown');
this.setState({ isLoading: true });
this._fetchData();
};
componentDidMount() {
this._fetchData();
}
render() {
if (this.state.isLoading)
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="darkorange" />
</View>
);
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
keyExtractor={item => item.email}
renderItem={({ item }) => (
<FriendListItem
friend={item}
onPress={() =>
this.props.navigation.navigate('FriendsScreen', {
friend: item,
})
}
/>
)}
ItemSeparatorComponent={() => <View style={styles.listSeparator} />}
ListEmptyComponent={() => <Text>empty</Text>}
onRefresh={this._refresh}
refreshing={this.state.isLoading}
/>
</View>
);
}
}
Double check your FlatList import. I'm pretty sure that you imported FlatList from react-native-gesture-handler. If yes then remove it.
FlatList should be imported from react-native like below.
import { FlatList } from 'react-native';
If above is not the case then share with me your StyleSheet.
Let me know if it helps.

How to render the response API with Flatlist in react native?

I have some issue with a Flatlist,
I need to render the response data from API in a Flatlist but it doesn't work!
but when I set the static data it works fine! and when i logging the { item } i don't show anything in Debugging! i think the syntax of Flatlist it's right!
anybody, can you help me with this issue?
mycode here
import React, { Component } from "react";
import {
StyleSheet,
Text,
View,
TextInput,
ScrollView,
Image,
ActivityIndicator,
FlatList,
ListItem
} from "react-native";
import Moment from "react-moment";
import Icon from "react-native-vector-icons/dist/FontAwesome";
export default class App extends Component {
constructor(props) {
super(props);
this.ApiKeyRef = "****";
this.watchPositionOpts = {
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 1000,
distanceFilter: 5
};
this.state = {
isLoading: true,
dataSource: [],
latitude: null,
longitude: null,
error: null
};
}
componentDidMount() {
this.watchId = navigator.geolocation.watchPosition(
this.watchPositionSuccess,
this.watchPositionFail,
this.watchPositionOpts
);
}
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watchId);
navigator.geolocation.stopObserving();
}
watchPositionSuccess = position => {
this.setState(
{
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null
},
() => this.fetchCallback()
);
};
watchPositionFail = err => {
this.setState({ error: err.message });
};
fetchCallback = async () => {
const { latitude, longitude } = this.state;
const req = `http://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&units=metric&appid=${
this.ApiKeyRef
}`;
const callback = responseJson => {
// console.log(responseJson);
// console.log(responseJson.city.name);
};
await fetch(req)
.then(response => response.json())
.then(responseJson =>
this.setState({ isLoading: false, dataSource: responseJson }, () =>
callback(responseJson)
)
)
.catch(error => console.log(error));
};
render() {
const { dataSource } = this.state;
if (this.state.isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View>
);
}
const icon =
dataSource.list[0].main.temp <= 20
? require("./assets/cloudySun.png")
: require("./assets/sunny.png");
return (
<ScrollView style={styles.container}>
<View style={styles.head}>
<Text style={styles.titleApp}>Weather App</Text>
</View>
<View style={styles.searchSection}>
<Icon
style={styles.searchIcon}
name="search"
size={15}
color="#333"
/>
<TextInput
style={styles.input}
placeholder="Find Your City.."
underlineColorAndroid="transparent"
/>
</View>
<View style={styles.details}>
{console.log(this.state.dataSource.city.name)} // I get the City name
<FlatList
data={this.state.dataSource}
renderItem={({ item }) => (
<Text>
{item.message}, {item.city.name}
{console.log(item)} // NO Output
</Text>
)}
keyExtractor={(item, index) => index}
/>
</View>
</ScrollView>
);
}
}
Add extraData props to FlatList. extraData is used for re-render the FlatList. extraData
Maybe it will help you.
<FlatList
data={dataSource}
extraData={this.state} //for re-render the Flatlist data item
renderItem={({ item }) => (
<Text>
{item.message}, {item.city.name}
</Text>
)}
keyExtractor={(item, index) => index}
/>
convert object response to the array
await fetch(req)
.then(response => response.json())
.then(responseJson =>{
let data = [];
data.push(responseJson); //convert object response to array
this.setState({ isLoading: false, dataSource: data }, () =>
callback(data)
)}
)
.catch(error => console.log(error));
you also have to change your logic for icons in render method:
const icon =
dataSource[0].list[0].main.temp <= 20
? require("./assets/cloudySun.png")
: require("./assets/sunny.png");

How to Fetch APIs and Print first element in response Array?

My React Native app fetch API data and I need to print the first index of response but it's not, and gets all of the "ozone" for example in all child of the parent Array and when I print val[0] when Mapping I have nothing printed
My Code|
export default class App extends Component {
constructor(props) {
super(props);
this.state = { isLoading: true, dataSource: null };
}
async componentDidMount() {
let API_WEATHER =
"https://api.weatherbit.io/v2.0/forecast/daily?city=Raleigh,NC&key={API_KEY}";
fetch(API_WEATHER)
.then(response => response.json())
.then(responseJson => {
console.log(responseJson.data);
this.setState({
isLoading: false,
dataSource: responseJson.data
});
})
.catch(error => {
console.log(error);
});
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator size="large" />
</View>
);
}
let weather= this.state.dataSource.map((val, key) => {
return (
<Text key={key}>
{val.ozone}
</Text>
);
});
return (
<ScrollView style={styles.container}>
<ScrollView>
<View>
<Text>{weather}</Text>
</View>
</ScrollView>
</ScrollView>
);
}
In this part of the code when i log the respone JSON obj
.then(responseJson => {
console.log(responseJson.data);
console.log(responseJson.data[0]);
console.log(responseJson.data[0].datetime);
}
i have what i need, but when print them in View i have Erroe
look at the Images
You're probably the first key of the object.
obj[Object.keys(obj)[0]];
Also, you can use
Try the for … in loop and break after the first iteration
for (var prop in object) {
// object[prop]
break;
}

Categories

Resources