Firebase react native flatlist keyExtractor - firebase

Im displaying some firebase data in a flatlist and im having trouble with the keyExtractor, I keep having the error:
undefined is not an object (evaluating "item.id")
I have added an id field to all my data in firebase and made sure they were a string but it's still not recognizing it as an id.
function Squad() {
const [gk, setGk] = useState([]);
useEffect(() => {
db.collection('squad').orderBy('position').get().then(snapshot => {
const gkData = snapshot.map(doc => {
const playerObject = doc.data();
return { name: playerObject.name, number: playerObject.number, id: playerObject.id };
});
setGk(gkData);
console.log(setGk);
});
}, []);
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{number} - {name}</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
)
return(
<View>
<View style={globalStyles.bar}>
<Text style={globalStyles.barText}>goalkeeper</Text>
</View>
<FlatList
data={setGk}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
)
}

The data you are passing into the Flatlist is the setter function! You want to pass in ‘gk’ not ‘setGk’

Related

React Native Firebase Firestore data not fetching properly

I want to fetch data only which data I call in my app, it's working very well but why I am getting all those unnecessary parts, how to remove that, thanks for your support. please check the database image and display output image how it's displaying. I don't want those parts where I marked red lines, because I have not call them in my code. Is it FlatList issue or Firebase issue?
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, FlatList, View, Text } from 'react-native';
import {firebase} from '../config';
const Testing = ({ navigation }) =>{
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [users, setUsers] = useState([]);
useEffect(() => {
const subscriber = firebase.firestore()
.collection('testing')
.onSnapshot(querySnapshot => {
const users = [];
querySnapshot.forEach(documentSnapshot => {
users.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setUsers(users);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
data={users}
renderItem={({ item }) => (
<View style={{ height: 50, flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>ID: {item.One}</Text>
<Text>Name: {item.five}</Text>
</View>
)}
/>
);}
export default Testing;
It looks like you've incomplete or "optional" data in your backend. If you don't want to render empty fields you can conditionally render them.
For the users data that is missing both properties you can filter the data prop.
Example:
<FlatList
data={users.filter(({ One, five }) => One || five)}
renderItem={({ item }) => (
<View style={{ .... }}>
{item.One && <Text>ID: {item.One}</Text>}
{item.five && <Text>Name: {item.five}</Text>}
</View>
)}
/>

Having issues rendering arrays of URLs in FlatLists in React Native

I am using Firebase v9 and React Native. I have one function and flatlist that is in charge of pulling and rendering group member data, and another function and flatlist that is in charge of rendering group posts. There are several issues going on here, but I would appreciate guidance on any part. I've been looking into this for a week.
The problem that I'm having is that the second flatlist is not rendering any images. I know that the styling is right because I've tried it with test data and I know that the URLs are being grabbed because I have can print a report of them. I've tried changing the way (item) is destructured, changing the renderItem functions, changing the parameters, and many more strategies, but nothing has worked.
I think it may have to do with the way that I'm storing my URLs in state and the temporary arrays I'm using. I'm also having the problem that the data doesn't load for the first flatlist when I first go to the screen, although it prints the right URLs. When I jump out and then go back to screen, the first flatlist renders the group members data (although the images are out of order sometimes). The data just prints out as Array [].
const MyGroupScreen = ({route, navigation}) => {
const { groupName, groupID } = route.params;
const [memNameLogs, setMemNameLogs] = useState([]);
const [memIDLogs, setMemIDLogs] = useState([]);
const [memImagesLogs, setMemImagesLogs] = useState([]);
const [memberCount, setMemberCount] = useState(0);
const [postIDLogs, setPostIDLogs] = useState([]);
const [postImagesLogs, setPostImagesLogs] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const getGroupInfo = async () => {
let memberIDs = [];
let memberNames = [];
let memberImages = [];
let userGroupsRef = collection(db, "groups", groupID, "members");
onSnapshot(userGroupsRef, (querySnapshot) => {
querySnapshot.forEach((document) => {
memberIDs.push(document.id)
onSnapshot(doc(db, "users", document.id), (snapshot) => {
const one = snapshot.data();
const two = one.firstName;
const three = one.lastName;
const four = two + ' ' + three;
memberNames.push(four);
});
const pathReference = ref(storage, 'userProfileImage/' + document.id);
getDownloadURL(pathReference)
.then((url) => {
memberImages.push(url);
})
});
setMemberCount(memberIDs.length);
setMemIDLogs(memberIDs);
setMemNameLogs(memberNames);
setMemImagesLogs(memberImages);
})
};
const getGroupPosts = async () => {
let postIDs = [];
let postImages = [];
let userPostsRef = collection(db, "groups", groupID, "posts");
onSnapshot(userPostsRef, (querySnapshot) => {
querySnapshot.forEach((document) => {
postIDs.push(document.id)
const postPathReference = ref(storage, 'posts/' + document.id + '/' + document.id + 0);
getDownloadURL(postPathReference)
.then((url) => {
postImages.push(url);
})
})
setPostIDLogs(postIDs);
setPostImagesLogs(postImages);
})
}
const [isFetching, setIsFetching] = useState(false);
const fetchData = () => {
getGroupPosts();
setIsFetching(false);
};
const onRefresh = () => {
setIsFetching(true);
fetchData();
};
useEffect(() => {
getGroupInfo();
getGroupPosts();
setIsLoading(false);
}, [])
const renderMembers = ({ item, index }) => (
<TouchableOpacity style={styles.memberInfo} onPress={()=>navigation.navigate('otherprofile', {userID: memIDLogs[index]})}>
<View style={{paddingBottom: 12}}>
<Image source={{uri: item}} style={styles.memberPicture}/>
</View>
<Text style={styles.memberName}>{memNameLogs[index]}</Text>
</TouchableOpacity>
);
const renderContent = ({ item, index }) => (
<TouchableOpacity onPress={()=>navigation.navigate('viewcontent')}>
<Image source={{uri: item}} style={styles.image}/>
</TouchableOpacity>
);
return (
<View style={styles.container}>
<View style={{alignItems:'center', width: width}}>
<View style={styles.groupBar}>
<View style={{flexDirection: 'row', flex: 1, justifyContent:'space-between', alignItems:'center'}}>
<View style={{flexDirection: 'row'}}>
<Text style={styles.groupName}>{groupName}</Text>
<Text style={styles.groupSize}>({memberCount})</Text>
</View>
</View>
<TouchableOpacity>
<Entypo name="menu" size={32} color={'#CACADB'}/>
</TouchableOpacity>
</View>
</View>
<FlatList
data={memImagesLogs}
renderItem={(item, index) => renderMembers(item, index)}
keyExtractor={item => item.id}
horizontal
showsHorizontalScrollIndicator='false'
style={{flex: 1}}
contentContainerStyle={{paddingTop: 12, paddingLeft: 24, backgroundColor: Colors.light.base, width: width}}
/>
<View style={{backgroundColor: Colors.light.base, height: 76, width: '100%', flexDirection:'row', alignItems:'center'}}>
<View style={{marginVertical: 36, marginLeft: 36, width: '15%', borderRadius: 15, height: 4, backgroundColor: '#CACADB'}}/>
</View>
<FlatList
data={postImagesLogs}
renderItem={(item, index) => renderContent(item, index)}
keyExtractor={item => item.id}
showsHorizontalScrollIndicator='false'
contentContainerStyle={{ paddingTop: 0, alignItems: 'center', justifyContent: 'flex-start'}}
columnWrapperStyle={{backgroundColor:Colors.light.base, width: width, justifyContent:'center'}}
style={{width: width}}
numColumns={4}
onRefresh={onRefresh}
refreshing={isFetching}
//extraData={postImages}
ListFooterComponent={()=>
<View style={{backgroundColor: Colors.light.base, height: '100%', width: width}}/>}
/>
<TouchableOpacity style={styles.addGroupButton} onPress={()=> navigation.navigate('newgroup', {screen: 'invitegroup', params: {groupID: groupID}})}>
<FontAwesome5 name='user-plus' size={20} color={Colors.light.base}/>
</TouchableOpacity>
</View>
);
}
export default MyGroupScreen;
In your useEffect(), you are calling getGroupPosts() which is an async function without an await or a .then()/.catch(). This is resulting in a behavior that will work sometimes work and not work. My suggestion is to call all async functions in your useEffect() like this:
getGroupInfo().catch(e => console.log(e));
getGroupPosts().catch(e => console.log(e));

Update Firebase Document in Functional Component with Text Inputs

This is my screen that the user can visit to edit/update their details. My problem is I can't seem to get the values from the Inputs to be used as the new data when the save button is pressed. It complains about being a function. How to convert my state to a string for the function to update the firebase document?
const UserProfileEditScreen = ({ navigation }) => {
const user = auth.currentUser;
const [userData, setUserData] = useState({});
const getUser = async() => {
const currentUser = await firestore
.ref('users')
.doc(user.uid)
.get()
.then((documentSnapshot) => {
if( documentSnapshot.exists ) {
setUserData(documentSnapshot.data());
}
})
}
const [displayName, setNewDisplayName] = useState('');
const [name, setName] = useState('');
const [surname, setSurname] = useState('');
const [birthdate, setBirthdate] = useState('');
const handleUpdate = () => {
firestore.collection('users').doc(user.uid).set({
displayName: setNewDisplayName,
name: setName,
surname: setSurname,
}).then(() => {
console.log('User Updated!');
Alert.alert(
'Profile Updated!',
'Your profile has been updated successfully.'
);
});
navigation.navigate('Profile');
}
useEffect(() => {
getUser();
}, [])
return (
<DismissKeyboard>
<View style={styles.container}>
<View style={styles.insideContainer}>
<Text style={[styles.headTextH2, styles.whiteText]}>Edit Account</Text>
<View style={{marginBottom:45}}></View>
<Text style={[styles.headTextH3, styles.whiteText]}>Display Name</Text>
<TextInput
style={styles.inputStyles}
placeholder={userData.displayName}
placeholderTextColor="#000"
value={displayName}
onChangeText={setNewDisplayName}
/>
<Text style={[styles.headTextH3, styles.whiteText]}>Name</Text>
<TextInput
style={styles.inputStyles}
placeholder={userData.name}
placeholderTextColor="#000"
value={name}
onChangeText={setName}
/>
<Text style={[styles.headTextH3, styles.whiteText]}>Surname</Text>
<TextInput
style={styles.inputStyles}
placeholder={userData.surname}
placeholderTextColor="#000"
value={surname}
onChangeText={setSurname}
/>
<OGButton title="Save Changes" onPress={()=> handleUpdate()} />
</View>
</View>
</DismissKeyboard>
)
}
Error Message:
setName, setSurname are functions that are used to set the value of name and surname respectively and you are passing them in Firestore's set method.
const [displayName, setNewDisplayName] = useState('');
const [name, setName] = useState('');
const [surname, setSurname] = useState('');
const [birthdate, setBirthdate] = useState('');
Instead you should pass the values like this:
firestore.collection('users').doc(user.uid).set({
displayName, name, surname
})

How to pull key from firebase realtime database and pass to edit screen?

I am pretty new to react native/firebase, and am trying to figure out how to grab the unique key from an entry in the database (they are randomly generated when using Push), so I can update the database entry on my edit screen. Any help or direction on how I could accomplish this is very much appreciated.
Here is my main feed screen where all items from the database are grabbed and displayed:
let ref = db.ref('dogs');
export default class Main extends React.Component {
_isMounted = false;
constructor() {
super();
this.state = {
currentUser: null,
errorMessage: null,
items: [],
key: '',
};
}
componentDidMount() {
this._isMounted = true;
const {currentUser} = firebaseAuth;
this.setState({currentUser});
ref.on('value', snapshot => {
if (this._isMounted) {
let data = snapshot.val();
let items = Object.values(data);
this.setState({items});
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const {currentUser} = this.state;
return (
<View style={styles.container}>
<View>
<View style={styles.container}>
{this.state.items.length > 0 ? (
<ItemComponent
style={styles.listDog}
items={this.state.items}
navigation={this.props.navigation}
/>
) : (
<Text>No dogs</Text>
)}
</View>
<View style={styles.bottomContainer}>
<TouchableOpacity
style={styles.addBtn}
onPress={() => this.props.navigation.navigate('dogCreation')}>
<View>
<FontAwesomeIcon style={styles.penIcon} icon={faBone} />
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
here is my item component:
export default class ItemComponent extends Component {
static propTypes = {
items: PropTypes.array.isRequired,
};
render() {
return (
<View style={styles.itemsList}>
{this.props.items.map((item, dog) => {
return (
<TouchableOpacity
key={dog}
onPress={() =>
this.props.navigation.navigate('Profile', {
name: item.name,
image_uri: item.image_uri,
parent: item.parent,
parentEmail: item.parentEmail,
parentTwo: item.parentTwo,
parentTwoEmail: item.parentTwoEmail,
})
}>
<View style={styles.dogCard}>
<Image source={{uri: item.image_uri}} style={styles.dogImage} />
<Text style={styles.itemtext}>{item.name} </Text>
<FontAwesomeIcon style={styles.chevron} icon={faChevronRight} />
</View>
</TouchableOpacity>
);
})}
</View>
);
}
}
here is my profile page:
export default class Profile extends React.Component {
render() {
const {navigation} = this.props;
const dogName = navigation.getParam('name', '');
const image_uri = navigation.getParam('image_uri', '');
const parent = navigation.getParam('parent', '');
const parentEmail = navigation.getParam('parentEmail', '');
const parentTwo = navigation.getParam('parentTwo', '');
const parentTwoEmail = navigation.getParam('parentTwoEmail', '');
return (
<View style={styles.container}>
<Image style={styles.dogImage} source={{uri: image_uri}} />
<View style={styles.contentBlock}>
<Text style={styles.header}>Name</Text>
<Text style={styles.textStyle}>{dogName}</Text>
</View>
<View style={styles.contentBlock}>
<Text style={styles.header}>Pet Parent 1 Info</Text>
<Text style={styles.subHeader}>Name</Text>
<Text style={styles.textStyle}>{parent}</Text>
<Text style={styles.subHeader}>Name</Text>
<Text style={styles.textStyle}>{parentEmail}</Text>
</View>
<View style={styles.contentBlock}>
<Text style={styles.header}>Pet Parent 2 Info</Text>
<Text style={styles.subHeader}>Name</Text>
<Text style={styles.textStyle}>{parentTwo}</Text>
<Text style={styles.subHeader}>Name</Text>
<Text style={styles.textStyle}>{parentTwoEmail}</Text>
</View>
<TouchableOpacity
style={styles.addBtn}
onPress={() =>
this.props.navigation.navigate('editProfile', {
name: dogName,
image_uri: image_uri,
parent: parent,
parentEmail: parentEmail,
parentTwo: parentTwo,
parentTwoEmail: parentTwoEmail,
})
}>
<Text>EDIT</Text>
</TouchableOpacity>
</View>
);
}
}
and here is my edit profile page update function:
let ref = db.ref('dogs');
let addItem = (
dog,
parent,
parentEmail,
parentTwo,
parentTwoEmail,
image_uri,
) => {
db.ref('/dogs/')
.update({
name: dog,
parent: parent,
parentEmail: parentEmail,
parentTwo: parentTwo,
parentTwoEmail: parentTwoEmail,
image_uri: image_uri,
});
};
just use on return snapshot return snapshot.key.
ref.on('value', snapshot => {
if (this._isMounted) {
let id = snapshot.key;
let data = snapshot.val();
let items = Object.values(data);
this.setState({items});
}
});
you can see more details here: https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#key
**some important points, if you are using the firebase library directly in react native, I advise you to use the version created specifically for React Native, which corrects several problems of timers within the OS.
you can see here: https://rnfirebase.io/
**other importante detail, your firebase return function is a listener, and using this._isMounted is anti patern. you can use subscriber to stop the listener.
so...
let subscriber;
...
componentDidMount() {
const {currentUser} = firebaseAuth;
this.setState({currentUser});
subscriber = ref.on('value', snapshot => {
let data = snapshot.val();
let items = Object.values(data);
this.setState({items});
});
}
componentWillUnmount() {
// Stop listening for updates when no longer required
this.subscriber();
}

React-Native Passing users email and password to submit button

import FirebaseAPI from '../MyModules/FirebaseAPI';
function submit() {
import FirebaseAPI from '../MyModules/FirebaseAPI';
export default function LinksScreen() {
const [email, onChangeText] = React.useState('Enter Email');
const [password, onChangeText2] = React.useState('Enter Password');
const submit = () => {
FirebaseAPI.createUser(email, password)
}
return (
<KeyboardAvoidingView style={styles.wrapper} behavior="padding">
<View style={styles.scrollViewWrapper}>
<ScrollView style={styles.scrollView}>
<Text style={styles.loginHeader}>Creat an Account </Text>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText(text)}
value={email}
/>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText2(text)}
value={password}
/>
<TouchableOpacity
style={{marginTop: '5%'}}
onPress= {submit()}>
<View>
<Text>Submit</Text>
</View>
//code from FirebaseAPI.js
import * as firebase from 'firebase'
export const createUser = (email, password) => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.catch((error) => console.log('createUser error: ', error));
}
//etc
my error is
TypeError: undefined is not an object (evaluating '_FirebaseAPI.default.createUser')
I assume its a scoping issue but unsure on how to fix it. Still new at react. Any help would be awesome!
The email and password are not scope of the submit function. You either need to move the submit function inside the component function or pass the values to the function
export default function LinksScreen() {
const [email, onChangeText] = React.useState('Enter Email');
const [password, onChangeText2] = React.useState('Enter Password');
const submit = () => {
FirebaseAPI.createUser(email, password)
}
return (
....
)
OR
<TouchableOpacity
style={{marginTop: '5%'}}
onPress= {() => submit(email, password)}>
<View>
<Text>Submit</Text>
</View>
</TouchableOpacity>
Also where you are importing the FirebaseAPI import as
import * as FirebaseAPI from '../MyModules/FirebaseAPI';

Resources