Reputation: 1864
At the moment I'm doing it like below. It works, but before adopting this concept on all my lists I'd like some input. Is this a good way of doing it? My original data lives in an Immutable.List.
For readers not used to Immutable.js: The difference from using a plain array is that we need to call theList.get(rowId) instead of simply theArray[rowId]. We also need to convert the index list to an array, keySeq().toArray()
In the constructur I initialize this.state with a dataSource on which I also perform the initial cloning of data with a separate function (cloneDataSource):
constructor(props) {
super(props);
let dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
getRowData: (dataBlob, sectionId, rowId) => dataBlob[sectionId].get(rowId),
sectionHeaderHasChanged : (s1, s2) => s1 !== s2
})
this.state = {
dataSource: this.cloneDataSource(dataSource)
}
}
The function for cloning data rows are called from both the constructor and from componentWillReceiveProps. This particular listView has two hardcoded sections:
cloneDataSource(dataSource) {
return dataSource.cloneWithRowsAndSections(
[
this.props.activeProjects,
this.props.inactiveProjects
],
[0,1],
[
this.props.activeProjects.keySeq().toArray(),
this.props.inactiveProjects.keySeq().toArray()
]
)
}
componentWillReceiveProps = (nextProps) => {
this.setState({dataSource: this.cloneDataSource(this.state.dataSource)})
}
The fact that I need to call my clone function twice worries me. Why won't it suffice to clone the data at componentWillReceiveProps?
Upvotes: 1
Views: 436
Reputation: 1890
You cannot only use componentWillReceiveProps
because it doesn't run on the first render and you have to use nextProps
, not this.props
. If your ListView
's data is coming from the top as props
, you're always going to need two spots where you'll update the ListView.DataSource
. One for the initial render and the other for updates coming in. One way to keep things DRY-ish is to do this:
constructor(props) {
super(props);
this.state = {
// You can initialize it directly on to the state object
dataSource: new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
getRowData: (dataBlob, sectionId, rowId) => dataBlob[sectionId].get(rowId),
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
}),
};
}
componentWillMount() { // Or `componentDidMount`
// 1. For initial render (note: I'm passing this.props, you'll see why on 2.)
this.updateDataSource(this.props);
}
componentWillReceiveProps(nextProps) {
// 2. For updates
// (note: you must pass `nextProps` because
// `this.props` has not been updated)
this.updateDataSource(nextProps);
}
// Do your thing
updateDataSource(data) {
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(
[
data.activeProjects,
data.inactiveProjects
],
[0, 1],
[
data.activeProjects.keySeq().toArray(),
data.inactiveProjects.keySeq().toArray()
]
)
});
}
EDIT
Also, you should note that not all props
will be in nextProps
because only the props that changed are in newProps
, so you should properly handle making sure that the prop fields actually exist.
Upvotes: 1