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({...});
Related
I want to read data from firebase realtime database.
I want to fix my "function readData()"
like,
If I use this funcion, I want to read my data, without enter "username".
for example, I created this,
username:hello,
email:hello#gmail.com
and If I press "read data" button, (without enter 'username')
I want to read recent data. (username and email both)
please help me!
this is my app.js
import { StatusBar } from 'expo-status-bar';
import { useState } from 'react';
import { Button, button, StyleSheet, Text, TextInput, View } from 'react-native';
import { ref, set, update, onValue, remove } from "firebase/database";
import { db } from './components/config';
export default function App() {
const [username, setName] = useState('');
const [email, setEmail] = useState('');
function createData() {
set(ref(db, 'users/' + username), {
username: username,
email: email
}).then(() => {
// Data saved successfully!
alert('data created!');
})
.catch((error) => {
// The write failed...
alert(error);
});
}
function update() {
set(ref(db, 'users/' + username), {
username: username,
email: email
}).then(() => {
// Data saved successfully!
alert('data updated!');
})
.catch((error) => {
// The write failed...
alert(error);
});
}
function readData() {
const starCountRef = ref(db, 'users/' + username);
onValue(starCountRef, (snapshot) => {
const data = snapshot.val();
setEmail(data.email);
});
}
return (
<View style={styles.container}>
<Text>firebase</Text>
<TextInput value={username} onChangeText={(username) => {setName(username)}} placeholder='Username' style={styles.TextBoxes}></TextInput>
<TextInput value={email} onChangeText={(email) => {setEmail(email)}} placeholder='Email' style={styles.TextBoxes}></TextInput>
<Button title='create data' onPress={createData}></Button>
<Button title='update data' onPress={update}></Button>
<Button title='read data' onPress={readData}></Button>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
TextBoxes: {
width:'90%',
fontSize:18,
padding:12,
backgroundColor:'grey',
marginVertical:10,
}
});
I understand you want to call last username by default if username is empty.
For this, you can define lastUserName state and call it if username is empty, for example;
const [lastUserName, setLastUserName] = useState('');
readData() ,
function readData() {
const starCountRef = ref(db, 'users/' + username !== '' ? username : lastUserName);
onValue(starCountRef, (snapshot) => {
const data = snapshot.val();
setEmail(data.email);
});
}
Then, view should be like that;
<View style={styles.container}>
<Text>firebase</Text>
<TextInput value={username}
onChangeText={(username) => {
setName(username)
setLastUserName(username) // add this line
}
}
placeholder='Username' style={styles.TextBoxes}></TextInput>
<TextInput value={email} onChangeText={(email) => {setEmail(email)}} placeholder='Email' style={styles.TextBoxes}></TextInput>
<Button title='create data' onPress={createData}></Button>
<Button title='update data' onPress={update}></Button>
<Button title='read data' onPress={readData}></Button>
</View>
Note : If you want to keep this state in global use Redux.
Note: Use localStorage if you want the application to run on the same state after closing and reopening. see: https://github.com/react-native-async-storage/async-storage
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
})
I'm developing my first ever React Native App. I have created Login/SignUp screen and connect these with Firebase for Authentication. As well as i have some screen containing Drawers. I want that if the user is Not Authenticated. Login/Signup screen should show otherwise HomeScreen(having many drawers) will show. For this i did some checks in MainRouteComponent but its not working
I am uploading my Code of all the related Components as well as some redux code i have used here. I know it will take time to go through that much code but i tried a lot and i am struggling now. Hope many of you will help. :)
Here is my code:-
App.js
import React from 'react';
import Main from './components/MainComponent';
import MainRoute from './components/MainRouteComponent'
import { Provider } from 'react-redux';
import { ConfigureStore } from './redux/configureStore';
import { PersistGate } from 'redux-persist/es/integration/react'
import { Loading } from './components/LoadingComponent';
const { persistor, store } = ConfigureStore();
export default App = () =>{
return (
<Provider store={store}>
<PersistGate
loading={<Loading />}
persistor={persistor}>
<MainRoute/>
</PersistGate>
</Provider>
);
}
MainRouteComponent.js
import React, { useState } from 'react';
import Main from './MainComponent';
import Root from './RootComponent';
import { auth } from '../firebase/firebase';
const AuthContext = React.createContext();
const MainRoute = () => {
const user = auth.currentUser
console.log("User", user)
console.log("Auth", isAuth)
return(
{(user == null) ?
<Root />
:
<Main />
}
)
}
export default MainRoute;
RootComponent.js
import React, {Component} from 'react';
import { createStackNavigator } from 'react-navigation';
import * as Animatable from 'react-native-animatable';
import SplashScreen from './SplashScreen';
import Login from './LoginComponent';
import Register from './RegistrationComponent';
const RootStack = createStackNavigator({
SplashScreen: { screen: SplashScreen,
navigationOptions: ({ navigation }) => ({
headerStyle: {
backgroundColor: "#512DA8"
},
headerTitle: "Welcome Foodies",
headerTintColor: '#fff',
headerTitleStyle: {
color: "#fff",
alignItems: 'center'
}
})
},
LoginComponent: {
screen: Login,
navigationOptions: ({ navigation }) => ({
headerStyle: {
backgroundColor: "#512DA8"
},
headerTitle: "Login",
headerTintColor: '#fff',
headerTitleStyle: {
color: "#fff",
alignItems: 'center'
}
})
},
Register: {
screen: Register,
navigationOptions: ({ navigation }) => ({
headerStyle: {
backgroundColor: "#512DA8"
},
headerTitle: "Sign Up",
headerTintColor: '#fff',
headerTitleStyle: {
color: "#fff",
alignItems: 'center'
}
})
},
},
{
initialRouteName: 'SplashScreen',
navigationOptions: ({ navigation }) => ({
headerStyle: {
backgroundColor: "#512DA8"
},
headerTintColor: '#fff',
headerTitleStyle: {
color: "#fff"
}
})
}
);
class Root extends Component {
render() {
return (
<RootStack />
)
}
}
export default Root;
LoginComponent.js
import React, { Component } from 'react';
import { View, StyleSheet, Text, ScrollView, Image } from 'react-native';
import { Input, CheckBox, Button, Icon } from 'react-native-elements';
import * as SecureStore from 'expo-secure-store';
import { loginUser} from '../redux/ActionCreators';
import { connect } from 'react-redux';
const mapStateToProps = state => {
return {
auth: state.AUTH
}
}
const mapDispatchToProps = dispatch => {
return {
loginUser: (creds) => dispatch(loginUser(creds))
}
}
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
remember: false
}
}
componentDidMount() {
SecureStore.getItemAsync('userinfo')
.then((userdata) => {
let userinfo = JSON.parse(userdata);
if (userinfo) {
this.setState({email: userinfo.email});
this.setState({password: userinfo.password});
this.setState({remember: true})
}
})
}
static navigationOptions = {
title: 'Login',
tabBarIcon: ({ tintColor }) => (
<Icon
name='sign-in'
type='font-awesome'
size={24}
iconStyle={{ color: tintColor }}
/>
)
};
handleLogin() {
this.props.loginUser({email: this.state.email, password: this.state.password});
//event.preventDefault();
if (this.state.remember)
SecureStore.setItemAsync('userinfo', JSON.stringify({email: this.state.email, password: this.state.password}))
.catch((error) => console.log('Could not save user info', error));
else
SecureStore.deleteItemAsync('userinfo')
.catch((error) => console.log('Could not delete user info', error));
this.setState({
email: '',
password: '',
remember: false
})
this.props.navigation.navigate('Home')
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Input
placeholder="Email"
leftIcon={{ type: 'font-awesome', name: 'envelope-o' }}
onChangeText={(email) => this.setState({email})}
value={this.state.email}
containerStyle={styles.formInput}
/>
<Input
placeholder="Password"
leftIcon={{ type: 'font-awesome', name: 'key' }}
onChangeText={(password) => this.setState({password})}
value={this.state.password}
containerStyle={styles.formInput}
/>
<CheckBox title="Remember Me"
center
checked={this.state.remember}
onPress={() => this.setState({remember: !this.state.remember})}
containerStyle={styles.formCheckbox}
/>
<View style={styles.formButton}>
<Button
onPress={() => this.handleLogin()}
title="Login"
icon={
<Icon
name='sign-in'
type='font-awesome'
size={24}
color= 'white'
/>
}
buttonStyle={{
backgroundColor: "#512DA8"
}}
/>
</View>
<View style={styles.formButton}>
<Button
onPress={() => navigate('Register')}
title="Register"
clear
icon={
<Icon
name='user-plus'
type='font-awesome'
size={24}
color= 'blue'
/>
}
titleStyle={{
color: "blue"
}}
buttonStyle={{
backgroundColor: "transparent"
}}
/>
</View>
</View>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
ActionCreator.js
import * as ActionTypes from './ActionTypes';
import { auth, firestore, fireauth, firebasestore } from '../firebase/firebase';
export const requestLogin = () => {
return {
type: ActionTypes.LOGIN_REQUEST
}
}
export const receiveLogin = (user) => {
return {
type: ActionTypes.LOGIN_SUCCESS,
user
}
}
export const loginError = (message) => {
return {
type: ActionTypes.LOGIN_FAILURE,
message
}
}
export const loginUser = (creds) => (dispatch) => {
// We dispatch requestLogin to kickoff the call to the API
dispatch(requestLogin(creds))
return auth.signInWithEmailAndPassword(creds.email, creds.password)
.then(() => {
var user = auth.currentUser;
dispatch(fetchFavorites());
dispatch(receiveLogin(user));
})
.then(response => { console.log('Login Successful', response); alert('Thank you for login!'); })
.catch(error => {console.log('Error', error); alert(error)})
.catch(error => dispatch(loginError(error.message)))
};
I also have Login Reducer as auth.js as i thought this can help in checking authentication to show screen accordingly.
auth.js
import * as ActionTypes from './ActionTypes';
export const Auth = (state = {
isLoading: false,
isAuthenticated: false,
user: null,
errMess: null
}, action) => {
switch (action.type) {
case ActionTypes.LOGIN_REQUEST:
return {...state,
isLoading: true,
isAuthenticated: false,
};
case ActionTypes.LOGIN_SUCCESS:
return {...state,
isLoading: false,
isAuthenticated: true,
errMess: '',
user: action.user
};
case ActionTypes.LOGIN_FAILURE:
return {...state,
isLoading: false,
isAuthenticated: false,
errMess: action.message
};
case ActionTypes.LOGOUT_REQUEST:
return {...state,
isLoading: true,
isAuthenticated: true
};
case ActionTypes.LOGOUT_SUCCESS:
return {...state,
isLoading: false,
isAuthenticated: false,
token: '',
user: null
};
default:
return state
}
}
Make separate routing for the stack screens which you don't want to be shown ! Try to make 2 stacks!
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';
in my app I pull data from Firebase and render them in a ListView. I want that the spinner will run until the entire list will appear.
I added the a view with a conditions that show if the state.loading is 'true', but if I change it in componentDidMount function does not work because the list is not yet displayed
Here is my code:
module.exports = React.createClass({
getInitialState() {
return({
loading: false,
displayName: '',
title: '',
dataSource: ds.cloneWithRows([{
title: '',
author: ''
}])
})
},
componentDidMount() {
let user = firebaseApp.auth().currentUser;
if (!user.displayName) {
this.props.navigator.push({
name: 'chooseName'
})
} else {
// proceed normally with application
this.setState({
displayName: user.displayName
})
this.listenForItems(topicsRef);
}
},
listenForItems(ref) {
ref.on('value', (snap) => {
let topics = [];
snap.forEach(topic => {
topics.push({
title: topic.val().title,
author: topic.val().author,
key: topic.key
})
})
this.setState({dataSource: ds.cloneWithRows(topics)});
})
},
signOut() {
// sign out the user
firebaseApp.auth().signOut()
.then(() => {
// Sign out successful
this.props.navigator.popToTop();
}, (error) => {
console.log(error);
})
},
details(data) {
this.props.navigator.push({
name: 'topicDetail',
displayName: this.state.displayName,
title: data.title,
author: data.author,
row_uid: data.key
})
},
renderRow(rowData) {
return (
<TouchableOpacity style={styles.row}
onPress={() => this.details(rowData)}
>
<Text style={styles.rowTitle}>
{rowData.title}
</Text>
<Text>
{rowData.author}
</Text>
</TouchableOpacity>
)
},
addTopic() {
topicsRef.push({
title: this.state.title,
author: this.state.displayName
})
},
render() {
if (this.state.loading) {
return (
<Container style={styles.containerSignIn}>
<Content>
<Spinner />
</Content>
</Container>);
}
return (
<View style={styles.flexContainer}>
<View style={styles.header}>
<TouchableOpacity
onPress={() => this.signOut()}
>
<Text style={styles.link}>
Sign out
</Text>
</TouchableOpacity>
<Text style={styles.title}>
{this.state.displayName}
</Text>
</View>
<View style={styles.body}>
<TextInput
placeholder='Something on your mind?'
style={styles.input}
onChangeText={(text) => this.setState({title: text})}
onEndEditing={() => this.addTopic()}
/>
<ListView
style={styles.list}
enableEmptySections={true}
dataSource={this.state.dataSource}
renderRow={(rowData) => this.renderRow(rowData)}
/>
</View>
</View>
)
}
});
I assume your Spinner component works.
Due to possible issues in your react lifecycle what I can recommend in the first run:
1.) if you want your Spinner to be active when your View shows up, set state.loading to true when defining your initialState.
getInitialState() {
return({
loading: true,
...
})
}
2.) change the loading state in the (success) Promise callback of your firebase request and NOT in componentDidMount().
listenForItems(ref) {
ref.on('value', (snap) => {
...
this.setState({
loading: false,
dataSource: ds.cloneWithRows(topics)
});
})
}
Hope that helps.