React Native Function Call from Component - javascript

I am using Flatlist to show a list of Date. I have created a component DateList to view the list of item.
I have already called an API apiFreelancerDate. However, currently I am trying to call another function through the component (which I will later use it for second API call).
Date screen:
apiFreelancerDate = () => {
let self = this;
AsyncStorage.getItem('my_token').then((keyValue) => {
axios({
method: 'get',
url: Constants.API_URL + 'user_m/confirmation_date/',
params: {
freelancer_id: self.props.navigation.state.params.fr_id,
},
responseType: 'json',
headers: {
'X-API-KEY': Constants.API_KEY,
'Authorization': keyValue,
},
})
.then(function (response) {
self.setState({
dataSource: response.data.data,
isLoading: false,
});
})
.catch(function (error) {
console.log('Error Response: ', error.response);
});
}, (error) => {
console.log('Error', error) //Display error
});
}
//test function call
testFunc = () => {
console.log('test function called');
}
//test function call
viewFreelancerDate() {
const { dataSource } = this.state;
return (
<View>
{<FlatList
data={dataSource}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => <DateList {...item} functionCall={this.testFunc}/>}
/>}
</View>
);
}
DateList component:
<TouchableOpacity
style={{ flex: 1 }}
onPress={() => this.props.navigation.navigate('masterFreelancerTime')}
>
<Text style={styles.textStartDate}>{this.props.date_start}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.activeBorder}
onPress={() => {
this.props.functionCall;
const newStatus = !this.state.status;
this.setState({
status: newStatus,
});
console.log('status: ', this.state.status);
console.log('Date: ', this.props.date_start);
}}
>
<Text style={styles.active_inactive}>{this.state.status ? "INACTIVE" : "ACTIVE"}</Text>
</TouchableOpacity>
I am very new to this so any help would be appreciated and I don't know if it's the right way to do it.
My main purpose is that I want to add a function call that will call an API which will show the state "INACTIVE" : "ACTIVE" based on what's in the database. And upon pressing/tapping, the "INACTIVE" : "ACTIVE" will toggle and at the same time it'll update it's value in the database.

You are mostly on right track. Although you may want to keep few things in mind.
e.g.
<TouchableOpacity
style={styles.activeBorder}
onPress={() => {
this.props.functionCall;
const newStatus = !this.state.status;
this.setState({
status: newStatus,
});
console.log('status: ', this.state.status);
console.log('Date: ', this.props.date_start);
}}
>
This is an overoptimisic assignment to state.status before api call finishes. What if the api call fails, in that case, your status is not in-sync with database. You can update the state without waiting for API to return the response, but you should update the state again in case API fails.
If you don't need optimistic state update, then you can do something like following:
Date screen:
//test function call
testFunc = () => {
console.log('test function called');
const dataSource = makeAPiCallAndGetUpdatedApiSource();
this.setState({ dataSource });
}
//test function call
viewFreelancerDate() {
const { dataSource } = this.state;
return (
<View>
{<FlatList
data={dataSource}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => <DateList {...item} functionCall={this.testFunc}/>}
/>}
</View>
);
}
DateList component:
constructor(props) {
this.state = {
status: props.status
};
}
...
componentDidUpdate() {
this.setState({ status: this.props.status })
}
...
<TouchableOpacity
style={styles.activeBorder}
onPress={() => {
this.props.functionCall();
}}
>
This way, after the api call, state of date screen will be updated ... which in turn will update DateList component, which will update the state of component.

Related

How to refetch correct data in useInfiniteQuery() - react query?

I have a list of items and I'm using useInfiniteQuery for pagination stuff,
So I have a case where I can edit Item info then re-fetch the list again to get the updated list of items,
So I'm trying to re-fetch the list using
refetchPage() FC it works when I'm on the first page 1
but
current behavior:
when I scroll down to page 2 "last page at this moment." then update the item when
using queryClient.refetchQueries
it replaces the prev data with the re-fetched data on the current page.
await queryClient.refetchQueries(['getUsualOrders'], {
stale: true,
});
using refetch().
refetchUsualOrderList({
refetchPage: (page, index) => {
return index === 0;
},
});
it replaces the prev data with current page data without the updated data
Actually, it sends a request for the next page "that doesn't exist"
So how can i re-fetch the data and keep the previous data?
Code
Hook
export const useUsualOrders = ({lang, pageNumber = 1, token}) => {
return useInfiniteQuery(
['getUsualOrders'],
() => getUsualOrders({lang, pageNumber, token}),
{
getNextPageParam: lastPage => {
if (lastPage.next !== null) {
return lastPage.next;
}
return undefined;
},
},
);
};
UI
const [currentPageNumber, setCurrentPageNumber] = useState(1);
const {
data: usualOrdersList,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
refetch: refetchUsualOrderList,
} = useUsualOrders({
lang,
token: currentUserInfo?.token,
pageNumber: currentPageNumber,
});
const loadMore = () => {
if (hasNextPage) {
setCurrentPageNumber(prev => prev + 1);
fetchNextPage();
}
};
const onRefetch = ()=>{
refetchUsualOrderList({
refetchPage: (page, index) => {
return index === 0;
},
});
}
<FlatList
showsVerticalScrollIndicator={false}
data={usualOrdersList.pages.map(page => page.results).flat()}
renderItem={renderModelItems}
keyExtractor={item => item.id}
onEndReached={loadMore}
onEndReachedThreshold={0.3}
// refreshControl={
// <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
// }
style={{
backgroundColor: secondary_bg_color,
}}
contentContainerStyle={[
styles.modelList,
{
backgroundColor: secondary_bg_color,
paddingHorizontal: PADDING_HORIZONTAL.SMALL,
},
]}
ListFooterComponent={
isFetchingNextPage ? (
<ActivityIndicator color={buttonBgColor} />
) : null
}
/>
response body

How to render new data (or GET new) after making a POST request with axios?

I'm using mockapi.io to practice with Axios API call
After I make a POST request, which create a new data, I want to render FlatList with the updated data. I'm thinking of making a new GET request to do that, but I'm not succeeded with it.
I need help
Here is where I call GET request, which already have mock data, and use FlatList to view it
ListScreen.js
class ListScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
render() {
const { data } = this.state;
return (
<View>
<FlatList
data={data}
renderItem={({ item }) => {
return (
<Item
name={item.lastName}
phone={item.phoneNumber}
/>
);
}}
keyExtractor={(item) => item.id}
/>
</View>
And here is where I call POST request
class Create extends Component {
handleSubmitData = (value) => {
console.log('check value: ', value)
axios.post('insert url', {
lastName: `${value.Name}`,
phoneNumber: `${value.Phone}`,
})
.then((response) => {
console.log('here is what you upload: ', response.data)
})
.catch((err) => {
console.log('Run into problem')
})
}
render() {
return (
<CreateForm
handleSubmitData={this.handleSubmitData}
/>
)
}
}
The CreateForm component looks something like this
class CreateForm extends Component {
render() {
const { handleSubmit } = this.props;
return (
<View>
<View>
<Field
name="Name"
component={}
/>
<Field
name="Phone"
component={}
/>
</View>
<View>
<Button
title='Save'
onPress={handleSubmit(this.props.handleSubmitData)}
/>
</View>
</View>
)
This can be done by lifting the state up, from ListScreen to ListScreen's parent.
In ListScreen, take out the state and move into its parent:
this.state = {
data: [],
}
Pass the state down to ListScreen as a prop, also pass the fetch function (see below):
<ListScreen data={this.state.data} fetchListData={this.fetchListData} />
Change ListScreen's render function to access the data from props:
const { data } = this.props;
Move the axios.get() out of ListScreen and into a method on the parent component.
class App extends Component {
fetchListData() {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
}
In ListScreen, call the function in componentDidMount:
componentDidMount() {
this.props.fetchListData();
}
Alternatively, you could call it in the parent component's componentDidMount(). This might be preferred if you render multiple ListScreen's for example.
Finally, in the Create component, pass the fetch function:
<Create fetchListData={this.fetchListData} />
Call it on successful creation:
.then((response) => {
this.props.fetchListData();
console.log('here is what you upload: ', response.data)
})
As I see in your code, when you are making a POST request it wont display an updated data to your screen because there is no GET request callback upon the success result.
Suggesting you are not using redux , in ListScreen.js you could wrap the GET request into a function and call it in componentDidMount(). It should be look like:
const getData () => {
axios.get('insert url')
.then(res => {
this.setState({
data: res && res.data ? res.data : []
})
})
.catch(err => {
console.log('Run into problem')
})
}
componentDidMount() {
this.getData()
}
Therefore, you need to pass or drill the GET request function into your child component as a props and use it in a POST request callback. The final POST request method should be look like:
handleSubmitData = (value) => {
console.log('check value: ', value)
axios.post('insert url', {
lastName: `${value.Name}`,
phoneNumber: `${value.Phone}`,
})
.then((response) => {
console.log('here is what you upload: ', response.data)
getData()
})
.catch((err) => {
console.log('Run into problem')
})
}
As you can see, after your POST request is finished, it will trigger a GET request to update your parent state, resulting a screen with an updated data. However, you have to make sure to pass your parameters correctly based on how your component structure is.

react native : there is way to message to the screen if axios id fail?

there is way to make a message to the screen when the get axios is fail .
how to do it ?
this is my example code :
getData = () => {
this.setState({ isLoading: true, data: [] });
var userPrincipalName = this.state.userPrincipalName;
///for debbugin only--NEED TO CHANGE "THE_NAME_USER" TO "userPrincipalName" IN THE AXIOS
THE_NAME_USER = "apaz";
axios
.get(
"https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/" +
THE_NAME_USER
)
.then(res => {
this.setState({
isLoading: false,
data: res.data
});
InfoStore.setList(res.data);
});
};
There are multiple ways to display an error message when something happens when API failed.
Check below example & change this according to your requirements.
import React from "react";
import { FlatList, ActivityIndicator, Text, View } from "react-native";
import axios from "axios";
export default class Example extends React.Component {
state = {
isLoading: true,
isError: false
};
componentDidMount() {
return axios
.get("https://reactnative.dev/movies.json")
.then(response => {
this.setState({
isLoading: false,
data: response.data.movies
});
})
.catch(error => {
this.setState({
isLoading: false,
isError: true
});
});
}
render() {
return (
<View style={{ flex: 1, paddingTop: 20 }}>
{
// Shows loding indicator until data loads
this.state.isLoading ?
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View>
:
// Shows an error if an error occured
this.state.isError ?
<View style={{ flex: 1, padding: 20, alignItems: 'center' }}>
<Text>Oops 😢 error while loading</Text>
</View>
:
// Display data after successful fetch
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<Text>
{item.title}, {item.releaseYear}
</Text>
)}
keyExtractor={({ id }, index) => id}
/>
}
</View>
);
}
}
Hope this helps you. Feel free for doubts.
Yes, it is possible, use two ".then" statement like this
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/" +
THE_NAME_USER
).then(response =>
{
const statusCode = response.status;
const data = response.json();
return Promise.all([statusCode, data]);
}).then(res => {
this.setState({
isLoading: false,
data: res.data
});
InfoStore.setList(res.data);
// console.log("InfoStore get;List", InfoStore.getList.slice());
}).catch(error => {
console.log(error);
alert(“your error is here”)
});
First ".then" actually deal HTTP response of your API call, if it's not working or error happens then it gives respective error code (for example, in success case it makes "200 OK" and error case return 500 or other error codes). you can easily show alert on these status code and check your api call data accordingly. error codes description are available on this link
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

Get storage data if there is no internet connection in React Native

I create an application that retrieves data from a URL (an array of objects) and display it in FlatList.
Currently, when launching the application the data is displayed correctly (they are retrieved asynchronously). If I switch to airplane mode, there is the message "No internet connection" but the data of my AsyncStorage are not displayed (the background of the application is white). If I disable airplane mode, my data will be displayed again.
class MontanteTab extends Component {
state = {
errors: null,
isLoading: true,
isConnected: true,
refreshing: false,
pronostics: [],
};
async componentDidMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivityChange);
if (this.state.isConnected) {
await this.loadPronostics();
}
try {
this.setState({pronostics: JSON.parse(await AsyncStorage.getItem(Keys.pronosticsMontante))});
} catch (error) {
console.log(error);
}
}
handleConnectivityChange = isConnected => {
console.log(isConnected);
this.setState({isConnected: isConnected});
};
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectivityChange);
}
onRefresh = () => {
console.log('refreshing...');
this.setState({refreshing: true});
this.loadPronostics();
this.setState({refreshing: false});
console.log('refreshed...');
};
loadPronostics() {
this.setState({isLoading: true, error: null});
return axios.get(AppConfig.apiUrl + 'montante').then(async response => {
await AsyncStorage.setItem(Keys.pronosticsMontante, JSON.stringify(response.data));
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>
)
}
if (!this.state.isConnected) {
return (
<OfflineNotice/>
)
}
return (
<View>
<FlatList
data={this.state.pronostics}
extraData={this.state.pronostics}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
title="Glisser pour rafraîchir"
tintColor="#fff"
titleColor="#fff"
/>
}
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, overflow: 'hidden'}}/>}
title={item.competition}
subtitle={item.equipe_domicile + ' - ' + item.equipe_exterieur}
onPress={() => this.props.navigation.navigate('PronosticsDetails', {
item,
})}
/>
)}
/>
</View>
);
}
}
How can I display my AsyncStorage data when there is no more internet connection?
And I have a bonus question : when I add a new data in my API and make a pull to refresh on my FlatList, my FlatList doesn't update. Why please?
If you want to display the flatlist while you have no internet connection but have it locally stored, replace:
if (!this.state.isConnected) {
return (
<OfflineNotice/>
)
}
with:
if (!this.state.isConnected && this.state.pronostics.length === 0) {
return (
<OfflineNotice/>
)
}
And React views refresh after a state change, such as one with this.setState. If you want to force a update manually after you "pull" your data, use this.forceUpdate.

React Native FlatList load more when we get to the bottom of the list

How to make load more with FlatList of React Native (Not infinite)
I've done this, but unfortunately it loads as infinitely.
This is my code snippet
<FlatList
data={this.props.data}
renderItem={({ item, separators }) => (
<TouchableHighlight
onPress={() => this._onPress(item)}
onShowUnderlay={separators.highlight}
onHideUnderlay={separators.unhighlight}
>
<Text> {item.title} </Text>
</TouchableHighlight>
)}
keyExtractor={item => item.id}
ListFooterComponent={this.renderFooter}
onEndReached={this.props.handleLoadMore}
onEndThreshold={0}
/>
And my handleLoadMore
handleLoadMore = () => {
console.log("test"); // <---- this line run infinitely
fetch(url, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(filters)
})
.then(response => response.json())
.then(responseJson => {
this.setState({
itemData: [
...this.state.itemData,
...responseJson.estate_list
],
itemPage: this.state.itemPage + 1
});
})
.catch(error => {
console.error(error);
});
};
There is issue when loading data in FlatList and your onEndReached handler will be called when the view is re-rendered. Try setting a flag like this :
constructor(props){
super(props);
this.state = {
hasScrolled: false
}
}
Then add this method :
onScroll = () => {
this.setState({hasScrolled: true})
}
Hook it up to FlatList:
<FlatList
onScroll={this.onScroll}
Finally load only when scrolled :
handleLoadMore = () => {
if(!this.state.hasScrolled){ return null; }
//here load data from your backend
}
constructor(props){
super(props);
this.state = {
loading: true
}
}
<FlatList
onEndReached={this.handleLoadMore}/>
handleLoadMore = () => {
if(!this.state.loading){ return null; }
this.setState({
page: this.state.page + 1,
loading: false
}, () => {
this.loadProducts();
});
};
loadProducts(catId,userkey){
$this.setState({
loading:true
});
}

Categories