View Flattening was Android only optimisation to avoid deep layout trees. The new React Native renderer, with shared C++ core, brings this feature to iOS. This optimisation is automatic and does not require a setup. Understanding of this article is not needed to make use of View Flattening.
The React API is designed to be declarative and reusable through composition. This is great for intuitive development but it leads to deep React Element Trees where component affect the layout but don’t paint anything on the screen. We call these types of components Layout-Only. Flatter view hierarchy has lower memory usage and faster drawing times.
Some requirements for a component to be Layout-Only:
- Pure
<View />
, not a subclass. - No event listeners.
- nativeID is not defined.
- Is not accessible element.
- Full opacity.
- No background colour.
- Rest of the requirements
Let's take an example of component that shows image with a title and call it ImageWithTitle. We would like to add some space between the component and the surrounding content.
function ImageWithTitle(props) {
return (
<View style={{padding: 10}}>
<Image source={{uri: props.image}} />
<Text>{props.title}</Text>
</View>
);
}
The top most <View />
, which adds padding, doesn't paint anything to the screen. If we delete host view representing the top most <View />
, it will not affect what is drawn on the screen. Framework applies padding to its children to make sure they are correctly positioned on the screen.
A common requirement is to place elements in a row or column.
For this example, we want to display names of two people with company name under the name, NameList.
To group person's name with company name, it has to be wrapped in a <View />
component to create new flex container.
<View style={{flexDirection:'row'}}>
<View>
<Text>Alice</Text>
<Text>Company A</Text>
</View>
<View>
<Text>Bob</Text>
<Text>Company B</Text>
</View>
</View>
There are three <View />
components. One is setting flexDirection: row
and the other two group components into separate flex containers.
Neither of them paint anything to the screen.
For a more complex example, we are going to build an item in a flat list showing movie screening details. The component, called MovieDetails, will display name, genre, thumbnail, place and time of a screening.
MovieDetails component
import React from 'react';
import {SafeAreaView, StyleSheet, Text, View, Image} from 'react-native';
function Movie(props) {
return (
<View style={styles.wrapper}>
<View style={styles.movieInfo}>
<View style={styles.thumbnail} />
<View style={styles.movieNameAndGenre}>
<Text>{props.title}</Text>
<Text style={styles.subtitle}>{props.genre}</Text>
</View>
</View>
<View style={styles.eventInfo}>
<Text>{props.time}</Text>
<Text>{props.place}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
padding: 10,
flexDirection: 'row',
alignItems: 'center',
},
subtitle: {
color: 'grey',
},
movieNameAndGenre: {
marginLeft: 10,
},
thumbnail: {
width: 60,
height: 60,
backgroundColor: 'blue',
},
eventInfo: {
marginLeft: 'auto',
},
movieInfo: {
flexDirection: 'row',
alignItems:'center',
}
});
The following screenshot show what view hierarchy is painted without flattening. All views have red border to make "Layout-Only" components visible.
The second screenshot shows what view hierarchy is painted with with flattening enabled. Four Layout-Only views were removed without affecting visual appearance.
Here are screenshots of what the view hierarchy looks like in RNTester with and without view flattening. This is closer to a real world app.