Main React Native UI Components

You might have noticed that in the render method we use special tags/elements like <View> and <Text> instead of <div> or <p>. Those special elements or React Native components come from the react-native library. There are whole bunch of them in there, and I’m sure more will come soon. There are components specific to iOS and Android as well as synthetic ones that would work across platforms. Typically, iOS-only components have IOS at the end of their name (for example, NavigatorIOS) while the universal cross-platform components don’t have such endings (for example, Navigator).

Describing all the React Native components would take a book on its own. Also, as I’ve said before, the community and Facebook developers themselves constantly and relentlessly add new components and update existing ones. It’s better to refer to the official documentation for the full up-to-date list of supported components. However, to be able to develop minimal mobile apps with React Native, you’ll need to learn the main (in my mind) components. They are:

All these components were selected because knowing them will provide you with the bare minimum to build somewhat useful apps, as you’ll see in the Timer and Weather App projects. Also, these components are universal; that is, you can (and should) use them for iOS and Android. Maybe you can even use the same code base for index.ios.js and index.android.js.

For this section of the book, I’ll be using code snippets from the Timer and Weather App projects to make the examples more realistic than just some foo-bars. The code for Timer is in timer. The code for Weather App is in weather.

View

As I mentioned before, View is the most basic component. If you don’t know what to use, then use View. You can wrap multiple other components in a View, similarly to wrapping them in a <div>, because render() must return only a single element. For example, to output the number of seconds left and a label underneath it, wrap them in a View:

 1 var Timer = React.createClass({
 2    render() {
 3      // ...
 4      return (
 5        <View>
 6          <Text style={styles.heading}>{this.props.time}</Text>
 7          <Text>Seconds left</Text>
 8        </View>
 9      )
10     }
11 })

Text

The Text component is for rendering text. Like most of the other components, we can supply it with styles. For example, this Text element is using Flex and has a font size of 36, padding on top of 40, and a margin of 10:

 1 var TimerWrapper = React.createClass({
 2   // ...
 3   render() {
 4     return (
 5       <ScrollView>
 6         <View style={styles.container}>
 7           <Text style={styles.heading}>Timer</Text>
 8          ...
 9         </View>
10       </ScrollView>
11     )
12   }
13 })
14 
15 var styles = StyleSheet.create({
16   ...
17   heading: {
18     flex: 1,
19     fontSize: 36,
20     paddingTop: 40,
21     margin: 10
22   },
23   ...
24 })

The result is shown in Figure 1.

Figure 1: Timer text
Figure 1: Timer text

Conveniently, we can combine two or more style objects in the style property using an array. For example, this Text element uses styles from navBarText and navBarButtonText:

1         <Text style={[styles.navBarText, styles.navBarButtonText, ]}>
2           {'<'} {previousRoute.name}
3         </Text>

The style attribute and combining of styles are not exclusive to Text. You can apply them to other components.

TextInput

TextInput is an input field component. You would typically use it in forms to capture user input such as email address, password, name, etc. This component has some familiar properties, such as:

Other attributes are specific to React Native. The main ones are:

The full list of up-to-date properties for TextInput for iOS and Android is at https://facebook.github.io/react-native/docs/textinput.html#props.

Consider this example, which renders a city name input field with the handler this.search. The button on the keyboard will say Search, the value is assigned to the state (a controlled component!), and the placeholder is San Francisco:

1   <TextInput
2     placeholder="San Francisco"
3     value={this.state.cityName}
4     returnKeyType="search"
5     enablesReturnKeyAutomatically={true}
6     onChangeText={this.handleCityName}
7     onEndEditing={this.search}
8     style={styles.textInput}/>

The result is shown in Figure 2, where you can observe the Search key on the virtual keyboard.

Figure 2: Search city by name with a virtual keyboard
Figure 2: Search city by name with a virtual keyboard

With the onChangeText property, we get the value of the input field as the argument to the handler function (handleCityName(event)). For example, to process the name of the city and set the state of cityName in a controlled component, we need to implement handleCityName like this:

1   ...
2   handleCityName(cityName) {
3     this.setState({ cityName: cityName})
4   },
5   ...

On the other hand, if you need more than text, there’s onChange. When the event comes to the onChange handler function, the event argument has a property called nativeEvent, and this property in turn has a property called text. You can implement the onChange handler like this:

 1 ...
 2 onNameChanged: function(event) {
 3   this.setState({ name: event.nativeEvent.text });
 4 },
 5 ...
 6 render() {
 7   return (
 8     <TextInput onChange={this.onNameChange} ... />
 9   )
10 }
11 })

ScrollView

This is an enhanced version of the View component. It allows for the content to be scrollable, so you can scroll up and down with touch gestures. This is useful when the content won’t fit on one screen. For example, I can use ScrollView as the root of my render() because I know that timerOptions can be a very large array, thus rendering many rows of data (Button components):

 1 var TimerWrapper = React.createClass({
 2   // ...
 3   render() {
 4     return (
 5       <ScrollView>
 6         <View style={styles.container}>
 7           <Text style={styles.heading}>Timer</Text>
 8           <Text style={styles.instructions}>Press a button</Text>
 9           <View style={styles.buttons}>
10             {timerOptions.map((item, index, list)=>{
11               return <Button key={index} time={item} startTimer={this.startTimer\
12 } isMinutes={this.state.isMinutes}/>
13             })}
14           </View>
15 		  ...
16         </View>
17       </ScrollView>
18     )
19   }
20 })

ListView

ListView is a view that renders a list of rows from the data provided. In most cases, you want to wrap a ListView in a ScrollView. The data must be in a certain format. Use dataSource = new ListView.DataSource() to create the data source object, then use dataSource.cloneWithRows(list) to populate the data source with data from a standard JavaScript array.

Here is an example. First we create the data source object:

1 let dataSource = new ListView.DataSource({
2   rowHasChanged: (row1, row2) => row1 !== row2
3 })

Then we use the cloneWithRows method to fill in the data from an array, response.list:

1 this.props.navigator.push({
2   name: 'Forecast',
3   component: Forecast,
4   passProps: {
5     forecastData: dataSource.cloneWithRows(response.list),
6     forecastRaw: response
7   }
8 })

Ignore the navigator call for now. It’s coming up later in the chapter.

We have the data, so now let’s render the ListView by providing the properties dataSource and renderRow. For example, this is the list of forecast info, with each row being a forecast on a certain day. The ListView’s parent is ScrollView:

 1 module.exports = React.createClass({
 2   render: function() {
 3     return (
 4       <ScrollView style={styles.scroll}>
 5         <Text style={styles.text}>{this.props.forecastRaw.city.name}</Text>
 6         <ListView dataSource={this.props.forecastData} renderRow={ForecastRow} s\
 7 tyle={styles.listView}/>
 8       </ScrollView>
 9     )
10   }
11 })

As you can guess, renderRow, which is ForecastRow in this example, is another component that is responsible for rendering an individual item from the data source provided. If there are no methods or states, you can create a stateless component (more on stateless components in chapter 10). In the ForecastRow, we output the date (dt_txt), description (description), and temperature (temp):

 1 const ForecastRow = (forecast)=> {
 2   return (
 3     <View style={styles.row}>
 4       <View style={styles.rightContainer}>
 5         <Text style={styles.subtitle}></Text>
 6         <Text style={styles.subtitle}>
 7           {forecast.dt_txt}: {forecast.weather[0].description}, {forecast.main.t\
 8 emp}
 9         </Text>
10        </View>
11     </View>
12   )
13 }

You can achieve the functionality of ListView with a simple Array.map() construct. In this case, there’s no need for a data source.

TouchableHighlight

TouchableHighlight captures user touch events. Developers implement buttons akin to anchor (<a>) tags in web development. The action is passed as the value of the onPress property. To implement a button, we also need to put some text inside of it.

For example, this is a button that triggers startTimer and has text that consists of the time property and either the word “minutes” or “seconds”:

 1 var Button = React.createClass({
 2   startTimer(event) {
 3     // ...
 4   },
 5   render() {
 6     return (
 7       <TouchableHighlight onPress={this.startTimer}>
 8         <Text style={styles.button}>{this.props.time} {(this.props.isMinutes) ? \
 9 'minutes' : 'seconds'}</Text>
10       </TouchableHighlight>
11     )
12   }
13 })

The style of TouchableHighlight by itself is nothing; for this reason, when we implement buttons we either style the text inside of the TouchableHighlight (Figure 3) or use an image with the Image component.

Figure 3: iOS buttons implemented with TouchableHighlight
Figure 3: iOS buttons implemented with TouchableHighlight

Similar components to TouchableHighlight are:

Switch

You’ve probably seen and used the Switch component or a similar native element many times. A visual example is shown in Figure 9-X. It’s a small toggle that is not dissimilar to a checkbox. This is a Boolean on/off input element that comes in handy in forms and app settings.

Figure 4: Switch control on iOS
Figure 4: Switch control on iOS

When implementing Switch, you provide at least two properties, onValueChange and value (a controlled component again!). For example, this toggle makes the apps save the city name, or not:

1       ...
2         <Text>Remember?</Text>
3         <Switch onValueChange={this.toggleRemember} value={this.state.isRemember\
4 }></Switch>
5       ....

In the handler toggleRemember, I set the state to the value that is the opposite of the current this.state.isRemember:

1   // ...
2   toggleRemember() {
3     this.setState({ isRemember: !this.state.isRemember}, ()=>{
4       // Remove the city name from the storage
5       if (!this.state.isRemember) this.props.storage.removeItem('cityName')
6     })
7   },
8   // ...

Navigator

Navigator is a highly customizable navigation component to enable navigation between screens in the app. We can use it to implement a navigation bar and/or a breadcrumbs navigation bar. A navigation bar is a menu at the top of the screen with buttons and a title.

There’s also NavigatorIOS, which is not used by Facebook and therefore not officially supported and maintained by the community. NavigatorIOS has a built-in navigation bar, but it works only for iOS development. Another drawback is that NavigatorIOS won’t refresh routes/screens when the properties to those routes change. Conversely, Navigator can be used on iOS and Android, and it refreshes the routes on the change of the properties passed to them. You can customize navigation bars to your liking.

Because Navigator is flexible, I found a few ways to implement it. There is a method where you have a route stack and then navigate by using route IDs and forward/back methods. I settled on this pattern, which uses abstraction and the NavigatorIOS interface (passProps). Let’s say the App component is the one you register with AppRegistry. Then you want to render the Navigator in App’s render method:

 1 const App = React.createClass({
 2   render() {
 3     return (
 4       <Navigator
 5         initialRoute={{
 6           name: 'Search',
 7           index: 0,
 8           component: Search,
 9           passProps: {
10             storage: storage
11           }
12         }}
13         ref='navigator'
14         navigationBar={
15           <Navigator.NavigationBar
16             routeMapper={NavigationBarRouteMapper}
17             style={styles.navBar}
18           />
19         }
20         renderScene={(route, navigator) => {
21           let props = route.passProps
22           props.navigator = navigator
23           props.name = route.name
24           return React.createElement(route.component, props)
25         }}
26       />
27     )
28   }
29 })

You can observe several attributes of Navigator:

To navigate to a new screen like Forecast (Forecast component) and pass properties to it, invoke navigator.push():

 1 		// ...
 2         this.props.navigator.push({
 3           name: 'Forecast',
 4           component: Forecast,
 5           passProps: {
 6             forecastData: dataSource.cloneWithRows(response.list),
 7             forecastRaw: response
 8           }
 9         })
10         // ...

In this example, I’m passing the component and props with each push() call. If you’re using a route stack, which is basically a list of components, then you can pass only an ID or the name of a component, not the entire object, and get the object from the stack. As usual, there’s more than one way to skin a catfish.