Michael
Michael

Reputation: 1864

What is the correct way to load datasource into listView in React Native

What is the correct way to load datasource into listview in React Native?

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

Answers (1)

rclai
rclai

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

Related Questions