The React Native examples you've worked with so far in this book have used plain text to act as buttons or links. In web applications, it's pretty easy to make text look like something that can be clicked—you just wrap it with the appropriate link. There's no such thing as mobile links, so you can style your text to look like a button.
So, let's style some text as a button. This is a great first step, making the text look touchable. But, we also want to give visual feedback to the user when they start interacting with the button. React Native provides two components to help with this: TouchableOpacity
and TouchableHighlight
. But before we dive into the code, let's take a look at what these components look like visually when users interact with them, starting with TouchableOpacity
:
There's two buttons rendered here, and the top one labeled Opacity is currently being pressed by the user. The opacity of the button is dimmed when pressed, which provides important visual feedback for the user. Let's see what the Highlight button looks like when pressed:
Instead of changing the opacity when pressed, the TouchableHighlight
component adds a highlight layer over the button. In this case, we're highlighting it using a more transparent version of the slate grey used in the font and border colors.
Which approach you use doesn't really matter. The important thing is that you provide the appropriate touch feedback for your users as they interact with your buttons. In fact, you might want to use the two approaches in the same app, but for different things. Let's create a Button
component makes it easy to use either approach:
import React, { PropTypes } from 'react'; import { Text, TouchableOpacity, TouchableHighlight, } from 'react-native'; import styles from './styles'; // The "touchables" map is used to get the right // component to wrap around the button. The // "undefined" key represents the default. const touchables = new Map([ ['opacity', TouchableOpacity], ['highlight', TouchableHighlight], [undefined, TouchableOpacity], ]); const Button = ({ label, onPress, touchable, }) => { // Get's the "Touchable" component to use, // based on the "touchable" property value. const Touchable = touchables.get(touchable); // Properties to pass to the "Touchable" // component. const touchableProps = { style: styles.button, underlayColor: 'rgba(112,128,144,0.3)', onPress, }; // Renders the "<Text>" component that's // styled to look like a button, and is // wrapped in a "<Touchable>" component // to properly handle user interactions. return ( <Touchable {...touchableProps}> <Text style={styles.buttonText}> {label} </Text> </Touchable> ); }; Button.propTypes = { onPress: PropTypes.func.isRequired, label: PropTypes.string.isRequired, touchable: PropTypes.oneOf([ 'opacity', 'highlight', ]), }; export default Button;
As you can see, the touchables
map is used to determine which React Native touchable component wraps the text, based on the touchable
property value. Here's the styles used to create this button:
button: { padding: 10, margin: 5, backgroundColor: 'azure', borderWidth: 1, borderRadius: 4, borderColor: 'slategrey', }, buttonText: { color: 'slategrey', }
Here's how we put those buttons to use in the main app module:
import React from 'react'; import { AppRegistry, View, } from 'react-native'; import styles from './styles'; import Button from './Button'; const TouchFeedback = () => ( <View style={styles.container}> { /* Renders a "<Button>" that uses "TouchableOpacity" to handle user gestures, since that is the default */ } <Button onPress={() => {}} label="Opacity" /> { /* Renders a "<Button>" that uses "TouchableHighlight" to handle user gestures. */ } <Button onPress={() => {}} label="Highlight" touchable="highlight" /> </View> ); AppRegistry.registerComponent( 'TouchFeedback', () => TouchFeedback );
Note that the onPress
callbacks don't actually do anything, but they are called, and we're passing them because they're a required property.