Update Firebase Document in Functional Component with Text Inputs - firebase

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
})

Related

How do I pass a specific object from a FlatList to a function so I could delete it from Firestore?

I have a watchlist of bookings for movies and when I click on a button for one of the FlatList rendered components I'd like to pass the object to the delete function and then to remove it from Firestore. I'm kind of stuck on how to do this. This is what I have so far:
const WatchList = () => {
const uid = auth.currentUser.uid;
const docRef = doc(db, 'users', uid);
const [user, setUser] = useState({});
const [watched, setWatched] = useState(true);
const [text, setText] = useState('Watched movies');
const [filteredBookings, setFilteredBookings] = useState(bookings);
const bookingsRef = collection(db, "booking");
const [bookings, setBookings] = useState({});
useEffect(() => {
getUser();
getBookings();
},[])
const getUser = async () => {
const snap = await getDoc(docRef)
setUser({user, ...snap.data()})
}
const getBookings = async () => {
const q = query(bookingsRef, where("users","array-contains",auth.currentUser.uid));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const a = [];
querySnapshot.forEach((doc) => {
a.push(doc.data());
});
setBookings(querySnapshot.docs);
});
}
const deleteBooking = (item) => {
console.log(item.data.().title)
}
return (
<View>
<View>
<Text>{text}</Text>
</View>
<FlatList
data = {filteredBookings}
numColumns = {1}
renderItem = {({item}) => (
<View>
<View>
<Text>{item.data().movie}</Text>
<Text>{item.data().day} - {item.data().showtime}</Text>
</View>
<View>
<TouchableOpacity onPress = {() => {deleteBooking(item)}}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
</View>
)}
/>
</View>
)
}
export default WatchList`
I've been trying to pass an item to display it in the console log to see if I got the right one first, but it's not working, so I'd really appreaciate some pointers. Thank you!
In your delete button's onPress method which you have defined as deleteBooking() you will get index also. you can rewrite as deleteBooking(item,index). and now in your definition use splice method of array. e.g. arrayName.splice(index,1) this will delete the record of given index.
For your array it should be like bookings.splice(index,1). Here 1 represents how many record you want to delete from given index.
Let me know doest it works for you or not.

How do I rerender a FlatList after onPress action?

I have a watchlist of bookings for movies and when I click on a button it deletes the booking from the Firestore database. This is the code I have:
const WatchList = () => {
const uid = auth.currentUser.uid;
const docRef = doc(db, 'users', uid);
const [user, setUser] = useState({});
const [watched, setWatched] = useState(true);
const [text, setText] = useState('Watched movies');
const [filteredBookings, setFilteredBookings] = useState(bookings);
const bookingsRef = collection(db, "booking");
const [bookings, setBookings] = useState({});
useEffect(() => {
getUser();
getBookings();
},[])
const getUser = async () => {
const snap = await getDoc(docRef)
setUser({user, ...snap.data()})
}
const getBookings = async () => {
const q = query(bookingsRef, where("users","array-contains",auth.currentUser.uid));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const a = [];
querySnapshot.forEach((doc) => {
a.push(doc.data());
});
setBookings(querySnapshot.docs);
});
}
const deleteBooking = async(id) => {
try {
await deleteDoc(doc(db, 'booking', id));
}
catch(e) {
console.log(e)
}
console.log('deleted:' + id)
}
return (
<View>
<View>
<Text>{text}</Text>
</View>
<FlatList
data = {filteredBookings}
numColumns = {1}
renderItem = {({item}) => (
<View>
<View>
<Text>{item.data().movie}</Text>
<Text>{item.data().day} - {item.data().showtime}</Text>
</View>
<View>
<TouchableOpacity onPress = {() => {deleteBooking(item.id)}}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
</View>
)}
/>
</View>
)
}
export default WatchList`
How do I get the FlatList to rerender after I press the button? When I refresh the whole app I see that it changes, but I'd like to have it rerendered after the onPress action. Thank you!
As far as I see from your implementation as data for FlatList you use filteredBookings but you set in only once as an initial state for useState hook. You need to update your list of filteredBookings every time you get books from the API like this:
useEffect(() => {
// filter returns new array object
const resultOfFiltering = bookings.filter((booking) => place your condition here)
setFilteredBookings(resultOfFiltering)
}, [bookings])

Firebase react native flatlist keyExtractor

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’

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';

Register and push data to Firebase in React Native

I have a registers and push data to Firebase function. I have a problem that when two functions are nested they are registered and stored in Firebase but there are warnings (below). But when I delete the push data function, there is no warning. I want to be able to register and be able to save data. Or can I write two separate functions and when onPress can call two functions at the same time?
This is my code:
handleSignUp = () => {
const { username, email, passwordOne } = this.state;
const { history } = this.props;
auth.doCreateUserWithEmailAndPassword(email, passwordOne)
.then(authUser => {
// Create a user in your own accessible Firebase Database too
db.doCreateUser(authUser.user.uid, username, email)
.then(() => {
this.setState({ ...INITIAL_STATE }, () => {
history.navigation.navigate("MainScreenNavigator");
});
})
.catch(error => this.setState({ errorMessage: error.message }));
})
.catch(error => this.setState({ errorMessage: error.message }));
};
And warning:
Warning: Can't call setState (or forceUpdate) 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.
doCreactUser function
export const doCreateUser = (id, username, email) =>
db.ref(`users/${id}`).set({
username,
email,
});
Full code:
import React, { Component } from "react";
import {
StyleSheet,
Text,
View,
StatusBar,
TextInput,
TouchableOpacity,
KeyboardAvoidingView
} from "react-native";
import Logo from "../components/Logo";
import { Actions } from "react-native-router-flux";
import { auth, db, firebase } from "../firebase/";
const INITIAL_STATE = {
username: "",
email: "",
passwordOne: "",
passwordTwo: "",
errorMessage: null
};
export default class Signup extends Component<{}> {
constructor(props) {
super(props);
this.state = { INITIAL_STATE };
}
handleSignUp = () => {
const { username, email, passwordOne } = this.state;
const { history } = this.props;
auth.doCreateUserWithEmailAndPassword(email, passwordOne)
.then(authUser => {
// Create a user in your own accessible Firebase Database too
db.doCreateUser(authUser.user.uid, username, email)
.then(() => {
this.setState({ ...INITIAL_STATE }, () => {
history.navigation.navigate("MainScreenNavigator");
});
})
.catch(error => this.setState({ errorMessage: error.message }));
})
.catch(error => this.setState({ errorMessage: error.message }));
};
goBack() {
Actions.pop();
}
render() {
const {
username,
email,
passwordOne,
passwordTwo,
} = this.state;
const isInvalid =
passwordOne !== passwordTwo ||
passwordOne === "" ||
email === "" ||
username === "";
const display = isInvalid ? "none" : "flex";
return (
<View style={styles.container}>
<StatusBar backgroundColor="#99d066" barStyle="light-content" />
<Logo />
<KeyboardAvoidingView>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder="Full Name"
placeholderTextColor="#000"
autoCapitalize="none"
selectionColor="#fff"
keyboardType="default"
onSubmitEditing={() => this.passwordOne.focus()}
onChangeText={username => this.setState({ username })}
value={this.state.username}
/>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder="Email"
placeholderTextColor="#000"
autoCapitalize="none"
selectionColor="#fff"
keyboardType="email-address"
onSubmitEditing={() => this.passwordOne.focus()}
onChangeText={email => this.setState({ email })}
value={this.state.email}
/>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder="Password"
secureTextEntry={true}
placeholderTextColor="#000"
autoCapitalize="none"
ref={input => (this.passwordOne = input)}
onChangeText={passwordOne => this.setState({ passwordOne })}
value={this.state.passwordOne}
/>
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholder="Confirm Password"
secureTextEntry={true}
placeholderTextColor="#000"
autoCapitalize="none"
ref={input => (this.passwordTwo = input)}
onChangeText={passwordTwo => this.setState({ passwordTwo })}
value={this.state.passwordTwo}
/>
</KeyboardAvoidingView>
<TouchableOpacity style={[styles.button, { display }]}>
<Text style={styles.buttonText} onPress={this.handleSignUp}>
Sign up
</Text>
</TouchableOpacity>
{this.state.errorMessage && (
<Text style={{ color: "#b71c1c", textAlign: "center" }}>
{this.state.errorMessage}
</Text>
)}
<View style={styles.signupTextCont}>
<Text style={styles.signupText}>Already have an account?</Text>
<TouchableOpacity onPress={this.goBack}>
<Text
style={styles.signupButton}
onPress={() => this.props.navigation.navigate("Login")}
>
{" "}
Sign in
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({...});

Resources