
<ListView>
was officially deprecated in React Native 0.48
. Sometimes, we ignore deprecation warnings (guilty! 🙈) and don't deal with things until they're officially removed. Do yourself a favor and replace your <ListView>
s now. It's easy, will improve your app performance, and will clean up your code ( Read: No. More. DataSource
s!).
My <ListView>
Component (Puppies! 🐶)
Here’s a simple class component named <PuppyLitter>
that we will need to update. It takes two props:
-
puppies
: An array of objects, one for each puppy in a litter. For example:[{id: 1, name: 'Winky', age: 2}, {id: 2, name: 'Floofy', age: 1} ]
-
getPuppies()
: A function that fetches an updated puppies array.
It renders a <ListView>
full of <PuppyInfo>
components, one for each puppy in the array. A <PuppyInfo>
will render things such as the puppy's name and date of birth.
// Vendor | |
import React from "react" | |
import { ListView, RefreshControl, View } from "react-native" | |
import { isEqual } from "lodash" | |
// Local | |
import PuppyInfo from "./PuppyInfo" | |
export default class PuppyLitter extends React.Component { | |
state = { | |
dataSource: null, | |
isLoading: false, | |
} | |
componentWillMount() { | |
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }) | |
this._setDataSource() | |
} | |
componentDidUpdate(lastProps) { | |
// If our array of puppies has changed, set the data source | |
if (!isEqual(this.props.puppies, lastProps.puppies)) this._setDataSource() | |
} | |
_setDataSource() { | |
this.setState({ dataSource: this.ds.cloneWithRows(this.props.puppies) }) | |
} | |
renderRow(puppy) { | |
return ( | |
<PuppyInfo puppy={puppy}/> | |
) | |
} | |
renderRefreshControl() { | |
return ( | |
<RefreshControl | |
refreshing={this.state.isLoading} | |
onRefresh={() => { | |
this.setState({ isLoading: true }) | |
this.props.getPuppies() | |
}} | |
/> | |
) | |
} | |
render() { | |
return ( | |
<View> | |
<ListView | |
dataSource={this.state.dataSource} | |
renderRow={puppy => this.renderRow(puppy)} | |
pageSize={8} | |
refreshControl={this.renderRefreshControl()} | |
/> | |
</View> | |
) | |
} | |
} |
Our Options
A <ListView>
can be replaced by any of the following:
-
<FlatList>
: A component that renders a list, such as a list of components. -
<SectionList>
: Same as a but with section support (think of a contact list with section headers labeled A, B, C..) - A
<VirtualizedList>
: Simple. Very performant. Great for immutable arrays. Not great for an array of objects with property values that change.
To keep things simple, I’ll replace the <ListView>
with a <FlatList>
. I have no need for sections. I just want a list of puppies. The list may change though. Sometimes I'm indecisive and like to rename my puppies.
Converting to a <FlatList>
First, let’s change our imports:
// import { ListView, RefreshControl, View } from "react-native"
import { FlatList, RefreshControl, View } from "react-native"
Next, get rid of all of this.
Really. No more DataSource
s! 🗑
state = { | |
// dataSource: null, | |
isLoading: false, | |
} | |
// componentWillMount() { | |
// this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }) | |
// this._setDataSource() | |
// } | |
// componentDidUpdate(lastProps) { | |
// // If our array of puppies has changed, set the data source | |
// if (!isEqual(this.props.puppies, lastProps.puppies)) this._setDataSource() | |
// } | |
// _setDataSource() { | |
// this.setState({ dataSource: this.ds.cloneWithRows(this.props.puppies) }) | |
// } |
Let’s simplify our renderRefreshControl()
method:
// renderRefreshControl() { | |
// return ( | |
// <RefreshControl | |
// refreshing={this.state.isLoading} | |
// onRefresh={() => { | |
// this.setState({ isLoading: true }) | |
// this.props.getPuppies() | |
// }} | |
// /> | |
// ) | |
// } | |
renderRefreshControl() { | |
this.setState({ isLoading: true }) | |
this.props.getPuppies() | |
} |
Finally, update the rendered component:
<View> | |
// <ListView | |
// dataSource={this.state.dataSource} | |
// renderRow={puppy => this.renderRow(puppy)} | |
// pageSize={8} | |
// refreshControl={this.renderRefreshControl()} | |
// /> | |
<FlatList | |
data={this.props.puppies} | |
renderItem={({item}) => this.renderRow(item)} | |
keyExtractor={(item, index) => item.id} | |
onRefresh={() => this.renderRefreshControl()} | |
refreshing={this.state.isLoading} | |
initialNumToRender={8} | |
/> | |
</View> |
Some things to note about <FlatList>
-
data
replaces yourdataStore
prop. This is where you pass in your array. -
renderItem
replacesrenderRow
.
IMPORTANT : renderItem
passes an item, 🐶
, from the data array, [🐶, 🐶, 🐶]
, within an object. Your puppy will be the value of an item property within this object: {item: 🐶}
. If you wish to keep your renderRow()
method as is, pass in the item instead.
Another option:
renderItem={({item: puppy}) _=>_ this.renderRow(puppy)}
- Keys have been added to each rendered item by using the
keyExtractor
prop. We'll use the id of the puppy object. - The new
this.renderRefreshControl()
function needs to be bound to the component inonRefresh
-
pageSize
does not exist here. UseinitialNumToRender
to communicate home many rows to render in the initial batch (approximately how many will fit in the screen at once)
My Final Component
// Vendor | |
import React from "react" | |
import { FlatList, RefreshControl, View } from "react-native" | |
import { isEqual } from "lodash" | |
// Local | |
import PuppyInfo from "./PuppyInfo" | |
export default class PuppyLitter extends React.Component { | |
state = { | |
isLoading: false, | |
} | |
renderRow(puppy) { | |
return ( | |
<PuppyInfo puppy={puppy}/> | |
) | |
} | |
renderRefreshControl() { | |
this.setState({ isLoading: true }) | |
this.props.getPuppies() | |
} | |
render() { | |
return ( | |
<View> | |
<FlatList | |
data={this.props.puppies} | |
renderItem={({item: puppy}) => this.renderRow(puppy)} | |
keyExtractor={(item, index) => item.id} | |
onRefresh={() => this.renderRefreshControl()} | |
refreshing={this.state.isLoading} | |
initialNumToRender={8} | |
/> | |
</View> | |
) | |
} | |
} |
Doesn’t that look so much better?
It’s way more performant too! 🏁
👋 Hi! I’m Juliette. I work at Eventric as a Software Developer. Come follow me on Twitter at @Juliette.
Top comments (0)