While looking at the documentation for React Native's components, you may note a component named ListView. This is a core component that is meant to display vertically scrolling lists of data.
Here's how ListView works. We will create a data source, fill it up with an array of data blobs, create a ListView component with that array as its data source, and pass it some JSX in its renderRow callback, which will take the data and render a row for each blob within the data source.
On a high level, here is how it looks:
class TasksList extends Component {
constructor (props) {
super (props);
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
dataSource: ds.cloneWithRows(['row 1', 'row 2'])
};
}
render () {
return (
<ListView
dataSource = { this.state.dataSource }
renderRow = { (rowData) => <Text>
{ rowData } </Text> }
/>
);
}
}
Let's look at what's going on. In the constructor of our component, we create an instance of ListViewDataSource. The constructor for a new ListViewDataSource accepts, as a parameter, an argument that can contain any of these four:
- getRowData(dataBlob, sectionID, rowID)
- getSectionHeaderData(dataBlob, sectionID)
- rowHasChanged(previousRowData, nextRowData)
- sectionHeaderHasChanged(previousSectionData, nextSectionData)
The getRowData is a function that gets the data required to render the row. You can customize the function however you like as you pass it in to the constructor of ListViewDataSource, but ListViewDataSource will provide a default if you don't specify.
The getSectionHeaderData is a function that accepts a blob of data and a section ID and returns just the data needed to render a section header. Like getRowData, it provides a default if not specified.
The rowHasChanged is a function that serves as a performance optimization designed to only re-render any rows that have their source data changed. Unlike getRowData and getSectionHeaderData, you will need to pass your own version of rowHasChanged. The preceding example, which takes in the current and previous values of the row and returns a Boolean to show if it has changed, is the most common implementation.
The sectionHeaderHasChanged is an optional function that compares the section headers' contents to determine whether they need to be re-rendered.
Then, in our TasksView constructor, our state receives a property of dataSource whose value is equal to calling cloneWithRows on the ListViewDataSource instance we created earlier. cloneWithRows takes in two parameters: a dataBlob and rowIdentities. The dataBlob is any arbitrary blob of data passed to it, and rowIdentities represents a two-dimensional array of row identifiers. The rowIdentities is an optional parameter--it isn't included in the preceding sample code. Our sample code passes a hardcoded blob of data--two strings: 'row 1' and 'row 2'.
It's also important to mention right now that the data within our dataSource is immutable. If we want to change it later, we'll have to extract the information out of the dataSource, mutate it, and then replace the data within the dataSource.
The ListView component itself, which is rendered in our TasksList, can accept a number of different properties. The most important one, which we're using in our example, is renderRow.
The renderRow function takes data from the dataSource of your ListView and returns a component to render for each row of data in your dataSource. In our preceding example, renderRow takes each string inside our dataSource and renders it in a Text component.
With the preceding code, here is how TasksList will render. Because we have not yet styled it, you will see that the iOS Status Bar overlaps the first row:
Great! There's not much to see, but we accomplished something: we created a ListView component, passed it some data, and got that data to be rendered on our screen. Let's take a step back and create this component in our application properly.