please help me out to implement pull to refresh on my app, I'm kinda new to react native, thanks. I don't know how to handle onRefresh and refreshing.
class HomeScreen extends Component {
state = { refreshing: false }
_renderItem = ({ item }) => <ImageGrid item={item} />
_handleRefresh = () => {
};
render() {
const { data } = this.props;
if (data.loading) {
return (
<Root>
<Loading size="large" />
</Root>
)
}
return (
<Root>
<HomeHeader />
<FlatList
contentContainerStyle={{ alignSelf: 'stretch' }}
data={data.getPosts}
keyExtractor={item => item._id}
renderItem={this._renderItem}
numColumns={3}
refreshing={this.state.refreshing}
onRefresh={this._handleRefresh}
/>
</Root>
);
}
}
export default graphql(GET_POSTS_QUERY)(HomeScreen);
Set variable
this.state = {
isFetching: false,
}
Create Refresh function
onRefresh() {
this.setState({isFetching: true,},() => {this.getApiData();});
}
And in last set onRefresh and refreshing in FlatList.
<FlatList
data={ this.state.FlatListItems }
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
/>
After fetching Data in function getApiData Make sure to set false isFetching.
this.setState({ isFetching: false })
You can also use refreshControl in Flatlist, ScrollView, and any other list component.
<FlatList
contentContainerStyle={{ alignSelf: 'stretch' }}
data={data.getPosts}
keyExtractor={(item) => item._id}
renderItem={this._renderItem}
numColumns={3}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._handleRefresh}
/>
}
/>
If someone is interested in doing it with React Hooks here is it:
First create isRefreshing state:
const [isRefreshing, setIsRefreshing] = useState(false)
Then create your onRefresh function that calls API to refresh data:
const onRefresh = () => {
//set isRefreshing to true
setIsRefreshing(true)
callApiMethod()
// and set isRefreshing to false at the end of your callApiMethod()
}
And after that, your FlatList component that looks like that:
return (
<FlatList
style={styles.flatList}
onRefresh={onRefresh}
refreshing={isRefreshing}
data={data}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
/>
)
// Sample code representing pull to refresh in flatlist. Modify accordingly.
import React, { Component } from 'react'
import { Text, View,StyleSheet,FlatList,Dimensions,Image,TouchableHighlight } from 'react-native'
export default class Home extends Component {
constructor(props)
{
super(props);
this.state = {
data : [],
gender : "",
isFetching: false,
}
}
componentWillMount()
{
this.searchRandomUser()
}
onRefresh() {
this.setState({ isFetching: true }, function() { this.searchRandomUser() });
}
searchRandomUser = async () =>
{
const RandomAPI = await fetch('https://randomuser.me/api/?results=20')
const APIValue = await RandomAPI.json();
const APIResults = APIValue.results
console.log(APIResults[0].email);
this.setState({
data:APIResults,
isFetching: false
})
}
render() {
return (
<View style = {styles.container}>
<TouchableHighlight
onPressOut = {this.searchRandomUser}
style = {{width:deviceWidth - 32, height:45,backgroundColor: 'green',justifyContent:"center",alignContent:"center"}}>
<Text style = {{fontSize:22,color: 'white',fontWeight: 'bold',textAlign:"center"}}>Reload Data</Text>
</TouchableHighlight>
<FlatList
data={this.state.data}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
keyExtractor = { (item, index) => index.toString() }
renderItem={({item}) =>
<View style = {styles.ContainerView}>
<View>
<Image
source={{uri : item.picture.large}}
style = {{height:100,width:100,borderRadius: 50,marginLeft: 4}}
resizeMode='contain'
/>
</View>
<View style = {{flexDirection: 'column',marginLeft:16,marginRight: 16,flexWrap:'wrap',alignSelf:"center",width: deviceWidth-160}}>
<Text>Email Id : {item.email}</Text>
<Text>Full Name : {this.state.gender} {item.name.first} {item.name.last}</Text>
<Text>Date of birth : {item.dob.age}</Text>
<Text>Phone number : {item.phone}</Text>
</View>
</View>
}
/>
</View>
)
}
}
const deviceWidth = Dimensions.get('window').width
const deviceHeight = Dimensions.get('window').height
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginTop:22
},
ContainerView:
{
// backgroundColor:'grey',
marginBottom:20,
paddingVertical:10,
backgroundColor: '#F5F5F5',
borderBottomWidth:0.5,
borderBottomColor:'grey',
width: deviceWidth-40,
marginLeft:20,
marginRight:20,
marginTop:20,
flexDirection:'row'
}
});
const [refreshing, setRefreshing] = useState(false)
<FlatList
style={{ padding: 15 }}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={_onRefresh}
tintColor="#F8852D"/>
}
showsVerticalScrollIndicator={false}
data={listData}
renderItem={renderItem}
item={(item, index) => index.toStirng()}
onEndReachedThreshold={0.8}
onEndReached={() => onEndReached()}
ListFooterComponent={() => renderFooter()}
/>
_onRefresh Function
const _onRefresh = () => {
console.log('_onRefresh')
setRefreshing(true);
getPosts();
};
"Pull to refresh" concept implies that the list can be manually refreshed thus can be changed outside the current view (e.g. fetched from server). So the callback onRefresh has to trigger the data reloading process (e.g. send a request to the server) and set the refreshing variable to truthy value. This will notify the user that the process was started by showing loading indicator. Once you got the data ready you need to set refreshing to falsy so the view will hide loading indicator.
this is the best that I can do. my Code Image
when I pull it down it dosen't refetch data from server I'm running graphql server which connected by Apollo to the app, and I don't know how to get data from server in _getData() function.
Instead of using pull to refresh on flat-list simply use on Scroll View.
Set Refresh State
this.state = {
isFetching: false,
}
Set onFresh in ScrollView
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.refresh}
onRefresh={() => this.onRefresh()}
tintColor="red"
/>
}>
<FlatList data={data} renderItem={this._renderItem} numColumns={3} />
</ScrollView>;
In OnRefreshFunction
onRefresh() {
this.setState({refresh: true});
setTimeout(() => {
this.setState({refresh: false});
}, 2000);
}
You can check the official document:
https://reactnative.dev/docs/refreshcontrol
For anyone looking for an updated answer using new React Hooks.
You can use isLoading and refetch from useQuery.
Then use RefreshControl with FlatList. Read more on RefreshControl: https://reactnative.dev/docs/refreshcontrol
const RenderImageGrid = (item) => <ImageGrid item={item} />
export const HomeScreen = () => {
const { data, isLoading, refetch } = useQuery('get_home_screen_data', GET_HOME_SCREEN_DATA)
return (
{isLoading ? (
<Root>
<Loading size="large" />
</Root>
) : (
<Root>
<HomeHeader />
<FlatList
data={data}
keyExtractor={item => item._id}
renderItem={RenderImageGrid}
numColumns={3}
refreshControl={
<RefreshControl
refreshing={isLoading}
onRefresh={refetch}
/>
}
/>
</Root>
)}
)
}
export default HomeScreen
Related
Hello I'm facing with render error in my movie app during printing results for movie searching. Im working in React-Native 0.70.5. Here is some code for this activity
`
import React,{useState,useEffect} from 'react';
import axios from 'axios';
import { View, StyleSheet, Text, TextInput,ScrollView } from "react-native";
const Search = () => {
const apiurl="https://api.themoviedb.org/3/search/movie?api_key=XXX"
const [state,setState] = useState({
s: "Enter a movie",
results:[]
});
const search = () => {
axios(apiurl + "&query="+ state.s).then(({ data }) => {
let results = data.search;
console.log(data);
setState(prevState => {
return{...prevState, results: results }
})
})
}
return (
<View>
<TextInput
onChangeText={text => setState(prevState => {
return {...prevState, s:text}
})}
onSubmitEditing={search}
value={state.s}
/>
<ScrollView>
{state.results.map(result =>(
<View key={result.id}>
<Text>{result.title}</Text>
</View>
))}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
center: {
flex: 1,
justifyContent: "center",
alignItems: "center",
textAlign: "center",
},
});
export default Search;
`
How to change the construction of this function to print movie titles corectly ?
This is just an example and not the best way to do it.
You need a success flag, something like this:
const [success,setSuccess] = useState(false);
const search = () => {
axios(apiurl + "&query="+ state.s).then(({ data }) => {
let results = data.results;
console.log(data);
setState(prevState => {
return{...prevState, results: results }
})
setSuccess(true);
})
}
<ScrollView>
{success && state.results.map(result =>(
<View key={result.id}>
<Text>{result.title}</Text>
</View>
))}
</ScrollView>
I am not sure but you can try this below ScrollView code...
<ScrollView>
{state.results.length>0 && (state.results.map((result) =>(
<View key={result.id}>
<Text>{result.title}</Text>
</View>
)))}
</ScrollView>
use optional chaining. Might be sometimes you don't get result.
<ScrollView>
{state?.results?.map(result =>(
<View key={result?.id}>
<Text>{result?.title}</Text>
</View>
))}
</ScrollView>
Let me know is it helpful or not
I have a profile component which is a tab screen, and here is the code:
class Profile extends React.Component {
constructor(props) {
super(props)
this.state = {
user: this.props.user,
bio: "",
storage_image_uri: '',
postCount: 0,
followerCount: 0,
followingCount: 0,
isLoading: true,
navigation: this.props.navigation,
userUID: Firebase.auth().currentUser.uid,
userPostsArray: []
}
this.firestoreRef =
Firebase.firestore()
.collection('globalPosts')
.where("uid", "==", this.state.userUID)
.orderBy("date_created", "desc");
}
componentDidMount() {
this.pullUserInfo()
this.unsubscribe = this.firestoreRef.onSnapshot(this.getCollection);
}
componentWillUnmount(){
this.unsubscribe();
}
getCollection = (querySnapshot) => {
const userPostsArray = [];
querySnapshot.forEach((res) => {
const {
...
} = res.data();
userPostsArray.push({
...
});
});
this.setState({
userPostsArray,
isLoading: false,
});
this.pullUserInfo()
}
pullUserInfo = async() => {
await Firebase.firestore()
.collection('users')
.doc(this.state.userUID)
.onSnapshot(function(doc){
if (doc.exists) {
console.log("pulling info")
this.setState({
postCount: doc.data().postCount,
followerCount: doc.data().followerCount,
followingCount: doc.data().followingCount,
storage_image_uri: doc.data().profilePic,
bio: doc.data().bio,
isLoading: false
})
} else {
console.log("No such document!");
}
}.bind(this))
}
gotToSettings() {
this.state.navigation.navigate('Settings')
}
//How I show my profile stats, follower, following, bio, profile pic
renderListHeader = () => {
return (
<View style = {styles.container}>
<View style={{ flexDirection: "row", padding: 20 }}>
<Text style = {styles.subheader}> {this.state.user.username} </Text>
</View>
<View style={{ flex: 1, flexDirection: "row", alignItems: 'center',
justifyContent: 'center',}}>
<ProfilePic storage_image_uri = {this.state.storage_image_uri} />
<ProfileStats postCount = {this.state.postCount} followerCount = {this.state.followerCount} followingCount = {this.state.followingCount}/>
</View>
<View style = {styles.lineStyle} />
<ProfileBio bio = {this.state.bio}/>
<TouchableOpacity
onPress={() => this.gotToSettings()}>
<Ionicons name="ios-settings" size={35} color="black" />
</TouchableOpacity>
</View>
)
}
render() {
const { navigation } = this.props;
const renderItem = ({ item }) => (
<CurrentUserPostCell
...
/>
);
if(this.state.isLoading){
return(
<View styles = {styles.container}>
<ActivityIndicator size="large" color="#9E9E9E"/>
</View>
)
}
return (
<View>
<FlatList
data={this.state.userPostsArray}
renderItem={renderItem}
keyExtractor={item => item.key}
ListHeaderComponent={this.renderListHeader}
contentContainerStyle={{ paddingBottom: 50 }}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
/>
</View>
)
}
}
As you can see, when the component mounts, pullUserInfo() is called, as well as the collection of user posts is fetched. There is no issue here getting this information for the first time.
My problem is, when I post something new, or follow someone new, the post count and following count goes up respectively in Firestore. But the new numbers are not reflected in my profile.
It is worth noting, when I create a new post, the new post is displayed in on the user profile. But even this event does not update pullUserInfo()
So, the information is not updating. Furthermore, when I click on the profile tab, I get the following warning:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method
How can I fix this, so that if the user information in Firestore changes, the changes update in the profile?
EDIT: I partly fixed my problem by adding "pull down to refresh" logic to the flatlist, since the flatlist header is the user profile information, and the flatlist data is the list of user posts.
At the end of flatlist in my render():
return (
<View>
<FlatList
data={this.state.userPostsArray}
renderItem={renderItem}
keyExtractor={item => item.key}
ListHeaderComponent={this.renderListHeader}
contentContainerStyle={{ paddingBottom: 50 }}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
onRefresh={this._refresh}
refreshing={this.state.isLoading}
/>
</View>
)
Outside the render:
_refresh = () => {
this.setState({ isLoading: true });
this.pullUserInfo()
};
This doesn't solve the full problem of the profile component only mounting once, therefore not refreshing the user stats automatically. The user has to manually pull down to refresh their profile if they want to see an updated bio, follower/following count, etc.
I am struggling with useFocusEffect or useEffect. I am trying to fetch data when user navigate to the user profile this is working but when user navigate back to the profile i have a duplicate posts! I've been working on this problem for 3 days. Please any ideas.
This is the code:
import React, { useState, useEffect, useRef } from "react";
import {
View,
Text,
FlatList,
Image,
TouchableOpacity,
Animated,
ActivityIndicator,
} from "react-native";
import APIKIT from "../../../services/APIKit";
import styles from "./UserPostStyle";
import moment from "moment";
import { MaterialCommunityIcons, FontAwesome } from "#expo/vector-icons";
import { useNavigation } from "#react-navigation/native";
import { useFocusEffect } from "#react-navigation/native";
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
moment.updateLocale("en", {
relativeTime: {
future: "in %s",
past: "%s ago",
s: "Just now",
ss: "%ss",
m: "1m",
mm: "%dm",
h: "1h",
hh: "%dh",
d: "1d",
dd: "%dd",
M: "1mo",
MM: "%dmo",
y: "1y",
yy: "%dy",
},
});
export function UserPosts({ ...props }) {
const [userPosts, setUserPosts] = useState([]);
const [posts, setPosts] = useState([]); // Array with the posts objects
let postsArray = useRef(posts).current; // Auxiliar array for storing posts before updating the state. Pd: this will not re-render the component as it is a reference
const [page, setPage] = useState(1);
const [lastPage, setLastPage] = useState(0);
const [loadingMore, setLoadingMore] = useState(false);
const navigation = useNavigation();
useFocusEffect(
React.useCallback(() => {
if (props.userId) {
fetchPosts();
}
return () => {};
}, [props.userToken, props.userId, page])
);
// useEffect(() => {
// console.log(" -----------useEffect page", page);
// console.log(" -----------useEffect lastPage", lastPage);
// let mounted = true;
// if (mounted && props.userId) {
// fetchPosts();
// }
// return () => {
// console.log("return useEffect page", page);
// console.log("return useEffect lastPage", lastPage);
// mounted = false;
// };
// }, [props.userToken, props.userId, page]);
const fetchPosts = async () => {
if (props.userToken !== null) {
const config = {
headers: { Authorization: `Bearer ${props.userToken}` },
};
APIKIT.get(`/user/posts/${props.userId}?page=${page}`, config)
.then((response) => {
let posts = [];
const { data } = response;
setLastPage(data.userPosts.last_page);
for (let userPost of data.userPosts.data) {
let post = {};
post["id"] = userPost.id;
post["location_coordinate"] = userPost.location_coordinate;
post["location_icon"] = userPost.location_icon;
post["location_id"] = userPost.location_id;
post["location_title"] = userPost.location_title;
post["location_vicinity"] = userPost.location_vicinity;
post["post_body"] = userPost.post_body;
post["created_at"] = moment(userPost.created_at).fromNow(true);
post["user_id"] = userPost.user_id;
posts.push(post);
}
posts.forEach((newPost) => {
// Add the new fetched post to the head of the posts list
postsArray.unshift(newPost);
});
setPosts([...postsArray]); // Shallow copy of the posts array to force a FlatList re-render
setUserPosts((prevPosts) => [...prevPosts, ...posts]);
setLoadingMore(false);
if (page === lastPage) {
setLoadingMore(false);
return;
}
})
.catch((e) => {
console.log("There is an error eccured while getting the posts ", e);
setLoadingMore(false);
});
}
};
const Item = ({
post_body,
id,
location_coordinate,
location_icon,
location_title,
location_vicinity,
location_id,
created_at,
}) => (
<View style={styles.postContainer}>
<TouchableOpacity
onPress={() => {
navigation.navigate("PostDetailsScreen", {
location_coordinate: JSON.parse(location_coordinate),
userAvatar: props.userAvatar,
username: props.username,
name: props.name,
created_at: created_at,
post_body: post_body,
location_title: location_title,
location_vicinity: location_vicinity,
location_icon: location_icon,
location_id: location_id,
});
}}
>
<View style={styles.postHeader}>
<Image
style={styles.userAvatar}
source={
props.userAvatar
? { uri: props.userAvatar }
: require("../../../../assets/images/default.jpg")
}
/>
<View style={styles.postUserMeta}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Text style={styles.name}>{props.name}</Text>
<Text style={styles.createdAt}>{created_at}</Text>
</View>
<Text style={styles.username}>#{props.username}</Text>
</View>
</View>
<View style={styles.postContent}>
<View>
<Text style={styles.postBody}>{post_body}</Text>
</View>
</View>
</TouchableOpacity>
<TouchableOpacity
style={styles.locationInfoContainer}
onPress={() => {
navigation.navigate("PostPlaceDetailsScreen", {
location_coordinate: JSON.parse(location_coordinate),
userAvatar: props.userAvatar,
username: props.username,
name: props.name,
created_at: created_at,
post_body: post_body,
location_title: location_title,
location_vicinity: location_vicinity,
location_icon: location_icon,
location_id: location_id,
});
}}
>
<View style={styles.locationInfo}>
<Image style={styles.locationIcon} source={{ uri: location_icon }} />
<View style={styles.locationMeta}>
<Text style={styles.locationTitle}>{location_title}</Text>
</View>
</View>
</TouchableOpacity>
<View
style={{
borderWidth: 1,
borderColor: "#F2F2F2",
marginTop: 10,
marginBottom: 10,
}}
/>
<View style={styles.postFooter}>
<TouchableOpacity>
<MaterialCommunityIcons name="comment" size={24} color="#999999" />
</TouchableOpacity>
<TouchableOpacity>
<FontAwesome name="star" size={24} color="#999999" />
</TouchableOpacity>
{/* After fav color #FFBE5B */}
<TouchableOpacity>
<MaterialCommunityIcons
name="dots-horizontal"
size={24}
color="#999999"
/>
</TouchableOpacity>
</View>
</View>
);
const renderItem = ({ item }) => (
<Item
location_coordinate={item.location_coordinate}
post_body={item.post_body}
id={item.id}
location_id={item.location_id}
location_icon={item.location_icon}
location_title={item.location_title}
location_vicinity={item.location_vicinity}
created_at={item.created_at}
/>
);
const emptyPosts = () => {
return (
<View style={styles.noPostsMessageContainer}>
<View style={styles.messageContainer}>
<Image
style={styles.noPostMessageImage}
source={require("../../../../assets/images/Logo.png")}
/>
<Text style={styles.noPostMessageText}>No posts yet!</Text>
</View>
</View>
);
};
const handleLoadingMore = () => {
if (page === lastPage) {
return;
}
setPage(page + 1);
setLoadingMore(true);
};
return (
<View style={styles.userPostContainer}>
<AnimatedFlatList
showsVerticalScrollIndicator={false}
data={userPosts}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{
paddingTop: 250,
paddingBottom: 100,
}}
scrollEventThrottle={16}
onScroll={props.scrolling}
ListEmptyComponent={emptyPosts}
onEndReachedThreshold={0.5}
onMomentumScrollEnd={() => handleLoadingMore()}
ListFooterComponent={
loadingMore && <ActivityIndicator size="large" animating />
}
/>
</View>
);
}
FlatList
return (
<View style={styles.userPostContainer}>
<AnimatedFlatList
showsVerticalScrollIndicator={false}
data={userPosts}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{
paddingTop: 250,
paddingBottom: 100,
}}
scrollEventThrottle={16}
onScroll={props.scrolling}
ListEmptyComponent={emptyPosts}
onEndReachedThreshold={0.5}
onMomentumScrollEnd={() => handleLoadingMore()}
ListFooterComponent={
loadingMore && <ActivityIndicator size="large" animating />
}
/>
</View>
);
handleLoadingMore function
const handleLoadingMore = () => {
if (page === lastPage) {
return;
}
setPage(page + 1);
setLoadingMore(true);
};
I think the problom is when the user navigate back to the profile the state still the same and useFocusEffect fetching the same data again.
setUserPosts((prevPosts) => [...prevPosts, ...posts]);
Thanks for you help.
I have never used this hook from "react-navigation" but in the doc it says:
Sometimes we want to run side-effects when a screen is focused. A side
effect may involve things like adding an event listener, fetching
data, updating document title, etc. While this can be achieved using
focus and blur events, it's not very ergonomic.
So, every time you focus an specific route the code will run.
I am trying to fetch data when user navigate to the user profile this
is working but when user navigate back to the profile i have a
duplicate posts!
What you are doing is wrong because you are not paginating your fetches, I mean, you will need a "pointer" to the last post you fetched... with this you will avoid to fetch the data you already have. Also, the user experience will be pretty faster, as you will have lighter responses from your DB.
Anyways, I suggest you to run this code in the "useEffect" hook from React Native and try to implement a db listener or an inifinite scroll with pagination. This will not work when you focus the screen, but you will be able to fetch data every time the user refresh the FlatList, just like Instagram, Twitter, Netflix...
Take a look on this: https://aboutreact.com/react-native-flatlist-pagination-to-load-more-data-dynamically-infinite-list/
If you really need to fetch data when you focus the specific route, just implement a pagination (save in your component state an index to the last post you fetched).
UPDATE
Sorry, I didn't see that you was doing the pagination in your code. It might be a problem when you update the state of your component...
Try something like that:
const [posts, setPosts] = useState([]); // Array with the posts objects
let postsArray = useRef(posts).current; // Auxiliar array for storing posts before updating the state. Pd: this will not re-render the component as it is a reference
const loadMorePosts = () => {
// ... stuff
// const newPosts = Fetch posts
newPosts.forEach((newPost) => {
// Add the new fetched post to the head of the posts list
postsArray.unshift(newPost);
})
// The last post will be at the top of the list
setPosts([...postsArray]); // Shallow copy of the posts array to force a FlatList re-render
}
// setUserPosts((prevPosts) => [...prevPosts, ...posts]);
i am using react-navigation v4 and i want to change the title and the approach i am using is slow while rendering(title that is set in the stack navigator is rendered first and then new one is rendered),is there better way i can render only the new one
export default NewsList = ({ results, navigation}) => {
return <View >
<FlatList
data={results}
keyExtractor={result => result.id}
renderItem={({ item }) => {
return (
<TouchableOpacity
onPress={() => navigation.navigate( 'news',title: item.title)}>
<NewsCard result={item} />
</TouchableOpacity>)
}}
/>
</View>
};
////////////////////////////////////////////////////////////////////
export default NewsScreen = ({ navigation}) => {
NewsScreen.navigationOptions = {
title: navigation.getParam("title");,
};
}
////////////////////////////////////////////////////////////////////
const NewsStack = createStackNavigator({
NewsList :NewsList,
News :NewsScreen
},{
initialRouteName:'NewsList',
defaultNavigationOptions: {
headerStyle: {
backgroundColor: '#4BB5C3',
},
},
navigationOptions: {
tabBarLabel: 'NewsList',
},
});
export default createAppContainer(NewsStack);
Ciao, try to set title before navigate into NewsList like this:
export default NewsList = ({ results, navigation}) => {
return <View >
<FlatList
data={results}
keyExtractor={result => result.id}
renderItem={({ item }) => {
return (
<TouchableOpacity
onPress={() => {
navigation.setParams({ otherParam: item.title });
navigation.navigate('news');
}
}>
<NewsCard result={item} />
</TouchableOpacity>)
}}
/>
</View>
};
And I think you could remove navigationOptions in NewsScreen.
Please check shared video form understanding my issue
https://drive.google.com/file/d/1GKU07Mv7IjiLnrfps5gWpfMPsMphvRDv/view
I need to Flatlist screen every time empty because this below code every time Flatlist API called and then after change Flatlist data.
App Flow
first screen: shows category
second screen: shows selected category quotes
import { StyleSheet,Button,View, Text,FlatList,Dimensions,TouchableHighlight,Clipboard,ToastAndroid,Share,YellowBox } from 'react-native';
import { createAppContainer,NavigationEvents } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createDrawerNavigator } from 'react-navigation-drawer';
import { createSwitchNavigator } from 'react-navigation-switch-transitioner'
import Icon from 'react-native-vector-icons/Ionicons';
import Style from '../constants/Style';
import Spinner from 'react-native-loading-spinner-overlay';
export default class QuoteActivity extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam('Title', 'Default Title'),
headerStyle: {
backgroundColor: navigation.getParam('BackgroundColor', '#5F4B8BFF'),
},
headerTintColor: navigation.getParam('HeaderTintColor', '#fff'),
headerLeft: (
<Icon
style={{ paddingLeft: 15,paddingTop: 5,color:'#FFFFFF' }}
onPress={() => navigation.goBack(null)}
name="ios-arrow-back"
size={40}
/>
)
};
};
constructor(props) {
super(props);
YellowBox.ignoreWarnings([
'Warning: componentWillMount is deprecated',
'Warning: componentWillReceiveProps is deprecated',
]);
}
state = {
spinner: false,isLoading: false,
dataSource:[]
}
// ON FOCUS CALL API
componentDidMount() {
this._doApiCall();
}
// API CALL
_doApiCall = () => {
this.setState({
spinner: true,isLoading:true
});
const { navigation } = this.props;
let CategoryId = navigation.getParam('CategoryId');
fetch('API_URL', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
}
}).then((response) => response.json())
.then((responseJson) => {
this.setState({
// SAMPLE JSON
// {
// "data": [
// {
// "Id": "462",
// "CategoryId": "5",
// "Title": "Being 'Single' is My Attitude!"
// },
// {
// "Id": "464",
// "CategoryId": "5",
// "Title": "I never dreamed about success. I worked for it."
// }
// ],
// "success": "1",
// "message": "2 Quotes found.",
// "service_time": "0.030284881591797 seconds"
// }
// SAMPLE JSON END
spinner:false,isLoading:false,
dataSource:responseJson.data
})
})
.catch((error) => {
console.error(error);
});
};
// Copy to clipboard
writeToClipboard = async (text) => {
await Clipboard.setString(text);
ToastAndroid.show('Quote copied!', ToastAndroid.SHORT);
};
// Share quotes
shareQuotes = async (text) => {
const result = await Share.share({
message:text.toString()+'\n\n Download app from play store',
});
};
_keyExtractor = (item) => item.id;
// RENDER DATA ITEMS
_renderItem = ({item}) => {
return (
<View style={Style.CategoryTitleList}>
<Text style={Style.CategoryTitle}>{item.Title}</Text>
<View style={[{ flexDirection:'row',justifyContent: 'center',alignItems:'center',textAlign:'center',alignSelf:"center"}]}>
<Icon name="ios-copy" onPress={() => this.writeToClipboard(item.Title)} title="Copy" size={25} color="#5F4B8BFF" style={[{margin:5,alignItems:'flex-end',paddingTop:3,paddingRight:20,paddingLeft:20 }]} />
<Icon name="ios-share" onPress={() => this.shareQuotes(item.Title)} size={25} color="#5F4B8BFF" style={[{margin:5,alignItems:'flex-end',paddingTop:3,paddingRight:20,paddingLeft:20 }]} />
</View>
</View>
)
}
render() {
// RENDER DATA ITEMS INSIDE RENDER FNS
renderItem = ({ item, index }) => {
return (
<View style={Style.CategoryTitleList}>
<Text style={Style.CategoryTitle}>{item.Title}</Text>
<View style={[{ flexDirection:'row',justifyContent: 'center',alignItems:'center',textAlign:'center',alignSelf:"center"}]}>
<Icon name="ios-copy" onPress={() => this.writeToClipboard(item.Title)} title="Copy" size={25} color="#5F4B8BFF" style={[{margin:5,alignItems:'flex-end',paddingTop:3,paddingRight:20,paddingLeft:20 }]} />
<Icon name="ios-share" onPress={() => this.shareQuotes(item.Title)} size={25} color="#5F4B8BFF" style={[{margin:5,alignItems:'flex-end',paddingTop:3,paddingRight:20,paddingLeft:20 }]} />
</View>
</View>
);
};
return (
<View style={Style.QuotesListView}>
<NavigationEvents
onDidFocus={this._doApiCall}
onWillFocus={() => this.setState({spinner: true})}
/>
<FlatList
data={this.state.dataSource}
renderItem={renderItem} // USED INSIDE RENDER FNS
refreshing={this.state.isLoading}
onRefresh={this._doApiCall}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={1000}
initialNumToRender={1}
removeClippedSubviews={false}
keyExtractor={this._keyExtractor}
/>
</View>
);
}
}```
Before doing the API Call you can clean the DataSource array that you have.
// API CALL
_doApiCall = () => {
this.setState({
spinner: true,isLoading:true,
dataSource: []
});
...
You can handle that in 2 different ways.
whenever you call _doApiCall function, change initial value of dataSource as an empty array.
_doApiCall = () => {
this.setState({
spinner: true,
isLoading: true,
dataSource: []
});
}
Using ternary operator, diaplay spinner when data loading & if not diaplay faltlist
{
this.state.isLoading ?
<Show your Spinner /> :
<FlatList
data={this.state.dataSource}
renderItem={renderItem}
refreshing={this.state.isLoading}
onRefresh={this._doApiCall}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={1000}
initialNumToRender={1}
removeClippedSubviews={false}
keyExtractor={this._keyExtractor}
/>
}
Feel free for doubt. hope this helps you.