Reader small image

You're reading from  React Native By Example

Product typeBook
Published inApr 2017
Reading LevelIntermediate
PublisherPackt
ISBN-139781786464750
Edition1st Edition
Languages
Right arrow
Author (1)
Richard Kho
Richard Kho
author image
Richard Kho

Richard Kho is a software engineer living in San Francisco. He taught himself how to code in 2014 and has lived a past life as a photographer and cinematographer. He currently works for Capital One and has taught software engineers at Hack Reactor in the past. Richard is also a technical advisor to Code Chrysalis, an advanced software engineering immersive in Tokyo.
Read more about Richard Kho

Right arrow

Chapter 2. Advanced Functionality and Styling the To-Do List App

Having built an MVP for Tasks, our to-do list app, it's time to delve into building out advanced functionality, and styling the application to make it look nice. This chapter will explore the following topics:

  • Utilizing the NavigatorIOS component to build an edit screen to add details to a task
  • Taking in a date and time for tasks to be due with DatePickerIOS
  • Creating a custom collapsible component for our app and utilizing LayoutAnimation to give us fluid transitions
  • Building a Button component for our UI to clear a to-do item's due date
  • Saving the data of an edited task and rendering a due date, if applicable
  • Porting the application over to Android, swapping out DatePickerIOS for DatePickerAndroid and TimePickerAndroid and NavigatorIOS for Navigator, and exploring the control flow in deciding which component is used

Using DatePickerIOS


A key feature in Tasks is the ability to set a reminder for when a task is due. Ideally, our users can set a day as well as a time for when a task should be completed so that they can be reminded of the due date. To accomplish this, we'll use an iOS component named DatePickerIOS. This is a component that renders a date and time selector that we can utilize in our application.

Listed here are the two props that we will be using with our DatePickerIOS component. Other props exist in the React Native documentation in case you are interested:

  • date: This is one of the two required props that track the current selected date. Ideally, this information is stored within the state of the component that renders DatePickerIOS. The date should be an instance of the Date object in JavaScript.
  • onDateChange: This is the other required prop and is fired when a user changes the date or time in the component. It accepts one argument, which is the Date object representing the new date and time...

Using onLayout


In our preceding example, we don't need to specify the height of the DatePickerIOS component when expanded. However, there may be scenarios in which you may need to manually get the dimensions of a component.

To calculate a component's height, we can utilize its onLayout property to fire a callback and then use that callback to save properties passed to the callback. The onLayout property is an event that is invoked on mount and layout changes, giving the event object a nativeEvent object that nests the component's layout properties. Using DatePickerIOS as an example, you can pass a callback to its onLayout prop like this:

<DatePickerIOS 
  date={ this.state.date } 
  onDateChange={ (date) => this._onDateChange(date) } 
  onLayout={ (event) => this._getComponentDimensions(event) } 
  style={ styles.datePicker }  
/> 

 

The event from onLayout gives access to the following property:

event: { 
  nativeEvent: { 
    layout: { 
      x: //some number 
      y: //some number...

Button


Let's build a clear due date button for the EditTask component and only selectively enable it if a due date has been selected for the to-do item. The Button component in React Native should help us render one quickly.

The Button component accepts a couple of props; the following four will be used in our application:

  • color: This is a string (or stringified hex) that sets either the text color on iOS or the background color on Android
  • disabled: This is a Boolean that disables the button if set to true; it defaults to false
  • onPress: This is a callback that is fired when a button is pressed
  • title: This is the text to display within the button

A sample Button component can be rendered like this:

<Button 
  color={ 'blue' } 
  disabled={ this.state.buttonDisabled } 
  onPress={ () => alert('Submit button pressed') } 
  title={ 'Submit' }  
/> 

Modify EditTask so that it has the following features:

  • It contains a Boolean, titled expanded, in its state to control the open/closed status of...

Switch


Switch is a component that renders a Boolean input and allows the user to toggle back and forth.

With Switch, these are the props that we will use:

  • onValueChange: This is a callback that is invoked with the new value of the switch when the value changes
  • value: This is a Boolean that determines whether the switch is set to its 'on' position or not; it defaults to false

A simple Switch component can look like this:

<Switch 
  onValueChange={ (value) =? this.setState({ toggled: value })} 
  value={ this.state.toggled } 
/> 

As stated earlier, Switch has two props that are required: its value and a callback to change its value when toggled.

Using this knowledge, let's make changes to the TasksList component so that it passes the completed, due, formattedDate, and text properties of each row to the EditTask component for use.

Then, make additions to the EditTask component so that it:

  • Expects the completed, due, formattedDate, and text props as part of its propTypes declaration.
  • Contains a...

Save button


In this section, we will create a button in the upper-right corner of the navigation bar that is labeled as Save. When it is tapped on, the following two things must happen:

  • The changes the user made to the to-do item (such as its name, completion status, and due date) must be saved to AsyncStorage, overwriting its previous details
  • The TasksList must be updated so that the user visually sees the changes they made right away

Rendering the Save button is easy with React Native. The object that gets pushed to NavigatorIOS needs to receive the following two key/value pairs:

  • rightButtonTitle: This is a string that renders the text shown in that area
  • onRightButtonPress: This is a callback that is fired when that button is pressed

At face value, this looks simple. However, we can't pass any information to the onRightButtonPress method of NavigatorIOS from a rendered child. Instead, we have to keep a copy of the changes we make inside our TasksList component as well, and update them as the...

TasksListCell modifications


Finally, we want to edit each row rendered by our ListView to display the due date, if one exists.

To do this, we will have to write some conditional logic to show the formatted date, if one is assigned to the to-do item we are rendering. This is also a good time to create a custom styles folder for this component as we will be needing it.

Spend some time creating your version of this feature. My solution is as follows:

// Tasks/app/components/TasksListCell/index.js 

... 
import styles from './styles'; 

You might notice from the above import statement that TasksListCell now imports its StyleSheet.

Add formattedDate to propTypes as an optional string:

export default class TasksListCell extends Component { 
  static propTypes = { 
    ... 
    formattedDate: PropTypes.string, 
  } 
... 
  render () { 
    ... 
    return ( 
      <View style={ styles.tasksListCellContainer }> 
        <TouchableHighlight 
          ... 
        > 
          <View style...

Platform


When your files have such little variance in the differences between their iOS and Android functionalities, it's okay to use the same file. Utilizing the Platform API, we can identify the type of mobile device the user is on and conditionally send them down a specific path.

Import the Platform API along with the rest of your React Native components:

import { Platform } from 'react-native';  

Then call its OS property within a component:

  _platformConditional () { 
    if (Platform.OS === 'ios') { 
      doSomething(); 
    } 

    if (Platform.OS === 'android') { 
      doSomethingElse(); 
    } 
  } 

This lets us control the path our app takes and allows for a little bit of code reuse.

Note

Android-specific filesIf we need to create a file that is supposed to only run on Android devices, simply name it <FILENAME>.android.js, just like the two index files. React Native will know exactly which file to build with, and this lets us create components that are platform-specific when...

DatePickerAndroid and TimePickerAndroid


Setting a time and date on Android is much different from iOS. With iOS, you have a DatePickerIOS component that includes both the date and time. On Android, this is split into two native modals, DatePickerAndroid for the date and TimePickerAndroid for the time. It's not a component to render either, it's an asynchronous function that opens the modal and waits for a natural conclusion before applying logic to it.

To open one of these, wrap an asynchronous function around it:

async renderDatePicker () { 
  const { action, year, month, day } = await DatePickerAndroid.open({ 
    date: new Date() 
  }); 

  if (action === DatePickerAndroid.dismissedAction) { 
    return; 
  } 

  // do something with the year, month, and day here 
} 

Both the DatePickerAndroid and TimePickerAndroid components return an object, and we can grab the properties of each object by using ES6 destructuring assignment, as shown in the preceding snippet.

As these components will render...

Saving updates


As we aren't using a navigation bar with the Android version of the app, we should create a Save button that handles the same save logic.

First, we should modify index.android.js to pass a saveCurrentEditedTask prop to EditTask from the TasksList component:

// index.android.js

... 
class Tasks extends Component { 
  ... 
  _renderScene (route, navigator) { 
    ... 
    if (route.index === 1) { 
      return ( 
        <EditTask 
          ... 
          saveCurrentEditedTask={ route.passProps
          .saveCurrentEditedTask } 
          ... 
        /> 
      ) 
    } 
  } 
} 

Then, modify TasksList to pass the _saveCurrentEditedTask method to EditTask in _renderAndroidEditTaskComponent:

// Tasks/app/components/TasksList/index.js 

... 
export default class TasksList extends Component { 
  ... 
  _renderAndroidEditTaskComponent (rowID) { 
    this.props.navigator.push({ 
      ... 
      passProps: { 
        ... 
        saveCurrentEditedTask: () => 
        this...

BackAndroid


The last thing we need to handle is the back button. A universal back button, either a hardware or software implementation, is found on each Android device. We will need to use the BackAndroid API to detect back button presses and set our own custom functionality. If we don't do this, the back button will automatically close the app each time it is pressed.

To use it, we can add an event listener during the componentWillMount life cycle event that will pop the navigator when a back button press is detected. We can also remove the listener when the component is unmounted.

During componentWillMount, add an event listener to the BackAndroid API for a hardwareButtonPress event, firing _backButtonPress when triggered:

// Tasks/app/components/EditTask/index.android.js 

... 
import { 
  BackAndroid, 
  ... 
} from 'react-native'; 
... 
export default class EditTask extends Component { 
  ... 
  componentWillMount () { 
    BackAndroid.addEventListener('hardwareButtonPress', () => ...

Summary


This was a long chapter! We accomplished a lot of things. First, we used NavigatorIOS to establish custom routes and created a component to edit a to-do item's details, including marking it as completed and adding a due date.

Then, we built a custom, reusable component with fluid animations to expand and collapse a child component, allowing DatePickerIOS to expand and collapse as needed. Afterward, we implemented logic to save the changes we make to a task using the navigation bar.

We also ported our app to support the Android operating system! We started by swapping out NavigatorIOS for Navigator, using the Platform API to trigger conditional logic depending on the type of mobile device our user is on, and creating iOS-and Android-specific components by appending .android and .ios to each index file.

We finished up the port to Android by rendering date and time pickers on Android, which are two separate popups, and creating a save button within our Android-specific EditTask component...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
React Native By Example
Published in: Apr 2017Publisher: PacktISBN-13: 9781786464750
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Richard Kho

Richard Kho is a software engineer living in San Francisco. He taught himself how to code in 2014 and has lived a past life as a photographer and cinematographer. He currently works for Capital One and has taught software engineers at Hack Reactor in the past. Richard is also a technical advisor to Code Chrysalis, an advanced software engineering immersive in Tokyo.
Read more about Richard Kho