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;
}
Related
I'm trying to get data from an array and using map function to render content. Look at
**{this.lapsList()}**
and the associated
**lapsList()**
function to understand what I'm trying to do. The result is nothing is displaying (Views under view, etc.) Here is my simplified code:
class StopWatch extends Component {
constructor(props) {
super(props);
this.state = {
laps: []
};
}
render() {
return (
<View style={styles.container}>
<View style={styles.footer}>
<View><Text>coucou test</Text></View>
{this.lapsList()}
</View>
</View>
)
}
lapsList() {
this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
}
_handlePressLap() {
console.log("press lap");
if (!this.state.isRunning) {
this.setState({
laps: []
})
return
}
let laps = this.state.laps.concat([{'time': this.state.timeElapsed}]);
this.setState({
laps: laps
})
console.log(laps);
}
}
Don't forget to return the mapped array , like:
lapsList() {
return this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
}
Reference for the map() method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Try moving the lapsList function out of your class and into your render function:
render() {
const lapsList = this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
return (
<View style={styles.container}>
<View style={styles.footer}>
<View><Text>coucou test</Text></View>
{lapsList}
</View>
</View>
)
}
lapsList() {
return this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
}
You forgot to return the map. this code will resolve the issue.
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>
);
}
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.
My data from the api is loaded in a component and returns a view containing the values, which does show in the other component. But I want an activity indicator on that page to show until the data is completely loaded.
I've followed some tutorials about setState with parent-child, child-parent, sibling-sibling and parentless relations. But none seem to work the way I want it.
This is the screen that shows first. It starts with the 'refreshing' view, which will show an activityindicator until the state.refreshing is set to false.
export default class AgendaScreen extends React.Component {
static navigationOptions = {
title: "Agenda"
};
constructor(props) {
super(props);
this.state={
refreshing: true
}
}
render() {
if (this.state.refreshing){
return(
//loading view while data is loading
<ImageBackground source={ScreenBackground} style={styles.container}>
<View style={{flex:1, paddingTop:20}}>
<ActivityIndicator />
</View>
</ImageBackground>
)
}
return(
<ImageBackground source={ScreenBackground} style={styles.container}>
<ScrollView>
<ImageBackground style={[styles.header,{justifyContent:'flex-end'}]} source = {HeaderImage}>
<Text style={{color:'white', fontSize:12, alignSelf:'center', backgroundColor:'transparent', marginBottom:2}}>
Complete summary
</Text>
</ImageBackground>
<Text style={styles.text}>Agenda</Text>
<Dates />
</ScrollView>
</ImageBackground>
);
}
}
This is the dates component, which gives me the view that I call with in the previous code block.
export default class Dates extends React.Component {
constructor(props) {
super(props);
this.state = {
data:[],
fill:[],
refreshing:true,
dates:[],
list:[]
}
}
componentDidMount() {
this.getData();
}
getData() {
fetch("API")
.then((result)=>result.json())
.then((res=>{
this.setState({
data:res,
refreshing:false
});
this.setState({
fill:this.state.data[0]
});
this.getDates();
this.loop();
}))
.catch(error =>{
console.error(error);
});
};
onRefresh() {
//Clear old data
this.setState({
data:[]
});
//Function to call api for latest data
this.getData();
};
getDates() {
var str = t(this.state.test, 'content.rendered').safeString
var arr = str.split(',');
var dates = [];
arr.forEach(function(e) {
dates.push(e.match(/;(\d{0,2}.[a-zA-Z]+)/g)[0].replace(';',''));
});
this.setState({
dates: dates
})
};
tempList=[];
loop(){
for (x=0;x<this.state.data.length;x++)
{
var fill=this.state.data[x]
this.tempList.push(
<View style={{flex:1}}>
<FlatList
data={[fill]}
renderItem={({item})=>
<View>
<Text style={styles.text}>
{t(item, 'title.rendered').safeObject}
</Text>
</View>
}
refreshControl={
<RefreshControl
//refresh control used for pull to refresh
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
/>
}
keyExtractor={(item, index) => index.toString()}
/></View>
)
}
this.setState({
list:this.tempList
})
}
render() {
return(
<View style={{flex:1, borderWidth:10}}><Text></Text>{this.state.list}
</View>
);
}
}
What I need is when Dates succesfully loaded his data from the api and returns the view, that the AgendaScreen state.refreshing will be stated to false.
Add below to your AgendaScreen Component
this.refresHandler = (e) =>{
this.setState({
refreshing:e
})
}
Add below props inside <Dates />
<Dates refresHandler={this.refresHandler}/>
change below code in Dates Component
getData() {
fetch("API")
.then((result)=>result.json())
.then((res=>{
this.setState({
data:res,
refreshing:false
});
this.setState({
fill:this.state.data[0]
});
this.getDates();
this.loop();
}))
.then(() =>{
this.props.refresHandler(this.state.refreshing)
})
.catch(error =>{
console.error(error);
});
}
I'm trying to make a game with react native and I want to show a different options when i change the picker value.
basically when I select the first option on the picker a component has to appear and when I select the second one another component.
I tried this function but not working
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
here is my code
export default class Facil extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true,
PickerValueHolder : '',
filter: [
{
"option":"Palabras por categoria"
},
{
"option":"Palabras por caracteres"
}
],
dataSource:[]
}
}
componentDidMount() {
return fetch(API_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
const resizeMode = 'stretch';
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
return (
<View style={styles.container}>
<Image source={require('../../Images/HomeLayout.png')}
style={styles.imagen}
/>
<View style={styles.mView}>
<View style={styles.panel}>
<Text style={styles.titlePanel}>MODO FACIL</Text>
<Text style={styles.instructions}>{instructions[0]}</Text>
<View style={styles.picker}>
<Picker
selectedValue={this.state.PickerValueHolder}
style={ {height: '100%',width: '100%'}}
mode="dropdown"
onValueChange={(itemValue, itemIndex) => this.setState({PickerValueHolder: itemValue})} >
{ this.state.filter.map((item, key)=>(
<Picker.Item label={item.option} value={item.option} key={key} />)
)}
</Picker>
</View>
<View style={styles.gameOpt}>
<Text>[dynamic options]</Text>
{pickerOptionText}
</View>
</View>
</View>
<TouchableOpacity style={styles.button}><Text style={styles.btnText}>Play!</Text></TouchableOpacity>
</View>
);
}
}
You forgot '()'.
pickerOptionText is a function, not a React component.
<Text>[dynamic options]</Text>
{pickerOptionText}
to:
<Text>[dynamic options]</Text>
{pickerOptionText()}
You can try using Conditional Rendering of JSX, by this you can use ternary operator and a simple if condition. this is written as:
{this.state.PickerValueHolder==this.state.filter[0] ?
<Text>{instructions[2]}</Text>
:<Text>{instructions[1]}</Text>
}
and if you need simple if condition then,
{ condition == true && <Text>your text here</Text>
}