The <Login />
component heavily relies on the users
store for logic, as it is mostly focused on rendering two forms for login and registration. All the validation for the forms is done by Firebase, so we only need to focus on rendering the UI elements and calling the proper store methods.
In this screen, we will be using the react-native-keyboard-aware-scroll
view, which is a module providing a self-scrolling <Scrollview />
, which reacts to any focused <TextInput />
so they are not hidden when the keyboard pops up.
Let's take a look at the code:
/*** src/screens/Login.js ***/ import React, { PropTypes } from 'react' import { ScrollView, TextInput, Button, Text, View, Image, ActivityIndicator } from 'react-native'; import { observer, inject } from 'mobx-react/native' import Icon from 'react-native-vector-icons/FontAwesome' import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' import LoginForm from '../components/LoginForm' import RegistrationForm from '../components/RegistrationForm' @inject('users') @observer class Login extends React.Component { onLogin(email, password) { this.props.users.login(email, password); } onPressRegister(email, password, name) { this.props.users.register(email, password, name); } render() { return ( <KeyboardAwareScrollView style={{padding: 20, marginTop: 20, backgroundColor: '#eee'}}> <Icon name="comments" size={60} color='#ccc' style={{alignSelf: 'center', paddingBottom: 20}}/> <View style={{alignItems: 'center', marginBottom: 20}}> <Text>- please, login to continue -</Text> </View> <LoginForm onPress={this.onLogin.bind(this)} busy={this.props.users.loggingIn} loggingError={this.props.users.loggingError} /> <View style={{alignItems: 'center', marginTop: 20, marginBottom: 20}}> <Text>- or register -</Text> </View> <RegistrationForm onPress={this.onPressRegister.bind(this)} busy={this.props.users.registering} registeringError={this.props.users.registeringError} /> </KeyboardAwareScrollView> ) } } export default Login;
We split the login screen in two forms: <LoginForm />
and <RegistrationForm />
. Both components need to be passed three props:
onPress
: What the component needs to do when the Send button is pressed.busy
: Are we waiting for remote data?loginError/registrationError
: Description of the error that happened when logging/register (in case it happened).We are wrapping the whole screen in a <KeyboardAwareScrollView />
to ensure no <TextInput />
gets hidden by the keyboard when focused. Let's take a look at the LoginForm
now:
/*** src/components/LoginForm.js ***/ import React, { PropTypes } from 'react' import { TextInput, Button, Text, View, Image, ActivityIndicator } from 'react-native'; class LoginForm extends React.Component { state= { loginEmail: '', loginPassword: '' } onPressLogin() { this.props.onPress(this.state.loginEmail, this.state.loginPassword); } render() { return ( <View style={{backgroundColor: 'white', padding: 15, borderRadius: 10}}> { this.props.loggingError && <View style={{backgroundColor: '#fcc', borderRadius: 5, alignItems: 'center', marginBottom: 10}}> <Text>{this.props.loggingError}</Text> </View> } <TextInput autoCapitalize='none' autoCorrect={false} keyboardType='email-address'returnKeyType='next' style={{height: 40}} onChangeText={(loginEmail) => this.setState({loginEmail})} value={this.state.loginEmail} placeholder='email' onSubmitEditing={(event) => { this.refs.loginPassword.focus(); }} /> <TextInput ref='loginPassword' style={{height: 40}} onChangeText={(loginPassword) => this.setState({loginPassword})} value={this.state.loginPassword} secureTextEntry={true} placeholder='password' /> { this.props.busy ? <ActivityIndicator/> : <Button onPress={this.onPressLogin.bind(this)} title='Login' /> } </View> ) } } export default LoginForm;
For the <TextInput />
elements containing the email, we set the property keyboardType='email-address'
so the @
sign is easily accessible on the software keyboard. There are other options such as numeric keyboards, but we will only use 'email-address'
for this app.
Another useful prop on <TextInput />
is returnKeyType
. We set returnKeyType='next'
for those form inputs that are not the last ones to display the Next
button in the keyboard so the user knows they can go to the next input by tapping that button. This prop is used in conjunction with a prop like the following:
onSubmitEditing={(event) => { this.refs.loginPassword.focus(); }}
onSubmitEditing
is a <TextInput />
prop that will be invoked when a user presses the Return
or Next
button on the keyboard. We are using it to focus on the next <TextInput />
, which is quite user-friendly when dealing with forms. To get the reference for the next <TextInput />
we use ref
, which is not the safest way, but is good enough for simple forms. For this to work, we need to assign the corresponding ref
to the next <TextInput />
: ref='loginPassword'
.
RegistrationForm
is a very similar form:
/*** src/components/RegistrationForm ***/ import React, { PropTypes } from 'react' import { ScrollView, TextInput, Button, Text, View, Image, ActivityIndicator } from 'react-native'; class RegisterForm extends React.Component { state= { registerEmail: '', registerPassword: '', registerName: '' } onPressRegister() { this.props.onPress(this.state.registerEmail, this.state.registerPassword, this.state.registerName); } render() { return ( <View style={{backgroundColor: 'white', padding: 15, borderRadius: 10}}> { this.props.registeringError && <View style={{backgroundColor: '#fcc', borderRadius: 5, alignItems: 'center', marginBottom: 10}}> <Text>{this.props.registeringError}</Text> </View> } <TextInput autoCapitalize='none' autoCorrect={false} keyboardType='email-address' returnKeyType='next' style={{height: 40}} onChangeText={(registerEmail) => this.setState({registerEmail})} value={this.state.registerEmail} placeholder='email' onSubmitEditing={(event) => { this.refs.registerName.focus(); }} /> <TextInput ref='registerName' style={{height: 40}} onChangeText={(registerName) => this.setState({registerName})} returnKeyType='next' value={this.state.registerName} placeholder='name' onSubmitEditing={(event) => { this.refs.registerPassword.focus(); }} /> <TextInput ref='registerPassword' style={{height: 40}} onChangeText={(registerPassword) => this.setState({registerPassword})} value={this.state.registerPassword} secureTextEntry={true} placeholder='password' /> { this.props.busy ? <ActivityIndicator/> : <Button onPress={this.onPressRegister.bind(this)} title='Register' /> } </View> ) } } export default RegisterForm;
18.119.136.84