Bootcamp Notes – Day 6 (Wed) – React Native: Week 2: Forms and Modals
React Native Forms and Modals
The first thing you need to understand about Forms in React Native, that there is no direct equivalent to the HTML <form> element. In React, you still had to use components (such as Form and LocalForm) that would render to HTML <form> element in the browser DOM – this is not necessary in React Native.
You will still use components that act similarly to HTML elements that take input from the user:
For example: Choose Yes/No, True/False, On/Off, etc:
- HTML: Radio button
- React Native: Switch component
For example: Choose from multiple options:
- HTML: <option> and <select>
- React Native: Picker and Picker.Item
For example: Enter text:
- HTML: <input>
- React Native: TextInput
- Learn to create a form using these input components: Switch, Picker, and DateTimePicker.
- Use the local component state to store the form data, and write an event handler to handle the form submission.
- Add the new component into the drawer and stack navigation for your application.
- First, install DateTimePicker from React Native Community. To do this, we need to use what’s called a scoped package syntax, which means we use an @ symbol, the name of the community, a forward slash, then the name of the package, as follows: expo install @react-native-community/datetimepicker
Add a new file named ReservationComponent.js and give it the following content:
ReservationComponent.js
import React, { Component } from 'react';
import { Text, View, ScrollView, StyleSheet,
Picker, Switch, Button } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
class Reservation extends Component {
constructor(props) {
super(props);
this.state = {
campers: 1,
hikeIn: false,
date: new Date(),
showCalendar: false
};
}
static navigationOptions = {
title: 'Reserve Campsite'
}
handleReservation() {
console.log(JSON.stringify(this.state));
this.setState({
campers: 1,
hikeIn: false,
date: new Date(),
showCalendar: false
});
}
render() {
return (
<ScrollView>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Number of Campers</Text>
<Picker
style={styles.formItem}
selectedValue={this.state.campers}
onValueChange={itemValue => this.setState({campers: itemValue})}
>
<Picker.Item label='1' value='1' />
<Picker.Item label='2' value='2' />
<Picker.Item label='3' value='3' />
<Picker.Item label='4' value='4' />
<Picker.Item label='5' value='5' />
<Picker.Item label='6' value='6' />
</Picker>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Hike-In?</Text>
<Switch
style={styles.formItem}
value={this.state.hikeIn}
trackColor={{true: '#5637DD', false: null}}
onValueChange={value => this.setState({hikeIn: value})}
/>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Date</Text>
<Button
onPress={() =>
this.setState({showCalendar: !this.state.showCalendar})
}
title={this.state.date.toLocaleDateString('en-US')}
color='#5637DD'
accessibilityLabel='Tap me to select a reservation date'
/>
</View>
{this.state.showCalendar && (
<DateTimePicker
value={this.state.date}
mode={'date'}
display='default'
onChange={(event, selectedDate) => {
selectedDate && this.setState({date: selectedDate, showCalendar: false});
}}
style={styles.formItem}
/>
)}
<View style={styles.formRow}>
<Button
onPress={() => this.handleReservation()}
title='Search'
color='#5637DD'
accessibilityLabel='Tap me to search for available campsites to reserve'
/>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
formRow: {
alignItems: 'center',
justifyContent: 'center',
flex: 1,
flexDirection: 'row',
margin: 20
},
formLabel: {
fontSize: 18,
flex: 2
},
formItem: {
flex: 1
}
});
export default Reservation;
MainComponent.js
import React, { Component } from ‘react’;
import Home from ‘./HomeComponent’;
import Directory from ‘./DirectoryComponent’;
import CampsiteInfo from ‘./CampsiteInfoComponent’;
import About from ‘./AboutComponent’;
import Contact from ‘./ContactComponent’;
import Reservation from ‘./ReservationComponent’;
import { View, Platform, StyleSheet, Text, ScrollView, Image } from ‘react-native’;
import { createStackNavigator } from ‘react-navigation-stack’;
import { createDrawerNavigator, DrawerItems } from ‘react-navigation-drawer’;
import { createAppContainer } from ‘react-navigation’;
import { Icon } from ‘react-native-elements’;
import SafeAreaView from ‘react-native-safe-area-view’;
import { connect } from ‘react-redux’;
import { fetchCampsites, fetchComments, fetchPromotions, fetchPartners } from ‘../redux/ActionCreators’;
const mapDispatchToProps = {
fetchCampsites,
fetchComments,
fetchPromotions,
fetchPartners
};
const DirectoryNavigator = createStackNavigator(
{
Directory: {
screen: Directory,
navigationOptions: ({navigation}) => ({
headerLeft: <Icon
name=’list’
type=’font-awesome’
iconStyle={styles.stackIcon}
onPress={() => navigation.toggleDrawer()}
/>
})
},
CampsiteInfo: { screen: CampsiteInfo }
},
{
initialRouteName: ‘Directory’,
defaultNavigationOptions: {
headerStyle: {
backgroundColor: ‘#5637DD’
},
headerTintColor: ‘#fff’,
headerTitleStyle: {
color: ‘#fff’
}
}
}
);
const HomeNavigator = createStackNavigator(
{
Home: { screen: Home }
},
{
defaultNavigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: ‘#5637DD’
},
headerTintColor: ‘#fff’,
headerTitleStyle: {
color: ‘#fff’
},
headerLeft: <Icon
name=’home’
type=’font-awesome’
iconStyle={styles.stackIcon}
onPress={() => navigation.toggleDrawer()}
/>
})
}
);
const AboutNavigator = createStackNavigator(
{
About: { screen: About }
},
{
defaultNavigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: ‘#5637DD’
},
headerTintColor: ‘#fff’,
headerTitleStyle: {
color: ‘#fff’
},
headerLeft: <Icon
name=’info-circle’
type=’font-awesome’
iconStyle={styles.stackIcon}
onPress={() => navigation.toggleDrawer()}
/>
})
}
);
const ContactNavigator = createStackNavigator(
{
Contact: { screen: Contact }
},
{
defaultNavigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: ‘#5637DD’
},
headerTintColor: ‘#fff’,
headerTitleStyle: {
color: ‘#fff’
},
headerLeft: <Icon
name=’address-card’
type=’font-awesome’
iconStyle={styles.stackIcon}
onPress={() => navigation.toggleDrawer()}
/>
})
}
);
const ReservationNavigator = createStackNavigator(
{
Reservation: { screen: Reservation }
},
{
defaultNavigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: ‘#5637DD’
},
headerTintColor: ‘#fff’,
headerTitleStyle: {
color: ‘#fff’
},
headerLeft: <Icon
name=’tree’
type=’font-awesome’
iconStyle={styles.stackIcon}
onPress={() => navigation.toggleDrawer()}
/>
})
}
);
const CustomDrawerContentComponent = props => (
<ScrollView>
<SafeAreaView
style={styles.container}
forceInset={{top: ‘always’, horizontal: ‘never’}}>
<View style={styles.drawerHeader}>
<View style={{flex: 1}}>
<Image source={require(‘./images/logo.png’)} style={styles.drawerImage} />
</View>
<View style={{flex: 2}}>
<Text style={styles.drawerHeaderText}>NuCamp</Text>
</View>
</View>
<DrawerItems {…props} />
</SafeAreaView>
</ScrollView>
);
const MainNavigator = createDrawerNavigator(
{
Home: {
screen: HomeNavigator,
navigationOptions: {
drawerIcon: ({tintColor}) => (
<Icon
name=’home’
type=’font-awesome’
size={24}
color={tintColor}
/>
)
}
},
Directory: {
screen: DirectoryNavigator,
navigationOptions: {
drawerIcon: ({tintColor}) => (
<Icon
name=’list’
type=’font-awesome’
size={24}
color={tintColor}
/>
)
}
},
Reservation: {
screen: ReservationNavigator,
navigationOptions: {
drawerLabel: ‘Reserve Campsite’,
drawerIcon: ({tintColor}) => (
<Icon
name=’tree’
type=’font-awesome’
size={24}
color={tintColor}
/>
)
}
},
About: {
screen: AboutNavigator,
navigationOptions: {
drawerLabel: ‘About Us’,
drawerIcon: ({tintColor}) => (
<Icon
name=’info-circle’
type=’font-awesome’
size={24}
color={tintColor}
/>
)
}
},
Contact: {
screen: ContactNavigator,
navigationOptions: {
drawerLabel: ‘Contact Us’,
drawerIcon: ({tintColor}) => (
<Icon
name=’address-card’
type=’font-awesome’
size={24}
color={tintColor}
/>
)
}
}
},
{
drawerBackgroundColor: ‘#CEC8FF’,
contentComponent: CustomDrawerContentComponent
}
);
const AppNavigator = createAppContainer(MainNavigator)
class Main extends Component {
componentDidMount() {
this.props.fetchCampsites();
this.props.fetchComments();
this.props.fetchPromotions();
this.props.fetchPartners();
}
render() {
return (
<View style={{
flex: 1,
paddingTop: Platform.OS === ‘ios’ ? 0 : Expo.Constants.statusBarHeight
}}>
<AppNavigator />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
drawerHeader: {
backgroundColor: ‘#5637DD’,
height: 140,
alignItems: ‘center’,
justifyContent: ‘center’,
flex: 1,
flexDirection: ‘row’
},
drawerHeaderText: {
color: ‘#fff’,
fontSize: 24,
fontWeight: ‘bold’
},
drawerImage: {
margin: 10,
height: 60,
width: 60
},
stackIcon: {
marginLeft: 10,
color: ‘#fff’,
fontSize: 24
}
});
export default connect(null, mapDispatchToProps)(Main);
Modals
- Add a Modal component that can be opened from the Reservation component
- Learn to show and hide the modal with an event handler
ReservationComponent.js
import React, { Component } from ‘react’;
import { Text, View, ScrollView, StyleSheet, Picker, Switch, Button, Modal } from ‘react-native’;
import DateTimePicker from ‘@react-native-community/datetimepicker’;
class Reservation extends Component {
constructor(props) {
super(props);
this.state = {
campers: 1,
hikeIn: false,
date: new Date(),
showCalendar: false,
showModal: false
};
}
toggleModal() {
this.setState({showModal: !this.state.showModal});
}
static navigationOptions = {
title: ‘Reserve Campsite’
}
handleReservation() {
console.log(JSON.stringify(this.state));
this.toggleModal();
}
resetForm() {
this.setState({
campers: 1,
hikeIn: false,
date: new Date(),
showCalendar: false,
showModal: false
});
}
render() {
return (
<ScrollView>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Number of Campers</Text>
<Picker
style={styles.formItem}
selectedValue={this.state.campers}
onValueChange={itemValue => this.setState({campers: itemValue})}
>
<Picker.Item label=’1′ value=’1′ />
<Picker.Item label=’2′ value=’2′ />
<Picker.Item label=’3′ value=’3′ />
<Picker.Item label=’4′ value=’4′ />
<Picker.Item label=’5′ value=’5′ />
<Picker.Item label=’6′ value=’6′ />
</Picker>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Hike-In?</Text>
<Switch
style={styles.formItem}
value={this.state.hikeIn}
trackColor={{true: ‘#5637DD’, false: null}}
onValueChange={value => this.setState({hikeIn: value})}
/>
</View>
<View style={styles.formRow}>
<Text style={styles.formLabel}>Date</Text>
<Button
onPress={() =>
this.setState({showCalendar: !this.state.showCalendar})
}
title={this.state.date.toLocaleDateString(‘en-US’)}
color=’#5637DD’
accessibilityLabel=’Tap me to select a reservation date’
/>
</View>
{this.state.showCalendar && (
<DateTimePicker
value={this.state.date}
mode={‘date’}
display=’default’
onChange={(event, selectedDate) => {
selectedDate && this.setState({date: selectedDate, showCalendar: false});
}}
style={styles.formItem}
/>
)}
<View style={styles.formRow}>
<Button
onPress={() => this.handleReservation()}
title=’Search’
color=’#5637DD’
accessibilityLabel=’Tap me to search for available campsites to reserve’
/>
</View>
<Modal
animationType={‘slide’}
transparent={false}
visible={this.state.showModal}
onRequestClose={() => this.toggleModal()}
>
<View style={styles.modal}>
<Text style={styles.modalTitle}>Search Campsite Reservations</Text>
<Text style={styles.modalText}>
Number of Campers: {this.state.campers}
</Text>
<Text style={styles.modalText}>
Hike-In?: {this.state.hikeIn ? ‘Yes’ : ‘No’}
</Text>
<Text style={styles.modalText}>
Date: {this.state.date.toLocaleDateString(‘en-US’)}
</Text>
<Button
onPress={() => {
this.toggleModal();
this.resetForm();
}}
color=’#5637DD’
title=’Close’
/>
</View>
</Modal>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
formRow: {
alignItems: ‘center’,
justifyContent: ‘center’,
flex: 1,
flexDirection: ‘row’,
margin: 20
},
formLabel: {
fontSize: 18,
flex: 2
},
formItem: {
flex: 1
},
modal: {
justifyContent: ‘center’,
margin: 20
},
modalTitle: {
fontSize: 24,
fontWeight: ‘bold’,
backgroundColor: ‘#5637DD’,
textAlign: ‘center’,
color: ‘#fff’,
marginBottom: 20
},
modalText: {
fontSize: 18,
margin: 10
}
});
export default Reservation;
Additional Resources:
- React Native – Switch
- React Native – Picker
- React Native – Button
- React Native – AccessibilityLabel
- NPM – Scoped packages
- React Native Community DateTimePicker
- React Native Community on Github
- React Native – Modal
- React Native – Button