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: