- React Native Cookbook
- Dan Ward
- 1066字
- 2025-04-04 14:57:29
How to do it...
- We'll start by opening App.js and adding the imports we'll be using:
import React from 'react'; import { Dimensions, StyleSheet, Text, View } from 'react-native';
- Next, we'll add the empty App class for the component, along with some basic styles:
export default class App extends React.Component { } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff' }, text: { fontSize: 40, } });
- With the shell of our app in place, we can now add the render method. In the render method, you'll notice we've got a View component using the onLayout property, which will fire off whenever the orientation of the device changes. The onLayout will then run this.handleLayoutChange, which we will define in the next step. In the Text element, we simply display the value of orientation on the state object:
export default class App extends React.Component { render() { return ( <View onLayout={() => this.handleLayoutChange} style={styles.container} > <Text style={styles.text}>
{this.state.orientation}
</Text> </View> ); } }
- Let's create the handleLayoutChange method of our component, as well as the getOrientation function that the handleLayoutChange method calls. The getOrientation function uses the React Native Dimensions utility to get the width and height of the screen. If height > width, we know that the device is in portrait orientation, and if not, then it is in landscape orientation. By updating state, a re-render will be initiated, and the value of this.state.orientation will reflect the orientation:
handleLayoutChange() {
this.getOrientation();
}
getOrientation() { const { width, height } = Dimensions.get('window'); const orientation = height > width ? 'Portrait' : 'Landscape'; this.setState({ orientation }); }
- If we run the app at this point, we'll get the error TypeError: null is not an object: (evaluating 'this.state.orientation'). This happens because the render method is attempting to read from the this.state.orientation value before it's even been defined. We can easily fix this problem by getting the orientation before render runs for the first time, via the React life cycle componentWillMount hook:
componentWillMount() {
this.getOrientation();
}
- That's all it takes to get the basic functionality we're looking for! Run the app again and you should see the displayed text reflect the orientation of the device. Rotate the device, and the orientation text should update:

- Now that the orientation state value is updating properly, we can focus on the UI. As mentioned before, we will create a menu that renders the options slightly differently based on the current orientation. Let's import a Menu component, which we'll build out in the next steps, and update the render method of our App component to use the new Menu component. Notice that we are now passing this.state.orientation to the orientation property of the Menu component:
import Menu from './Menu'; export default class App extends React.Component { // ... render() { return ( <View onLayout={() => {this.handleLayoutChange()}} style={styles.container} > <Menu orientation={this.state.orientation} /> <View style={styles.main}> <Text>Main Content</Text> </View> </View> ); } }
- Let's also update the styles for our App component. You can replace the styles from step 2 with the following code. By setting the flexDirection to row on the container styles, we'll be able to display the two components horizontally:
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
main: {
flex: 1,
backgroundColor: '#ecf0f1',
justifyContent: 'center',
alignItems: 'center',
}
});
- Next, let's build out the Menu component. We'll need to create a new /Menu/index.js file, which will define the Menu class. This component will receive the orientation property and decide how to render the menu options based on the orientation value. Let's start by importing the dependencies for this class:
import React, { Component } from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
- Now we can define the Menu class. On the state object, we will define an array of options. These option objects will be used to define the icons. As discussed in the Using font icons recipe in the previous chapter we can define icons via keywords, as defined in the vector-icon directory, found at https://expo.github.io/vector-icons/:
export default class Menu extends Component {
state = {
options: [
{title: 'Dashboard', icon: 'dashboard'},
{title: 'Inbox', icon: 'inbox'},
{title: 'Graphs', icon: 'pie-chart'},
{title: 'Search', icon: 'search'},
{title: 'Settings', icon: 'gear'},
],
};
// Remainder defined in following steps
}
- The render method for this component loops through the array of options in the state object:
render() {
return (
<View style={styles.content}>
{this.state.options.map(this.renderOption)}
</View>
);
}
- As you can see, inside the JSX in the last step, there's a call to renderOption. In this method, we are going to render the icon and the label for each option. We'll also use the orientation value to toggle showing the label, and to change the icon's size:
renderOption = (option, index) => {
const isLandscape = this.properties.orientation === 'Landscape';
const title = isLandscape
? <Text style={styles.title}>{option.title}</Text>
: null;
const iconSize = isLandscape ? 27 : 35;
return (
<View key={index} style={[styles.option, styles.landscape]}>
<FontAwesome name={option.icon} size={iconSize} color="#fff" />
{title}
</View>
);
}
In the previous code block, notice that we are defining a key property. When dynamically creating a new component, we always need to set a key property. This property should be unique for each item, since it's used internally by React. In this case, we are using the index of the loop iteration. This way, we can be assured that every item will have a unique key value since the data is static. You can read more about it in the official documentation at https://reactjs.org/docs/lists-and-keys.html.
- Finally, we'll define the styles for the menu. First, we will set the backgroundColor to dark blue, and then, for each option, we'll change the flexDirection to render the icon and label horizontally. The rest of the styles add margins and paddings so that the menu items are nicely spaced apart:
const styles = StyleSheet.create({
content: {
backgroundColor: '#34495e',
paddingTop: 50,
},
option: {
flexDirection: 'row',
paddingBottom: 15,
},
landscape: {
paddingRight: 30,
paddingLeft: 30,
},
title: {
color: '#fff',
fontSize: 16,
margin: 5,
marginLeft: 20,
},
});
- If we run our application now, it will display the menu UI differently depending on the orientation of the screen. Rotate the device, and the layout will automatically update:
