Sungpah Lee
Sungpah Lee

Reputation: 1021

dataSource is updated but Listview is not refreshed in react-native after componentWillReceiveProps

I'm trying to refresh listview when componentWillReceiveProps executes. It seems dataSource is updated after fetching ( I put console.log(this.state.dataSource) in the render() and it changes according to the endpoint's current status. )

However, the ListView stays as same. I was wondering why and was suspicious of dataSource. So I ended up making string object to print out the dataSource in the render(). var str = JSON.stringify(this.state.dataSource._dataBlob.s1);

And <View><Text>{str}</Text>

The above part changes when componentWillReceiveProps() executed. However, the listview stays as same. I'm having no clue why this happens.

Here is the snippet of my code.

componentWillReceiveProps(){
    var query = ApiUrl.urlForWeekInfo(this.state.email);
    console.log(query);
    this._executeQuery(query,3);
  }


  constructor(props) {
    super(props);

    var dataSource = new ListView.DataSource(
      {rowHasChanged: (r1, r2) => r1.guid !== r2.guid});

      this.state = {
      isLoading: false,
      ...
      first_name: this.props.first_name,
      email: this.props.email,
      dataSource: dataSource.cloneWithRows(this.props.listings)      
    };
  }
..

_executeQuery(query,i) {

    this.setState({ isLoading: true });
    fetch(query)
      .then(response => response.json())
      .then(json => this._handleResponse(json.response,i))
      .catch(error =>
         this.setState({
          isLoading: false,
          message: 'Something bad happened ' + error
    }));
  }

  _handleResponse(response,i) {
    this.setState({ isLoading: false , message: '' });
    if (response.application_response_code.substr(0, 1) === '1') {
      if(i==1){
        this.props.navigator.push({
          title: 'Apply',
          component: Apply,
          passProps: {listings: response.listings, times: response.timetable, week_num: this.state.week_num}
        });
      } else if (i==3){

        var output = response.listings;
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(output)
        });

        console.log(response.listings);
        this.forceUpdate();

      }
    } else {
    }
  }

render() {

    console.log(this.state.dataSource._dataBlob.s1);

    var str = JSON.stringify(this.state.dataSource._dataBlob.s1);
    var listviewsource = this.state.dataSource;

    return (

    <View style={[this.border('red'), styles.container_top]}>
      <View style={[this.border('black'), styles.container_top_buttons]} >

        <ListView
          dataSource={listviewsource}
          renderRow={this.renderRow.bind(this)}/> 

      </View>      
      {spinner}
      <View>
      <Text>{str}</Text>
      </View>

    </View>




        );
        }}

Please share any idea or experience you might have.


I ended up finding a solution for this issue:

var array = response.listings;
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    //=> call another ds variable.

    this.setState({
      rawData: array,
      dataSource: ds.cloneWithRows(response.listings)
    });

Upvotes: 0

Views: 762

Answers (1)

Mihir
Mihir

Reputation: 3912

There is no direct method to update rows to your ListView. There is not much documentation on ListView.DataSource out there. So here it goes-

If you want to add/append/prepend/update/delete rows from a ListView, all you have to do is just update the dataSource and call cloneWithRows() in setState.

For eg. I want to fetch data from a URL. Each time I hit "Load more", I append new rows to my data source.

getInitialState: function(){
    return {
        rawData: [],
        dataSource: new ListView.DataSource({
          rowHasChanged: (row1, row2) => row1 !== row2
        }),
        loaded: false,
        page: 1,
    }
},

At the very first time, I call this function in ComponentDidMount() which populates my ListView with initial data and "page"=1 in the state.

  reloadData: function(){
    this.setState({ loaded: false, rawData: [], page: 1 });

    API.getUpcomingMovies(this.state.page)
      .then((data) => {
          this.setState({
            rawData: this.state.rawData.concat(data),
            dataSource: this.state.dataSource.cloneWithRows(this.state.rawData.concat(data)),
            loaded: true,
          });
      });
  },

And every time I tap on "load more" to get JSON from the API, I call the following function. Where "page" is incremented every time I hit "load more".

fetchData: function(){

    this.setState({ loaded: false, page: this.state.page+1 });

    API.getUpcomingMovies(this.state.page)
        .then((data) => {
            this.setState({
              rawData: this.state.rawData.concat(data),
              dataSource: this.state.dataSource.cloneWithRows(this.state.rawData.concat(data)),
              loaded: true,
            });
        });
  },

As you can see, I am concatenating my rawData[] (which is a simple array holding JSON objects). If you want to prepend new data to your rawData, you can do something like this-

this.setState({
    rawData: data.concat(this.state.rawData)
)}

Upvotes: 1

Related Questions