How to store the data when fetching from API - javascript

I create an application that retrieves data from a URL and display it. I'm a beginner and therefore I do not use Redux or other for the moment.
I managed to recover the data and display it on my application but I would like to use the local storage of the phone. I saw the examples for AsyncStorage on the documentation of the Expo website but I don't know how to adapt them to my code. In addition, do I have to display local storage data only when there is no internet connection? Or do I always still have to display them?
import React, {Component} from 'react';
import {ScrollView, View, FlatList, Image, ActivityIndicator} from 'react-native';
import {ListItem} from "react-native-elements";
import {createAppContainer, createStackNavigator} from "react-navigation";
import PronosticsDetailsScreen from "../../screens/PronosticsDetailsScreen";
import AppConfig from "../../AppConfig";
class MontanteTab extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
return fetch('https://myurl.com')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function () {
});
})
.catch((error) => {
console.error(error);
});
}
render() {
if (this.state.isLoading === true) {
return (
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<View>
<ScrollView>
<View>
<FlatList
data={this.state.dataSource}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => (
<ListItem
key={item.id}
roundAvatar
badge={{
value: item.statut,
textStyle: {color: '#fff'},
containerStyle: {marginRight: 0, backgroundColor: item.couleur}
}}
avatar={<Image
source={{uri: 'https://myurl.com/' + item.image}}
style={{borderRadius: 50, height: 50, width: 50}}/>}
title={item.competition}
subtitle={item.equipe_domicile + ' - ' + item.equipe_exterieur}
onPress={() => this.props.navigation.navigate('PronosticsDetails', {
item,
})}
/>
)}
/>
</View>
</ScrollView>
</View>
);
}
}
EDIT :
I tried this, but my data are not displayed :
import React, {Component} from 'react';
import {ScrollView, View, FlatList, Image, ActivityIndicator, AsyncStorage} from 'react-native';
import axios from "axios";
import {ListItem} from "react-native-elements";
import {createAppContainer, createStackNavigator} from "react-navigation";
import AppConfig from "../../AppConfig";
import Keys from "../../data/Constants/Storage";
import PronosticsDetailsScreen from "../../screens/PronosticsDetailsScreen";
class MontanteTab extends Component {
state = {
errors: null,
isLoading: true,
pronostics: [],
};
async componentDidMount() {
const isConnected = true;
if (isConnected) {
await this.loadPronostics();
}
try {
this.setState({pronostics: JSON.parse(await AsyncStorage.getItem(Keys.pronosticsMontante))});
} catch (error) {
console.log(error);
}
}
loadPronostics() {
this.setState({isLoading: true, error: null});
return axios.get(AppConfig.apiUrl + 'montante').then(async response => {
await AsyncStorage.setItem(Keys.pronosticsMontante, JSON.stringify(this.state.pronostics));
this.setState({isLoading: false});
}).catch(error => {
this.setState({isLoading: false, error: error.response});
console.log(error);
});
}
render() {
if (this.state.isLoading === true) {
return (
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<View>
<ScrollView>
<View>
<FlatList
data={this.state.pronostics}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => (
<ListItem
key={item.id}
roundAvatar
badge={{
value: item.statut,
textStyle: {color: '#fff'},
containerStyle: {marginRight: 0, backgroundColor: item.couleur}
}}
avatar={<Image
source={{uri: AppConfig.imagesPronosticsUrl + item.image}}
style={{borderRadius: 50, height: 50, width: 50}}/>}
title={item.competition}
subtitle={item.equipe_domicile + ' - ' + item.equipe_exterieur}
onPress={() => this.props.navigation.navigate('PronosticsDetails', {
item,
})}
/>
)}
/>
</View>
</ScrollView>
</View>
);
}
}

You can use React Native AsyncStorage for storing data to local storage of the device.
import { AsyncStorage } from 'react-native'
Use this to save data
AsyncStorage.setItem('key', 'value');
AsyncStorage accepts value as an only string, so you may need to use stringify() before setting the value to AsyncStorage
And to retrieve data use
AsyncStorage.getItem('key');
Code:
const KEY = 'USER_DATA'
let keyValue = { name: yogi }
AsyncStorage.setItem(KEY,keyValue);
AsyncStorage.getItem(KEY).then(asyncStorageRes => {
console.log(JSON.parse(asyncStorageRes))
});

As this is a React Native project, I think AsyncStorage is what you're looking for. You can set the value in your empty setState callback in componentDidMount. If you only need to store the data at the end of a session, you can use AppState and set the value when nextState is background or inactive.
Use AsyncStorage.getItem() when AppState or nextState is active.
https://facebook.github.io/react-native/docs/appstate
https://facebook.github.io/react-native/docs/asyncstorage

Its depend on how frequently your listing data change,
if listing data is constant then you can store API response in local storage. and then display list data from local storage.
If listing data changing frequently, still you can use local storage. store data in local storage on API response.
On next time page load show data from local storage and also make API call to get latest data and on API response display data to list and also update in local storage.
Using this way user will not have to wait for API response.
For Storing Data use can use javascript syntax localStorage.setItem('key', 'apiresponse') and localStorage.getItem('key')
OR
can create Service class which will store API response in object, which can be also use in other files.

Related

Inability to get data from API react native

Hi so my main question is when making a api call to https://newsdata.io, i want to access the results method on the object the api returns. However react native is saying results is undefined. Why cant i see the data object but not the methods attached to the data variable.
import React from 'react'
import { Text, View, Image } from 'react-native'
export const Feed = () => {
async function data() {
const response = await fetch('https://newsdata.io/api/1/news?apikey=pub_11306c8c5e2932eab7155edacbc6339247174&q=web%203')
const data = await response.json()
const results = data.results;
const imageURLDATA = results.forEach(element => {
console.log(element.image_url)
})
}
data()
return (
<View>
<Text style={{fontSize: 40, fontWeight: "700", marginTop: 20}}>Feed</Text>
{results.forEach(element => {
<View>
< Image source={{
uri: `${element.image_url}`
}}/>
</View>
})}
</View>
)
}
You need to take the component lifecycle into consideration. Use useState to create a variable that trigger component rerenders and useEffect to call functions at certain events:
import React, { useState, useEffect } from 'react';
import { View, Image, Text } from 'react-native';
export const Feed = () => {
const [feedData, setFeedData] = useState([]);
async function getData() {
// you may want to change your api key
const response = await fetch(
'https://newsdata.io/api/1/news?apikey=pub_11306c8c5e2932eab7155edacbc6339247174&q=web%203'
);
const data = await response.json();
const results = data.results;
setFeedData(results);
}
//will call provided function when items in array is updated
useEffect(() => {
console.log('Feed data updated')
feedData.forEach((element) => {
console.log(element.image_url);
});
}, [feedData]);
// will call provided function once after first render
useEffect(() => {
getData();
}, []);
return (
<View>
<Text style={{ fontSize: 40, fontWeight: '700', marginTop: 20 }}>
Feed
</Text>
{/*forEach returns null, map returns value*/}
{feedData.map((element) => (
<View>
<Image
source={{
uri: `${element.image_url}`,
}}
// provide width to element or it wont render
style={{width:100,height:100}}
/>
</View>
)
)}
</View>
);
};
export default Feed;
Here's a demo

Flat list is not rendering

I'm trying to display a flat list (values form json placeholder) filterable with a search bar and it's not rendering for some reason. The values are not visible. Thanks!
The flat list code:
import React, { Component } from "react";
import { View, Text, FlatList, Button } from "react-native";
import { ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
temp: [],
error: null,
search: null
};
}
componentDidMount() {
this.getData();
}
getData = async () => {
const url = `https://jsonplaceholder.typicode.com/users`;
this.setState({ loading: true });
try {
const response = await fetch(url);
const json = await response.json();
this.setResult(json);
} catch (e) {
this.setState({ error: 'Error Loading content', loading: false });
}
};
setResult = (res) => {
this.setState({
data: [...this.state.data, ...res],
temp: [...this.state.temp, ...res],
error: res.error || null,
loading: false
});
}
renderHeader = () => {
return <SearchBar placeholder="Search Here..."
lightTheme round editable={true}
value={this.state.search}
onChangeText={this.updateSearch} />;
};
updateSearch = search => {
this.setState({ search }, () => {
if ('' == search) {
this.setState({
data: [...this.state.temp]
});
return;
}
this.state.data = this.state.temp.filter(function(item){
return item.name.includes(search);
}).map(function({id, name, email}){
return {id, name, email};
});
});
};
render() {
return (
this.state.error != null ?
<View style={{flexDirection: 'column',justifyContent: 'center', alignItems: 'center' }}>
<Text>{this.state.error}</Text>
<Button onPress={
() => {
this.getData();
}
} title="Reload" />
</View> :
<FlatList
ListHeaderComponent={this.renderHeader}
data={this.state.data}
keyExtractor={item => item.email}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.email}
/>
)}
/>
);
}
}
export default FlatListDemo;
Importing this list to:
import React, {useState, useEffect} from 'react'
import { Text, View, StyleSheet, StatusBar, SafeAreaView } from "react-native"
import "firebase/auth";
import 'react-native-gesture-handler';
import "firebase/auth";
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import FlatListDemo from './FlatListDemo';
const Home: () => React$Node = () => {
return(
<>
<StatusBar barStyle="dark-content"/>
<SafeAreaView style={{flex: 1}}>
<FlatListDemo/>
</SafeAreaView>
</>
);
};
export default Home;
This is what it looks like (values should be there). Using Expo on Android:
photo1
Thanks! I appreciate the help!
It seems to me that you are using a ListElement as it is defined in version 1.2.0 of react-native-elements but the actual version that you are using is > 1.2.0.
You are implementing it similar to the documentation of react-native-elements 1.2.0.
However, the definition of ListItem has changed in newer version. In the newest version, the ListItem component is just a simple view wrapper. Hence, it needs to contain children in order to display the information that you want.
In your case this could be implemented as follows (I did not know what roundAvatar is doing).
renderItem={({ item }) => (
<ListItem>
<ListItem.Content>
<ListItem.Title>{`${item.name}`}</ListItem.Title>
<ListItem.Subtitle>{item.email}</ListItem.Subtitle>
</ListItem.Content>
</ListItem>
Check the documentation of the newest version for the exact features that you want to use.

How to display data retrieved from firebase in react naive for ios

My team is working on a tutoring app where students send in requests from the student side to the tutor side. We are able to reference firebase for out data and see it in the console. But we are not able to display on the page.
This is the tutor side code here:
import React from 'react';
import { FlatList, Text, StyleSheet, View } from 'react-native';
import firebaseRef from './Firebase';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = ({
sessions: []
});
}
componentDidMount(){
const { uid } = firebaseAuth.currentUser
firebaseRef.database().ref('Sessions/' + uid).on('value', function (snapshot) {
sessions = snapshot.val()
this.setState({
sessions: sessions,
sessions,
});
})
}
getSessionsRef = () => {
return firebaseRef.database().ref('sessions')
}
render(){
const {sessions} = this.state
return(
<View style={styles.container}>
<FlatList>
<Text>{sessions}</Text>
</FlatList>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex: 1,
justifyContent:'center',
padding:-1.5
}
});
Something like this:
<FlatList
data={sessions}
keyExtractor={(item, index) => item.id}
renderItem={({item}) => (
<Text>{item}</Text>
)}
/>

SetState doesn't seem to update

I'm working on a very simple react-native app where I type the name of an artist in a searchbox, retrieve a list of artists from the spotify API and I display this list in FlatList component.
I manage to get the list of artists and I want to save it in the local state so that I pass it to the FlatList component.
The list object looks like this : [{...}, {...}, {...}, {...}]
But it doesn't seem to work and I think that my state is not updating and I don't know what I'm doing wrong.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
FlatList,
StatusBar,
TextInput,
} from 'react-native';
import colors from './utils/colors';
import { List, ListItem, SearchBar } from 'react-native-elements';
import { searchArtist } from './utils/fetcher';
import { debounce } from 'lodash';
export default class spotilist extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
query: '',
artists: [],
error: null,
refreshing: false,
};
}
render() {
return (
<View style={ styles.container }>
<StatusBar barStyle="light-content" />
<TextInput style={ styles.searchBox }
value={this.state.value}
onChangeText={ this.makeQuery }
/>
<List>
<FlatList
data={this.state.artists}
//renderItem={({item}) => <Text>{item.name}</Text>}
/>
</List>
// {
// this.state.artists.map(artist => {
// return (
// <Text key={artist.id}>{artist.name}</Text>
// )
// })
// }
</View>
);
}
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
console.log(artists); // I have the list
this.setState({
artists: this.state.artists,
});
})
.catch((error) => {
throw error;
});
}, 400);
}
Thank you for your help.
UPDATE
I also tried using this without success :
<List>
<FlatList
data={this.state.artists}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={item.name}
avatar={{ uri: item.images[0].url }}
/>
)}
/>
</List>
In the makeQuery function you need to set the response from the server like..
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
console.log(artists); // I have the list
this.setState({
artists: artists, //Here is the change
});
})
.catch((error) => {
throw error;
});
}, 400);

React Native/Redux - Can't access Redux store

I'm trying to access two parts of my Redux store within a React Native component that I'm working on and for some reason, probably something simple, I can't get things working.
I'm passing the orderID as a property to the component and then I want to retrieve the appropriate order details from the Redux store: orders[orderID] but when trying to assign the local variable:
const order = this.props.orders[orderID];
I'm getting the error: Cannot read property 'orders' of undefined, i.e.: for some reason the orders part of the Redux store appears to not have connected to the component props.
Code is as follows:
import React from 'react';
import { connect } from 'react-redux';
import {
View,
Text
} from 'react-native';
import Status from '../Status';
import Card from './Card';
import CardSection from './CardSection';
const OrderDetail = ({ orderID }) => {
const order = this.props.orders[orderID];
const {
id,
status,
gross_price,
currency_symbol,
bookings
} = order;
return (
<Card>
<CardSection>
<View style={styles.headerContentStyle}>
<View style={styles.bookingIdHeaderContainerStyle}>
<Text style={styles.headerTextStyle}>Booking #{id}</Text>
</View>
<View style={styles.grossPriceHeaderContainerStyle}>
<Text style={styles.headerTextStyle}>{currency_symbol}{gross_price}</Text>
</View>
<View style={styles.statusHeaderContainerStyle}>
<Status status={status} />
</View>
</View>
</CardSection>
<CardSection>
<View style={styles.orderListContentStyle}>
<Text>Booking #1234</Text>
</View>
</CardSection>
</Card>
);
};
const styles = {
headerContentStyle: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
headerTextStyle: {
fontSize: 18
},
bookingIdHeaderContainerStyle: {
flex: 5
},
grossPriceHeaderContainerStyle: {
flex: 2
},
statusHeaderContainerStyle: {
flex: 2
},
orderListContentStyle: {
}
};
const mapStateToProps = state => {
return ({
orders: state.orders,
bookings: state.bookings
});
};
export default connect(mapStateToProps)(OrderDetail);
Any suggestions?
OrderDetail is a stateless functional component, and functional components don't have a this keyword.
May be this is what you wanted:
const OrderDetail = (props) => {
const order = props.orders[props.orderID];
// ...

Categories