how to use useState on a specific onPress Touchable Opacity - javascript

I have a screen that outputs all the groups a user isnt a member of. Each group has a join button that when clicked adds the user to the members subcollection under groups collection in firestore.
The state of the button is supposed to change from join to Joined when a user clicks the join button and then change from joined to join when the user clicks it again.
My problem is that since all the buttons have the same joinedButton state which I am listening to, changes of when a user clicks one button the state of all the buttons changes, while only the clicked one should change.
The buttons are outputted using an array map of the promise received from a firestore query.
Any ideas how I can change the state of only the button that has been clicked?
import { StyleSheet, Text, View, Image } from 'react-native'
import React, { useState, useEffect, useContext } from 'react'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { db } from '../../firebase'
import { AuthContext } from '../../navigation/AuthProvider'
const DiscoverGroupList = ({ navigation }) => {
const [joinedButton, setJoinedButton] = useState(false);
const fetchGroups = async () =>{
//code to
}
const { user } = useContext(AuthContext);
const joinGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.set({
userId: user.uid,
isMember: true,
})
setJoinedButton(true)
} catch (error) {
console.log(error)
}
}
const leaveGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.delete()
setJoinedButton(false)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
fetchGroups()
}, [joinedButton])
return (
<>
{groupsYouManage.map((item) => (
<View key={item.groupId} style={styles.groupWrapper}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image source={{ uri: item.groupImage }} style={styles.groupImage} />
<View>
<Text style={styles.groupListTitle}>{item.groupName}</Text>
<Text style={styles.groupMembers}>{item.groupMembers}</Text>
</View>
</View>
{!joinedButton ? (
<TouchableOpacity style={styles.join} onPress={() => joinGroup(item.groupId)}>
<Text style={styles.joinText}>Join</Text>
</TouchableOpacity>
) : (
<TouchableOpacity style={styles.join} onPress={() => leaveGroup(item.groupId)}>
<Text style={styles.joinText}>Joined</Text>
</TouchableOpacity>
)
}
</View>
))}
</>
)

It looks like you're setting a value in the database of members collection with the ID and isMember: true. Is it possible that when you map over the data instead of rendering the button based off of the useState joinedButton, could you set the button to be rendered based on the isMember bool?
{item.isMember ? <leaveGroup button /> : <joinGroupButton />}

I think creating separate state for every item present in the array can help.
import { StyleSheet, Text, View, Image } from 'react-native'
import React, { useState, useEffect, useContext } from 'react'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { db } from '../../firebase'
import { AuthContext } from '../../navigation/AuthProvider'
const DiscoverGroupList = ({ navigation }) => {
const fetchGroups = async () =>{
//code to
}
const { user } = useContext(AuthContext);
const joinGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.set({
userId: user.uid,
isMember: true,
})
} catch (error) {
console.log(error)
}
}
const leaveGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.delete()
} catch (error) {
console.log(error)
}
}
useEffect(() => {
fetchGroups()
}, [joinedButton])
return (
<>
{groupsYouManage.map((item) => {
const [joinedButton, setJoinedButton] = useState(false);
const handleJoin = () => {
joinGroup(item.groupId)
setJoinedButton(true);
}
const handleLeave = () => {
leaveGroup(item.groupId)
setJoinedButton(false);
}
return (
<View key={item.groupId} style={styles.groupWrapper}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image source={{ uri: item.groupImage }} style={styles.groupImage} />
<View>
<Text style={styles.groupListTitle}>{item.groupName}</Text>
<Text style={styles.groupMembers}>{item.groupMembers}</Text>
</View>
</View>
{!joinedButton ? (
<TouchableOpacity style={styles.join} onPress={handleJoin }>
<Text style={styles.joinText}>Join</Text>
</TouchableOpacity>
) : (
<TouchableOpacity style={styles.join} onPress={handleLeave}>
<Text style={styles.joinText}>Joined</Text>
</TouchableOpacity>
)
}
</View>
)})}
</>
)

Related

How to add button in React Native Flatlist?

I am trying to create a very simple question-and-answer app.
If I click on the show answer button then the answer should show only where I click, but now all answers are showing when I click on the button. I fetch question answers from Firestore. What is the problem Please check my Firestore data and Flatlist code. please check the images, Thank you in advance for your support
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, FlatList, View, Text, Pressable, Button, StyleSheet } from 'react-native';
import {firebase} from '../config';
const Testing = ({ navigation }) =>{
const [users, setUsers] = useState([]);
const todoRef = firebase.firestore().collection('dd11');
const [showValue, setShowValue] = useState(false);
useEffect(() => {
todoRef.onSnapshot(
querySnapshot => {
const users = []
querySnapshot.forEach((doc) => {
const { QuestionOne, ans, optionOne, optionTwo, optionThree, optionFour
} = doc.data()
users.push({
id: doc.id,
QuestionOne, ans, optionOne, optionTwo, optionThree, optionFour
})
})
setUsers(users)
}
)
}, [])
return (
<View style={{ flex:1,}}>
<FlatList
data={users}
numColumns={1}
renderItem={({item}) => (
<Pressable >
<View>
<View style={{paddingLeft: 10, paddingRight: 10,}}>
{item.QuestionOne && <Text>{item.QuestionOne}</Text>}
{item.optionOne && <Text>{item.optionOne}</Text>}
{item.optionTwo && <Text>{item.optionTwo}</Text>}
{item.optionThree && <Text>{item.optionThree}</Text>}
{item.optionFour && <Text>{item.optionFour}</Text>}
{showValue? item.ans &&<Text style={{color: 'green'}} >{item.ans}</Text> : null}
<Button title="Show Answer" onPress={() => setShowValue(!showValue)} />
</View>
</View>
</Pressable>
)} />
</View>
);}
export default Testing;
you can try this, by maintaining an array of indexes, only those will be shown :)
NOte: also letmeknow if you want that func that if answer is shown and you want to hide it on press again.
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, FlatList, View, Text, Pressable, Button, StyleSheet } from 'react-native';
import {firebase} from '../config';
const Testing = ({ navigation }) =>{
const [users, setUsers] = useState([]);
const todoRef = firebase.firestore().collection('dd11');
const [showValue, setShowValue] = useState(false);
const [answerIndexs,setAnsIndex] = useState([])
useEffect(() => {
todoRef.onSnapshot(
querySnapshot => {
const users = []
querySnapshot.forEach((doc) => {
const { QuestionOne, ans, optionOne, optionTwo, optionThree, optionFour
} = doc.data()
users.push({
id: doc.id,
QuestionOne, ans, optionOne, optionTwo, optionThree, optionFour
})
})
setUsers(users)
}
)
}, [])
const onPressOfShowAnswer = (index) => {
const existingIndexs = [...answerIndexs]
if(!existingIndexs.includes(index)){
existingIndexs.push(index)
}else{
existingIndexs.splice(index,1)
}
setAnsIndex(existingIndexs)
}
return (
<View style={{ flex:1,}}>
<FlatList
data={users}
numColumns={1}
renderItem={({item,index}) => (
<Pressable >
<View>
<View style={{paddingLeft: 10, paddingRight: 10,}}>
{item.Q1 && <Text>{item.QuestionOne}</Text>}
{item.optionOne && <Text>{item.optionOne}</Text>}
{item.optionTwo && <Text>{item.optionTwo}</Text>}
{item.optionThree && <Text>{item.optionThree}</Text>}
{item.optionFour && <Text>{item.optionFour}</Text>}
{ answerIndexs.includes(index) ? item.ans &&<Text style={{color: 'green'}} >{item.ans}</Text> : null}
<Button title="Show Answer" onPress={() => onPressOfShowAnswer(index)} />
</View>
</View>
</Pressable>
)} />
</View>
);}
export default Testing;

"Error: Invalid hook call. Hooks can only be called inside of the body of a function component.." in react native expo

TestScreen.js
export default function TestScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<AlcoResult/>
</View>
);
}
2.AlcoResult.js
import { StyleSheet, Text, View,TouchableOpacity } from 'react-native'
import React from 'react'
import AlcoTestButton from './AlcoTestButton'
const AlcoResult = () => {
return (
<View style={styles.container}>
<TouchableOpacity
onPress={()=>AlcoTestButton()}
style={styles.button}>
<Text style={{ color: "white"}}>pull data</Text>
</TouchableOpacity>
</View>
)
}
AlcoTestButton.js
import { StyleSheet, Text, View, ActivityIndicator, FlatList } from 'react-native'
import React, { useEffect, useState, Component } from 'react'
import { SafeAreaView } from 'react-navigation';
const url = "url";
const AlcoTestButton = () => {
const [isLoading,setLoading] = useState(true);
const [alcohol, setAlcohol] = useState([]);
const [temperature, setTemperature] = useState([]);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((json) => {
setAlcohol(json.alcohol);
setTemperature(json.temperature);
})
.catch((error) =>alert(error))
.finally(setLoading(false));
})
return (
<SafeAreaView style={styles.container}>
{isLoading ? (<ActivityIndicator />) :(
<View>
<Text>Alcohol = {alcohol}</Text>
<Text>Temperature = {temperature}</Text>
</View>
)}
</SafeAreaView>
)
}
export default AlcoTestButton
So here is my code... I tried different solutions on several websites but still got the same error.
I'm new to react native, If possible could anyone point out what are my errors if any in the structure of the codes?
Thank you.
The problem is that you are calling a component and returning UI elements instead of a function when pressing the button, so i'd suggest something like this
(I'm unsure of the structure of your data, but this should put you on the right track at least)
const AlcoResult = () => {
const [isLoading,setLoading] = useState(false);
const [alcohol, setAlcohol] = useState();
const [temperature, setTemperature] = useState();
const fetchData= async ()=>{
setLoading(true)
fetch(url)
.then((response) => response.json())
.then((json) => {
setAlcohol(json.alcohol);
setTemperature(json.temperature);
})
.catch((error) =>{alert(error)})
.finally(()=>{setLoading(false)});
}
return (
<View style={styles.container}>
<TouchableOpacity
onPress={()=>fetchData()}
style={styles.button}>
<Text style={{ color: "white"}}>pull data</Text>
</TouchableOpacity>
{isLoading && (<ActivityIndicator />)}
{!isLoading && !!temperature && !!alcohol &&
<View>
<Text>Alcohol = {alcohol}</Text>
<Text>Temperature = {temperature}</Text>
</View>
}
</View>
)
}

Not sure how to map variable to show in app

Here's the function-
const setLoading = (value) => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase().startsWith(value.toLowerCase());
});
setFiltered(messages);
console.log(filtered);
};
I want to display the variable 'messages' separately in my app, how would I do that? 'messages' variable needs to be displayed within the default react native 'Text' component. I have written down 'messages' below within Text component but currently it's not displaying anything (since it is within function) -
import React, { useState, useEffect, useReducer } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator, Keyboard} from 'react-native';
import { Searchbar } from 'react-native-paper';
import { theme } from '../theme';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { apiStateReducer } from '../reducers/ApiStateReducer';
import CognitensorEndpoints from '../services/network/CognitensorEndpoints';
import DefaultView from '../components/default/DefaultView';
import DashboardListCard from '../components/DashboardListCard';
const AppHeader = ({
scene,
previous,
navigation,
searchIconVisible = false,
}) => {
const [dashboards, dispatchDashboards] = useReducer(apiStateReducer, {
data: [],
isLoading: true,
isError: false,
});
const [filtered, setFiltered] = useState([]);
const setLoading = (value) => {
const messages = dashboards.data.message.filter((item) => {
const title = item.dashboardTitle || item.dashboardName;
return title.toLowerCase().startsWith(value.toLowerCase());
});
setFiltered(messages);
console.log(filtered);
};
const dropShadowStyle = styles.dropShadow;
const toggleSearchVisibility = () => {
navigation.navigate('Search');
};
useEffect(() => {
CognitensorEndpoints.getDashboardList({
dispatchReducer: dispatchDashboards,
});
}, []);
return (
<>
<View style={styles.header}>
<View style={styles.headerLeftIcon}>
<TouchableOpacity onPress={navigation.pop}>
{previous ? (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.visible}
/>
) : (
<MaterialIcons
name="chevron-left"
size={24}
style={styles.invisible}
/>
)}
</TouchableOpacity>
</View>
<Text style={styles.headerText}>
{messages}
</Text>
<View style={styles.headerRightIconContainer}>
{searchIconVisible ? (
<TouchableOpacity
style={[styles.headerRightIcon, dropShadowStyle]}
onPress={toggleSearchVisibility}>
<MaterialIcons name="search" size={24} style={styles.visible} />
</TouchableOpacity>
) : (
<View style={styles.invisible} />
)}
</View>
</View>
</>
);
};
If your messages variable is an array you can map it
{messages.map((message, key)=>(
<Text style={styles.headerText}>
{message.dashboardName}
</Text>
))}
Since your messages variable is stored in 'filtered' state, you can map it by doing this:
{filtered.map((item, index) => <Text key={index}>{item.dashboardName}<Text>)}

How to use realtime database in react native when loged using firestore?

So I am here to ask if there is a method to use firebase with real-time database in 1 apps.
My login already worked using Firestore.
This is my code that is storing 1 text to my Firestore data, Its worked too.
I want to change it to real-time database whenever there a new data inserted, but still have the id user logged to my react-native app
The point is I want to use Realtime Database instead of Firestore.
Login as Firestore, Save data as Realtime Database
import React, { useEffect, useState } from 'react'
import { FlatList, Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native'
import styles from './styles';
import { firebase } from '../../firebase/config'
export default function HomeScreen(props) {
const [entityText, setEntityText] = useState('')
const [entities, setEntities] = useState([])
const entityRef = firebase.firestore().collection('entities')
const userID = props.extraData.id
useEffect(() => {
entityRef
.where("authorID", "==", userID)
.orderBy('createdAt', 'desc')
.onSnapshot(
querySnapshot => {
const newEntities = []
querySnapshot.forEach(doc => {
const entity = doc.data()
entity.id = doc.id
newEntities.push(entity)
});
setEntities(newEntities)
},
error => {
console.log(error)
}
)
}, [])
const onAddButtonPress = () => {
if (entityText && entityText.length > 0) {
const timestamp = firebase.firestore.FieldValue.serverTimestamp();
const data = {
text: entityText,
authorID: userID,
createdAt: timestamp,
};
entityRef
.add(data)
.then(_doc => {
setEntityText('')
Keyboard.dismiss()
})
.catch((error) => {
alert(error)
});
}
}
const renderEntity = ({item, index}) => {
return (
<View style={styles.entityContainer}>
<Text style={styles.entityText}>
{index}. {item.text}
</Text>
</View>
)
}
return (
<View style={styles.container}>
<View style={styles.formContainer}>
<TextInput
style={styles.input}
placeholder='Add new entity'
placeholderTextColor="#aaaaaa"
onChangeText={(text) => setEntityText(text)}
value={entityText}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Add</Text>
</TouchableOpacity>
</View>
{ entities && (
<View style={styles.listContainer}>
<FlatList
data={entities}
renderItem={renderEntity}
keyExtractor={(item) => item.id}
removeClippedSubviews={true}
/>
</View>
)}
</View>
)
}
Can someone help me and tell me how to do that?
thanks

react native and useFocusEffect duplicate data when navigating back

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]);

Categories

Resources