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 im listening to changes of, When a user clicks one button the sate of all the buttons changes, when 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 cicked?
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>
))}
</>
)
CodePudding user response:
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 />}
CodePudding user response:
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>
)})}
</>
)