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: