William Kurniawan
Oct 6, 2020
npm install -g expo-cli
expo init
App.tsx
and replace the content with the following code.import React from 'react';
import {StyleSheet, View} from 'react-native';
function useStyles() {
return StyleSheet.create({
root: {
backgroundColor: '#000000',
flex: 1,
},
});
}
const App: React.FC = () => {
const styles = useStyles();
return <View style={styles.root}></View>;
};
export default App;
expo start
in your terminal. You can test your app using Android/iOS simulator or your real device. If you want to run the app from your real device, you need to install Expo Client. You can download the Expo Client from Play Store or App Store. At this point, you will only see a blank black screen.expo install <dependency>
. Run the following command to install React Hook Form.expo install react-hook-form
react-hook-form
into your project dependencies. You can verify this by opening your package.json
. You will notice that react-hook-form
is listed in your project dependencies."dependencies": {
...
"react-hook-form": "^6.9.0",
...
}
StyleSheet
. However, feel free to come up with your own design. I am pretty sure you can do better than me. Also, there are many UI libraries that you can use to build the user interface, such as React Native Paper, Native Base, UI Kitten, etc.useStyles
function with the following code.function useStyles() {
return StyleSheet.create({
button: {
alignItems: 'center',
backgroundColor: 'rgb(93, 95, 222)',
borderRadius: 8,
height: 48,
justifyContent: 'center',
},
buttonTitle: {
color: '#FFFFFF',
fontSize: 17,
fontWeight: '600',
lineHeight: 22,
},
content: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 16,
paddingVertical: 32,
},
forgotPasswordContainer: {
alignItems: 'flex-end',
},
form: {
alignItems: 'center',
backgroundColor: 'rgb(58, 58, 60)',
borderRadius: 8,
flexDirection: 'row',
height: 48,
paddingHorizontal: 16,
},
label: {
color: 'rgba(235, 235, 245, 0.6)',
fontSize: 15,
fontWeight: '400',
lineHeight: 20,
width: 80,
},
root: {
backgroundColor: '#000000',
flex: 1,
},
safeAreaView: {
flex: 1,
},
subtitle: {
color: 'rgba(235, 235, 245, 0.6)',
fontSize: 17,
fontWeight: '400',
lineHeight: 22,
},
textButton: {
color: '#FFFFFF',
fontSize: 15,
fontWeight: '400',
lineHeight: 20,
},
textInput: {
color: '#FFFFFF',
flex: 1,
},
title: {
color: '#FFFFFF',
fontSize: 28,
fontWeight: '700',
lineHeight: 34,
},
});
}
style
. The style names and values usually match how CSS works on the web, except names are written using camel casing, e.g. backgroundColor
rather than background-color
.SizedBox
. Basically, this component is only a View
which will work as a separator. If you are coming from Flutter, this component is similar to SizedBox
widget in Flutter.src/components/SizedBox.tsx
and write the following code.import React from 'react';
import { View } from 'react-native';
interface Props {
height?: number;
width?: number;
}
const SizedBox: React.FC<Props> = ({ height, width }) => {
return <View style={{ height, width }} />;
};
export default SizedBox;
App.tsx
to import the required components to build our login form. Update your import statements with the following code.import React from 'react';
import {
Alert,
KeyboardAvoidingView,
Platform,
Pressable,
SafeAreaView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
// Components
import SizedBox from './src/components/SizedBox';
App
component with the following code.const App: React.FC = () => {
const styles = useStyles();
return (
<View style={styles.root}>
<SafeAreaView style={styles.safeAreaView}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.content}
>
<Text style={styles.title}>Welcome back!</Text>
<SizedBox height={8} />
<Text style={styles.subtitle}>Sign in to your account</Text>
<SizedBox height={32} />
<Pressable>
<View style={styles.form}>
<Text style={styles.label}>Email</Text>
<TextInput
autoCapitalize="none"
autoCompleteType="email"
autoCorrect={false}
keyboardType="email-address"
returnKeyType="next"
style={styles.textInput}
textContentType="username"
/>
</View>
</Pressable>
<SizedBox height={16} />
<Pressable>
<View style={styles.form}>
<Text style={styles.label}>Password</Text>
<TextInput
autoCapitalize="none"
autoCompleteType="password"
autoCorrect={false}
returnKeyType="done"
secureTextEntry
style={styles.textInput}
textContentType="password"
/>
</View>
</Pressable>
<SizedBox height={16} />
<View style={styles.forgotPasswordContainer}>
<Text style={styles.textButton}>Forgot password?</Text>
</View>
<SizedBox height={16} />
<TouchableOpacity>
<View style={styles.button}>
<Text style={styles.buttonTitle}>Continue</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
</View>
);
};
react-native
import statement to import the required module to implement React Hook Form.// Modules
import { Controller, useForm } from 'react-hook-form';
FormData
type to support form values.interface FormData {
email: string;
password: string;
}
App
component.const App: React.FC = () => {
const { control, handleSubmit } = useForm<FormData>({
defaultValues: {
email: '',
password: '',
},
});
const onSubmit = handleSubmit(({ email, password }) => {
Alert.alert('Data', `Email: ${email}\nPassword: ${password}`);
});
...
return (
...
);
};
...
<Controller
control={control}
name="email"
render={({ onBlur, onChange, value }) => (
<TextInput
autoCapitalize="none"
autoCompleteType="email"
autoCorrect={false}
keyboardType="email-address"
onBlur={onBlur}
onChangeText={onChange}
returnKeyType="next"
style={styles.textInput}
textContentType="username"
value={value}
/>
)}
/>
...
<Controller
control={control}
name="password"
render={({ onBlur, onChange, value }) => (
<TextInput
autoCapitalize="none"
autoCompleteType="password"
autoCorrect={false}
onBlur={onBlur}
onChangeText={onChange}
onSubmitEditing={onSubmit}
returnKeyType="done"
secureTextEntry
style={styles.textInput}
textContentType="password"
value={value}
/>
)}
/>
...
...
<TouchableOpacity onPress={onSubmit}>
<View style={styles.button}>
<Text style={styles.buttonTitle}>Continue</Text>
</View>
</TouchableOpacity>
...
Alert
displaying your email and password. If you realise, the form is not quite user friendly yet. For example, when you press next
after you enter your email address, the keyboard will disappear and the password field is not focused. To address this issue, you need to do a little bit of work. You need to attach ref
into your text field components. Then, you can call .focus()
function to programmatically focus on a particular text input. Update your App.tsx
with the following code.import React from 'react';
import {
Alert,
Keyboard,
KeyboardAvoidingView,
Platform,
Pressable,
SafeAreaView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from 'react-native';
// Modules
import { Controller, useForm } from 'react-hook-form';
// Components
import SizedBox from './src/components/SizedBox';
interface FormData {
email: string;
password: string;
}
function useStyles() {
return StyleSheet.create({
button: {
alignItems: 'center',
backgroundColor: 'rgb(93, 95, 222)',
borderRadius: 8,
height: 48,
justifyContent: 'center',
},
buttonTitle: {
color: '#FFFFFF',
fontSize: 17,
fontWeight: '600',
lineHeight: 22,
},
content: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 16,
paddingVertical: 32,
},
forgotPasswordContainer: {
alignItems: 'flex-end',
},
form: {
alignItems: 'center',
backgroundColor: 'rgb(58, 58, 60)',
borderRadius: 8,
flexDirection: 'row',
height: 48,
paddingHorizontal: 16,
},
label: {
color: 'rgba(235, 235, 245, 0.6)',
fontSize: 15,
fontWeight: '400',
lineHeight: 20,
width: 80,
},
root: {
backgroundColor: '#000000',
flex: 1,
},
safeAreaView: {
flex: 1,
},
subtitle: {
color: 'rgba(235, 235, 245, 0.6)',
fontSize: 17,
fontWeight: '400',
lineHeight: 22,
},
textButton: {
color: '#FFFFFF',
fontSize: 15,
fontWeight: '400',
lineHeight: 20,
},
textInput: {
color: '#FFFFFF',
flex: 1,
},
title: {
color: '#FFFFFF',
fontSize: 28,
fontWeight: '700',
lineHeight: 34,
},
});
}
const App: React.FC = () => {
const emailInput = React.useRef<TextInput>(null);
const passwordInput = React.useRef<TextInput>(null);
const { control, handleSubmit } = useForm<FormData>({
defaultValues: {
email: '',
password: '',
},
});
const onSubmit = handleSubmit(({ email, password }) => {
Alert.alert('Data', `Email: ${email}\nPassword: ${password}`);
});
const styles = useStyles();
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.root}>
<SafeAreaView style={styles.safeAreaView}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.content}
>
<Text style={styles.title}>Welcome back!</Text>
<SizedBox height={8} />
<Text style={styles.subtitle}>Sign in to your account</Text>
<SizedBox height={32} />
<Pressable onPress={() => emailInput.current?.focus()}>
<View style={styles.form}>
<Text style={styles.label}>Email</Text>
<Controller
control={control}
name="email"
render={({ onBlur, onChange, value }) => (
<TextInput
autoCapitalize="none"
autoCompleteType="email"
autoCorrect={false}
keyboardType="email-address"
onBlur={onBlur}
onChangeText={onChange}
onSubmitEditing={() => passwordInput.current?.focus()}
ref={emailInput}
returnKeyType="next"
style={styles.textInput}
textContentType="username"
value={value}
/>
)}
/>
</View>
</Pressable>
<SizedBox height={16} />
<Pressable onPress={() => passwordInput.current?.focus()}>
<View style={styles.form}>
<Text style={styles.label}>Password</Text>
<Controller
control={control}
name="password"
render={({ onBlur, onChange, value }) => (
<TextInput
autoCapitalize="none"
autoCompleteType="password"
autoCorrect={false}
onBlur={onBlur}
onChangeText={onChange}
onSubmitEditing={onSubmit}
ref={passwordInput}
returnKeyType="done"
secureTextEntry
style={styles.textInput}
textContentType="password"
value={value}
/>
)}
/>
</View>
</Pressable>
<SizedBox height={16} />
<View style={styles.forgotPasswordContainer}>
<Text style={styles.textButton}>Forgot password?</Text>
</View>
<SizedBox height={16} />
<TouchableOpacity onPress={onSubmit}>
<View style={styles.button}>
<Text style={styles.buttonTitle}>Continue</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
</View>
</TouchableWithoutFeedback>
);
};
export default App;
Copyright © 2020 William Kurniawan. All rights reserved.