4

Here is my sample code, I would like the ListView elements to be updated on click.

This line defines the style:

(this.state.selectedField.id==field.id)?'green':'white'

In this example the active View should be highlighted with green color. The state is getting updated inside handleClick(), but renderField() method is not being called.

How to make ListView re-render on state change triggered by click?

RNPlayNative Link

import React, {Component} from 'react';
import {
  AppRegistry,
  View,
  ListView,
  Text,
  TouchableOpacity
} from 'react-native';

class SampleApp extends Component {

  constructor(props) {
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      fields: ds.cloneWithRows([
        {id:0},{id:1},{id:2}
      ]),
      selectedField: {id:0}
    };
  }

  handleClick(field) {
    console.log("Selected field:",field);
    this.setState({
      selectedField: field
    });
  }

  renderField(field) {
    return (
      <TouchableOpacity onPress={this.handleClick.bind(this, field)} >
        <View style={{backgroundColor:(this.state.selectedField.id==field.id)?'green':'white'}}>
          <Text style={{left:0, right:0, paddingVertical:50,borderWidth:1}}>{field.id}</Text>
        </View>
      </TouchableOpacity>
    );
  }

  render() {
    return (
      <View>
        <ListView
          dataSource={this.state.fields}
          renderRow={this.renderField.bind(this)}
        />
      </View>
    );
  }
}

AppRegistry.registerComponent('SampleApp', () => SampleApp);
2
  • Try this: stackoverflow.com/questions/26882177/… Commented Jul 7, 2016 at 12:14
  • I am already using inline styles, the problem is in linking style dynamically to state on events such as click. Commented Jul 7, 2016 at 12:22

3 Answers 3

3

The list is rendered again when the dataSource changes, and in your example since the dataSource never changes, the listview is never re-rendered. This can be achieved by adding a field in state variable to hold the data for dataSource. And in componentDidMount method, have the dataSource clone rows with data state variable. Whenever you'd want to re-render the listview, you will have to change the data state variable only and list will be automatically re-rendered. I have changed you data to add a selected state with every object. Here is the updated code.

class SampleApp extends Component {

  constructor(props) {
     super(props);
     var ds = new ListView.DataSource({
         rowHasChanged: (row1, row2) => row1 !== row2,
    });
     var dataVar = [
         {
           id:0,
           selected: true,
         },{
           id:1,
           selected: false,
         },{
           id:2,
           selected: false,
         }
       ];
     this.state = {
       data: dataVar,
       fields: ds,
     };
   }

   componentDidMount() {

     this.setState({
       fields: this.state.fields.cloneWithRows(dataVar)
     });
   }


   handleClick(field) {
     console.log(field);
     field.selected = !field.selected;

     var dataClone = this.state.data;
     console.log(dataClone);

     dataClone[field.id] = field;

     this.setState({
       data: dataClone,
     });
   }

   renderField(field) {
     let color = (field.selected == true)?'green':'white';
     return (
       <TouchableOpacity onPress={this.handleClick.bind(this, field)} >
         <View style={{backgroundColor:color}}>
           <Text style={{left:0, right:0, paddingVertical:50,borderWidth:1}}>     {field.id}</Text>
         </View>
       </TouchableOpacity>
     );
   }

   render() {
     return (
       <View>
         <ListView
           dataSource={this.state.fields}
           renderRow={(field) => this.renderField(field)}
         />
       </View>
     );
   }
 }

Here is a working rnplay sample

Sign up to request clarification or add additional context in comments.

3 Comments

You forgot to fix this line inside componentDidMount: fields: this.state.fields.cloneWithRows(this.state.data)
I tried the solution, but for some reason I can't trigger the renderField() function on update of data. handleClick() works properly and data gets updated.
Okay, I got so far that it works. My application had this line: if (!this.state.loaded) { return this.renderLoadingView(); } for postponing view rendering until data gets loaded that for some reason blocked the renderField() call.
1

Already answered many times, if you want to changes the row rendering on a props, this props needs to go in your data if you want to row to update.

See these 2 answers:

Comments

0

Here would be my solution to the question, but it still takes too long to process every click:

handleClick(field) {
  newFields = this.state.fields.map( (x) => x.key === field.key && x.active !== true ? {...x,active:true} : {...x,active:false})  
  this.setState({
    fields:newFields,
    dataSource: this.state.dataSource.cloneWithRows(newFields)
  });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.