I started to learn react-native and for now I created a few components in order to use signing in my firebase project. I already created a firebase project from firebase console and dealt with lots of errors. Finally I created my App.js as :
import React, {Component} from 'react';
import {View} from 'react-native';
//import firebase from 'firebase';
//import firebase from '#firebase/app';
import * as firebase from 'firebase';
import '#firebase/auth';
import Header from './src/components/common/Header';
import LoginForm from './src/components/LoginForm';
import CardSection from './src/components/common/CardSection';
import Button from './src/components/common/Button';
import Spinner from './src/components/common/Spinner';
class Main extends Component {
state = {loggedIn: null};
componentDidMount() {
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: 'AIzaSyCuggnPGozfGB9M1elP7yOG5kdH3Xk5BDM',
authDomain: 'digitus-trial.firebaseapp.com',
databaseURL:
'https://digitus-trial-default-rtdb.europe-west1.firebasedatabase.app',
projectId: 'digitus-trial',
storageBucket: 'digitus-trial.appspot.com',
messagingSenderId: '811615820065',
appId: '1:811615820065:web:993900aab9e5927b5a0660',
measurementId: 'G-4KSTW48LX0',
});
} else {
firebase.app();
}
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({loggedIn: true});
} else {
this.setState({loggedIn: false});
}
});
}
logoutClicked() {
firebase.auth().signOut();
}
renderContent() {
switch (this.state.loggedIn) {
case true:
return (
<CardSection>
<Button onPress={this.logoutClicked.bind(this)}>Logout</Button>
</CardSection>
);
case false:
return <LoginForm />;
default:
return (
<View>
<Spinner size="large" />
</View>
);
}
}
render() {
return (
<View>
<Header headerText="Digitus Trial" />
{this.renderContent()}
</View>
);
}
}
export default Main;
My problem is when I press login button my button fade out instead of showing a indicator. And also I couldn't get any message which I already put console.log flagging. I'm working with Windows 10 and Android emulator. Can you help me out? For further information here is my form file:
import React, {Component} from 'react';
import {TextInput} from 'react-native';
import firebase from '#firebase/app';
import '#firebase/auth';
import Button from './common/Button';
import Card from './common/Card';
import CardSection from './common/CardSection';
import Spinner from './common/Spinner';
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {email: '', password: '', loading: false};
}
loginClick() {
this.setState({loading: true});
const {email, password} = this.state;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(this.loginSuccess.bind(this))
.catch(() => {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(this.loginSuccess.bind(this))
.catch(this.loginFail.bind(this));
});
}
onPressSignIn() {
this.setState({
loading: true,
});
}
renderCurrentState() {
const {inputStyle} = styles;
if (this.state.loading) {
return (
<Card>
<CardSection>
<TextInput
placeholder="Email"
style={inputStyle}
value={this.state.email}
onChangeText={(email) => this.setState({email})}
/>
</CardSection>
<CardSection>
<TextInput
placeholder="Password"
style={inputStyle}
value={this.state.password}
onChangeText={(password) => this.setState({password})}
/>
</CardSection>
<CardSection>
<Spinner size="large" />
</CardSection>
</Card>
);
}
return (
<Card>
<CardSection>
<TextInput
placeholder="Email"
style={inputStyle}
value={this.state.email}
onChangeText={(email) => this.setState({email})}
/>
</CardSection>
<CardSection>
<TextInput
placeholder="Password"
style={inputStyle}
value={this.state.password}
onChangeText={(password) => this.setState({password})}
/>
</CardSection>
<CardSection>
<Button onPress={() => this.onPressSignIn()}>Login</Button>
</CardSection>
</Card>
);
}
render() {
return <Card>{this.renderCurrentState()}</Card>;
}
}
//Android has no shadow property, in order to use in IOS emulator make elevation:1
const styles = {
inputStyle: {
color: '#000',
paddingRight: 5,
paddingLeft: 5,
fontSize: 18,
lineHeight: 23,
flex: 1,
},
};
export default LoginForm;
After several trials, I found that firebase services are too slow. For every login operation, I need to wait 30 secs or so. So my problem was solved.
Related
I am beginning to code with react native and I am trying my first app. I am in the process of making the login/signup of the app. I managed to log the user in but I cannot seem to figure out how to keep the user logged in when I close the app. How can I keep a user logged in to the app even when the app is closed?
This is what I have-
login.js
import { StatusBar } from 'expo-status-bar'
import { StyleSheet, Text, View, FlatList, Image, Button, Pressable, ScrollView } from 'react-native';
import React, {useState, useEffect} from 'react'
import { TextInput } from 'react-native-gesture-handler';
export default function Login(props) {
const message = props.navigation.getParam('message', null)
const [ username, setUsername ] = useState("")
const [ password, setPassword ] = useState("")
const log = () => {
fetch(`http://192.168.5.223:8000/home/login/`, {
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
body: JSON.stringify({ username: username, password: password}),
})
.then( res => res.json())
.then( res => {
console.log(res)
if (res.valid === true){
if (res.set === true){
props.navigation.navigate("Home", {"user": username})
} else {
props.navigation.navigate("Profile", {"user": username})
}
} else {
props.navigation.navigate("Login", {'message': "username/password are incorrect"})
}
})
.catch( error => console.log(error))
}
const sign = () => {
props.navigation.navigate("Signup")
}
return (
<View style={styles.container}>
<ScrollView style={styles.scroll} >
<View style={styles.main}>
<Text style={styles.error}>{message}</Text>
<Text style={styles.label}>
Username:
</Text>
<TextInput style={styles.input} placeholder="Username"
onChangeText={ text => setUsername(text)} value={username}
autoCapitalize={'none'}
/>
<Text style={styles.label}>Password:</Text>
<TextInput style={styles.input} placeholder="Password" onChangeText={ text => setPassword(text)}
value={password} secureTextEntry={true} autoCapitalize={'none'}
/>
<Button onPress={ () => log()} title="Login"></Button>
</View>
</ScrollView>
<View style={styles.footer}>
<Button onPress={ () => sign()} title="Don't have an acount? Sign up now" />
</View>
<StatusBar style="auto"/>
</View>
)
}
Login.navigationOptions = screenProps => ({
headerLeft: () => null,
gestureEnabled: false,
headerStyle: {
backgroundColor: 'black'
},
headerTintColor: 'white',
})
If you want your a user session to be persisted when the user closes and reopens the app you can use the AsyncStorage API.
See for instance this question Persistent User Session on React Native App
You need to store a token/cookie that will be used by your server to authenticate the user.
I'm trying to get a login function to redirect to a certain view, but I can't find a way to use a navigate or this.props.push inside the context api. The function that makes the redirection is handleLogin and is consumed from loginForm view.
but if I tried to use useNavigate, I would have to transform that class component to a functional
AppProvider
// React
import React from 'react';
// AppContext
import AppContext from '../AppContext';
// Axios
import axios from 'axios';
// React-cookies
import cookies from "js-cookie";
class AppProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
store: {
userInfo: {},
isLoggedIn: localStorage.getItem('isLogged') ? true : false
},
actions: {
handleLogin: this.handleLogin
}
}
}
handleLogin = (e, credentials) => {
e.preventDefault();
axios.post('/users/signin', credentials, {
'content-type': 'application-json'
})
.then((res) => {
cookies.set('auth-token', res.headers['auth-token']);
this.setState({store:{isLoggedIn: true}});
localStorage.setItem('isLogged', true);
})
}
render() {
return(
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
);
}
}
export default AppProvider;
LoginForm:
// React
import React, { useState } from 'react';
// Mui
import { Grid, Card, CardContent, TextField, Button, Typography } from '#mui/material';
// React-router
import { Link, useNavigate } from 'react-router-dom';
// Axios
import axios from 'axios';
// React-cookies
import { useCookies } from "react-cookie";
// Context
import AppContext from '../../AppContext';
const LoginForm = () => {
const navigate = useNavigate();
const [ credentials, setCredentials ] = useState({
email: null,
password_digest: null
});
const [cookies, setCookies] = useCookies(['auth-token']);
const handleChange = (e) => {
setCredentials({...credentials, [e.target.name]: e.target.value});
}
return(
<AppContext.Consumer>
{context => {
console.log(context)
return(
<Grid container direction="column" alignItems="center" paddingTop="10%">
<Grid item xs={3}>
<Card
style={{
background: '#1e272e',
padding: '1em'
}}
>
<Typography textAlign="center" color="white">
Welcome!
</Typography>
<CardContent>
<form onSubmit={(e) => context.actions.handleLogin(e, credentials)}>
<TextField
variant='filled'
label="email"
name="email"
onChange={handleChange}
sx={{
display: 'block',
margin: '.5rem 0'
}}
inputProps={{style: {color: 'white'}}}
InputLabelProps={{style: {color: 'white'}}}
/>
<TextField
variant="filled"
label="password"
name="password_digest"
type="password"
onChange={handleChange}
rows={4}
sx={{
display: 'block',
margin: '1rem 0'
}}
inputProps={{style: {color: 'white'}}}
InputLabelProps={{style: {color: 'white'}}}
/>
<Button variant='contained'
color='primary'
type='submit'>
Login
</Button>
</form>
<Link to="/register"><Typography variant="span" style={{fontSize: '.8em'}} mt={2} color="white">Don't you have an account? Sign up</Typography></Link>
</CardContent>
</Card>
</Grid>
</Grid>
)
}}
</AppContext.Consumer>
)
}
export default LoginForm;
You have to create a route config declaring routes and the files each route points to. This will help in letting react-router know what view to display when redirected to the respective link.
Refer the tutorial here for more information.
I want to clear input after my form submission get successful. I don't want to use reset button in this case.
I have passed submitted data to api that is in another file.
Please help.
file forgotPassword.js
import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { forgotPassword } from "../../actions/authActions";
import classnames from "classnames";
class ForgotPassword extends Component {
constructor() {
super();
this.state = {
email:"",
errors: {}
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
onChange = e => {
this.setState({ [e.target.id]: e.target.value });
};
onSubmit = e => {
e.preventDefault();
var emailId = {
email: this.state.email
};
this.props.forgotPassword(emailId, this.props.history);
};
render(){
const { errors } = this.state;
return (
<div className="container">
<div className="row">
<div className="col s8 offset-s2">
<div className="col s12" style={{ paddingLeft: "11.250px" }}>
<h4><b>Forgot Password</b></h4>
</div>
<form noValidate onSubmit={this.onSubmit}>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.email}
error={errors.email}
id="email"
type="email"
className={classnames("", {
invalid: errors.email
})}
/>
<label htmlFor="email">Email</label>
<span className="red-text">{errors.email}</span>
</div>
<div className="col s12" style={{ paddingLeft: "11.250px" }}>
<button
style={{
width: "150px",
borderRadius: "3px",
letterSpacing: "1.5px",
marginTop: "1rem"
}}
type="submit"
className="btn btn-large waves-effect waves-light hoverable blue accent-3"
>
Submit
</button>
</div>
</form>
</div>
</div>
</div>
);
}
onHandleSubmit(e) {
e.preventDefault();
const email = this.state.email;
this.props.onSearchTermChange(email);
console.log(email);
this.setState({
email: ''
});
}
}
ForgotPassword.propTypes = {
forgotPassword: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
export default connect(
mapStateToProps,
{ forgotPassword }
)(ForgotPassword);
File authaction.js where calling api
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING} from "./types";
export const forgotPassword = (userData, history) => dispatch => {
axios
.post("/api/users/forgotpassword", userData)
.then(res =>
console.log("forgot password",res)
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
After successfull result on api I'm unable to clear input in forgot password form component.
Please let me know if any other way to do this task.I'm a newbie in react.
Many thanks for help.
Changing the value of a TextInput component
You can change the context of the input after an onPress event coming from a button.
export default class App extends React.Component {
state = {
text : "Username"
}
render() {
return (
<View style={styles.container}>
// TextInput gets its value from the state.text above.
<TextInput value={this.state.text } style={{borderColor:"black", border:1}}/>
// Button calls the function in onPress when it is pressed, which cleans up the state.text
<Button title="CLEAN" onPress={() => this.setState({text: ""})} />
</View>
);
}
}
I have an simple application that have just two screens: Login and Home. I want to go to Home screen from Login screen. I don't want to use a Navigator, if it is possible. This is my code:
Index.js
import { AppRegistry } from 'react-native';
import App from './src/App';
AppRegistry.registerComponent('App', () => App);
App.js
class App extends Component {
constructor(props) {
super(props);
}
render() {
return(
<LoginPage />
);
}
}
export default App;
LoginPage.js
export class LoginPage extends Component{
constructor(props) {
super(props);
this.state = { email: '', password: '' };
}
goToHomePage = () => {
// HERE!!!!!!
};
onButtonPress = () => {
this.goToHomePage();
};
render(){
return(
<View>
<TextInput
style={Styles.textInput}
onChangeText={(email) => this.setState({email})}
placeholder={'Email'}
value={this.state.email}
/>
<TextInput
style={Styles.textInput}
onChangeText={(password) => this.setState({password})}
secureTextEntry
placeholder={'Password'}
value={this.state.password}
/>
<Button onPress={() => this.props.navigation.navigate('HomePage')}
title="Login"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
</View>
);
};
}
HomePage.js
export class HomePage extends Component{
constructor(props) {
super(props);
}
goToLoginPage = () => {
// HERE !!!!!!
};
onButtonPress = () => {
this.goToLoginPage();
};
render () {
return (
<View style={Styles.container}>
<View>
<LoginHeader Title={'Titulo do HomePage'} />
</View>
<Button
style={Styles.button}
title={'Logout'}
onPress={this.onButtonPress}
/>
</View>
)
}
}
So, how can I implement a method for move to screens with this code? I've tried to use react-native-navigation and react-navigation, but does not work for me in this case.
EDIT
I've tried to use this:
App.js
import { createStackNavigator } from 'react-navigation';
const RootStack = createStackNavigator(
{
HomePage: HomePage,
LoginPage: LoginPage,
},
{
initialRouteName: 'LoginPage',
}
);
class App extends Component {
constructor(props) {
super(props);
}
render() {
return(
<RootStack />
);
}
}
export default App;
LoginPage.js
import { createStackNavigator } from 'react-navigation';
export class LoginPage extends Component{
constructor(props) {
super(props);
this.state = { email: '', password: '' };
}
goToHomePage = () => {
// HERE!!!!!!
};
onButtonPress = () => {
this.goToHomePage();
};
render(){
return(
<View>
<TextInput
style={Styles.textInput}
onChangeText={(email) => this.setState({email})}
placeholder={'Email'}
value={this.state.email}
/>
<TextInput
style={Styles.textInput}
onChangeText={(password) => this.setState({password})}
secureTextEntry
placeholder={'Password'}
value={this.state.password}
/>
<Button onPress={() => this.props.navigation.navigate('HomePage')}
title="Login"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
</View>
);
};
}
Do you import React, {Component} from "react"?
I'm trying to deconstruct the style object but it gives me error on the line const { errorTextStyle } = myStyle; saying: Unexpected token (8:8)".
Below is the whole code:
import React, { Component } from 'react';
import { Platform, Dimensions, Text } from 'react-native';
import { Card, CardSection, Button, Input } from './common';
class LoginForm extends Component {
/////////////////////
state = {email: '', password: '', error: ''};
const { errorTextStyle } = myStyle;
///////////////////// methods
onButtonPress(){
const { email, password } = this.state;
firebase.auth().signInWithEmailAndPassword(email,password).
catch(() => {
firebase.auth().createUserWithEmailAndPassword(email,password).
catch(() => {
this.setState({error: 'Authentication Failed.'});
});
});
}
////////////////////// render
render(){
return(
<Card>
<CardSection >
<Input
placeholder="Type here :)"
onChangeText={ email => this.setState({ email }) }
value={ this.state.email }
label={ 'Email: ' }
autoCorrect={false}
/>
</CardSection >
<CardSection >
<Input
placeholder="Type here :)"
onChangeText={ password => this.setState({password}) }
value={this.state.password}
label={'Password: '}
autoCorrect={false}
secureTextEntry
/>
</CardSection >
<Text style={ errorTextStyle }>
{this.state.error}
</Text>
<CardSection>
<Button onPress={this.onButtonPress.bind(this)}>
Login :)
</Button>
</CardSection>
</Card>
);
}
}
const myStyle = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
}
};
export default LoginForm;
Since the variable is being used inside render, move that code inside that method. You can only declare methods and fields inside the class declaration, expressions must go inside a method. state = ... is working for you because that's a field declaration. Try the following:
render(){
const { errorTextStyle } = myStyle;
...
}