React-data-grid: Grid does not properly re-render when more than one row is updated at a time.

Created on 24 May 2016  Â·  8Comments  Â·  Source: adazzle/react-data-grid

This can be showcased using the built-in editors example, just modify its render method as follows to have a button that, when clicked, would set the value of the task column in each row to 'after click'.
The interface does not reflect the new values in all the rows, only the first row is visually updated. One has to click on each row individually to force it to update.

render:function(){
    var _this=this;
    return(
    <div><div><input type="button" value="click me" onClick={function(){
      var _rows=_this.state.rows.slice(0);
      _rows.forEach(function(r){
          Object.assign(r, {task: 'after click'});
      });
      _this.setState({rows:_rows});
    }} /></div>
      <ReactDataGrid
      enableCellSelect={true}
      columns={columns}
      rowGetter={this.rowGetter}
      rowsCount={this.state.rows.length}
      minHeight={500}
      onRowUpdated={this.handleRowUpdated} /></div>
    )
  }
wontfix

Most helpful comment

Hi everyone, I ran into this problem yesterday as well. it turned out that in Row's shouldComponentUpdate it compares this.props.row !== newProps.row and this could determine if a row will be updating or not. However, remember that in Javascript, === and !== performs strict comparsion, in terms of objects, this means that as long as they refer to the same object, it will return true. This means if your rowGetter looks like this (I'm using Backbone's collection and model in this case)

rowGetter: function(i) {
    return this.getCollection().at(i).attributes;
}

, the rows won't get updated because it will always refer to the same object in memory.
The

solution

would be to return a clone of the object, which could look like this using underscore's clone method

 rowGetter: function(i) {
    return _.clone(this.getCollection().at(i).attributes);
}

All 8 comments

Ditto! I thought it was me - I'm secretly glad it is in the grid :)

On 24 May 2016 at 16:09, ckazan [email protected] wrote:

This can be showcased using the built-in editors example, just modify its
render method as follows to have a button that, when clicked, would set the
value of the task column in each row to 'after click'.
The interface does not reflect the new values in all the rows, only the
first row is visually updated. One has to click on each row individually to
force it to update.

render:function(){
var _this=this;
return(

var _rows=_this.state.rows.slice(0);
_rows.forEach(function(r){
Object.assign(r, {task: 'after click'});
});
_this.setState({rows:_rows});
}} />

enableCellSelect={true}
columns={columns}
rowGetter={this.rowGetter}
rowsCount={this.state.rows.length}
minHeight={500}
onRowUpdated={this.handleRowUpdated} />

)
}

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub
https://github.com/adazzle/react-data-grid/issues/332

Andrew McGregor
07940 22 33 11

Hey peeps, any news on this issue?!

Or any clues as how to fix?

Hi everyone, I ran into this problem yesterday as well. it turned out that in Row's shouldComponentUpdate it compares this.props.row !== newProps.row and this could determine if a row will be updating or not. However, remember that in Javascript, === and !== performs strict comparsion, in terms of objects, this means that as long as they refer to the same object, it will return true. This means if your rowGetter looks like this (I'm using Backbone's collection and model in this case)

rowGetter: function(i) {
    return this.getCollection().at(i).attributes;
}

, the rows won't get updated because it will always refer to the same object in memory.
The

solution

would be to return a clone of the object, which could look like this using underscore's clone method

 rowGetter: function(i) {
    return _.clone(this.getCollection().at(i).attributes);
}

Thank you @leimd!
For those of us not using underscore.js, a good way to clone the object (since it is just values, no functions) is:

     rowGetter: function(i) {
        return JSON.parse(JSON.stringify(this.getCollection().at(i).attributes));
    }

@tgehrs if you use babel polyfill: Object.assign , then you can use Object.assign() as well, probably a bit faster than JSON.parse(JSON.stringify

FWIW, the logic to clone a row probably shouldn’t be in the rowGetter(). If you can, for the sake of rendering performance, you should determine whether a row needs to be cloned when you handle updates.

Similarly, JSON.parse(JSON.stringify(...)) is effective but not the best thing from a performance perspective. If you can, use Object.assign({}, this.getCollection().at(i).attributes) or spread syntax {...this.getCollection().at(i).attributes}. Object.assign can be trivially replicated with a shallow clone implementation like:

function cloneShallow(source) {
  var target = {}

  for (var property in source) {
    if (source.hasOwnProperty(property)) {
      target[property] = source[property];
    }
  }

  return target
}

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Please reopen this if you feel it has been incorrectly closed and we will do our best to look into it. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Thilagm picture Thilagm  Â·  3Comments

gauravagam picture gauravagam  Â·  3Comments

GenoD picture GenoD  Â·  3Comments

ryanwtyler picture ryanwtyler  Â·  3Comments

martinnov92 picture martinnov92  Â·  3Comments