Bootcamp Notes – Day 4 (Mon) – React Native: Week 2: React Native UI Components and Redux

React Native UI Components and Redux

You will learn to add more UI components, starting with adding icons to StackNavigator headers and customizing the side drawer. You will also add a way to mark individual campsites as a favorite with a heart icon. We will start using Redux to manage application state, adding, action types, action creators, reducers, and the store.

We will also use json-server again to serve date and image files, and use Redux Thunk and Fetch API to communicate with server.

Will also setup ActivityIndicator to let users know when assets are loading, and also notify users when a server request has resulted in an error.

We will also learn a couple of different ways to debug your application in React Native using the standalone React Devtools and Expo Developer Menu to access a view of the components, just as you did with React dev tools browser extension in the React course.


Exercise: Icons, Favorites, and Comments

  • Display the comments for each campsite in the CampsiteInfo screen.
  • Use the Icon component from React Native Elements to create a Favorite icon button that can be pressed to change the icon’s appearance.

CampsiteInfoComponent.js

import React, { Component } from ‘react’;
import { Text, View, ScrollView, FlatList } from ‘react-native’;
import { Card, Icon } from ‘react-native-elements’;
import { CAMPSITES } from ‘../shared/campsites’;
import { COMMENTS } from ‘../shared/comments’;
function RenderCampsite(props) {
    const {campsite} = props;
    if (campsite) {
        return (
            <Card 
                featuredTitle={campsite.name}
                image={require(‘./images/react-lake.jpg’)}
            >
                <Text style={{margin: 10}}>
                    {campsite.description}
                </Text>
                <Icon
                    name={props.favorite ? ‘heart’ : ‘heart-o’}
                    type=’font-awesome’
                    color=’#f50′
                    raised
                    reverse
                    onPress={() => props.favorite ? 
                        console.log(‘Already set as a favorite’) : props.markFavorite()}
                />
            </Card>
        );
    }
    return <View />;
}
function RenderComments({comments}) {
    const renderCommentItem = ({item}) => {
        return (
            <View style={{margin: 10}}>
                <Text style={{fontSize: 14}}>{item.text}</Text>
                <Text style={{fontSize: 12}}>{item.rating} Stars</Text>
                <Text style={{fontSize: 12}}>{`– ${item.author}, ${item.date}`}</Text>
            </View>
        );
    };
    return (
        <Card title=’Comments’>
            <FlatList
                data={comments}
                renderItem={renderCommentItem}
                keyExtractor={item => item.id.toString()}
            />
        </Card>
    );
}
class CampsiteInfo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            campsites: CAMPSITES,
            comments: COMMENTS,
            favorite: false
        };
    }
    markFavorite() {
        this.setState({favorite: true});
    }
    static navigationOptions = {
        title: ‘Campsite Information’
    }
    render() {
        const campsiteId = this.props.navigation.getParam(‘campsiteId’);
        const campsite = this.state.campsites.filter(campsite => campsite.id === campsiteId)[0];
        const comments = this.state.comments.filter(comment => comment.campsiteId === campsiteId);
        return (
            <ScrollView>
                <RenderCampsite campsite={campsite}
                    favorite={this.state.favorite}
                    markFavorite={() => this.markFavorite()}
                />
                <RenderComments comments={comments} />
            </ScrollView>
        );
    }
}
export default CampsiteInfo;

Exercise: Navigation Icons and Custom Side Drawer

  • Add icons to your Drawer and Stack Navigators
  • Customize the side drawer in the Drawer Navigation.

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 { 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’;
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 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}
                    />
                )
            }
        },
        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 {
    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 Main;

Additional Notes: