Hi,
"Uncaught TypeError: Cannot read property 'setScrollLeft' of null"
Don't know what might cause this, can anybody here experience this kind of error?
Thanks,
I've noticed this problem as well.
My grid has:
1) a fixed column (eg select all)
2) filtering enabled
Steps to reproduce:
1) Click on filter rows button twice, and it'll show the warning above when it hides the filter box
The error is thrown in HeaderRow, see line 147, because this.cells[i] is returning null:
setScrollLeft(scrollLeft: number) {
this.props.columns.forEach( (column, i) => {
if (column.locked) {
this.cells[i].setScrollLeft(scrollLeft);
}
});
},
I think there is an issue in Header.js (https://github.com/adazzle/react-data-grid/blob/master/packages/react-data-grid/src/Header.js) line 110
key={row.ref} means that the HeaderRow's key is now being set to a function
I'm experiencing the same issue as you too! Like you, to reproduce the error, I clicked on the filter button twice, and on the 2nd click, the exception you've described will be thrown.
My columns have filtering and sorting enabled, I have rowSelection enabled and I've set my column headers to be resizable as well.
All other functions, other than clicking on the "Filter" button, are alright and do not throw the Uncaught TypeError: Cannot read property 'setScrollLeft' of null exception!
Hope this helps others who're experiencing the same issue!
Btw I'm using react-data-grid version 2.0.24
I think this is the column (the leftmost column of the row that gets added to the table when you click on "Filter Rows" when you got rowSelection enabled) that causes _this2.cell[i] to return null, because it has no cell field.
{
key: "select-row",
name: "",
locked: true,
width: 60,
filterable: false,
onCellChange: function…
....
}
Take note of the field locked: true that makes it enter the if (column.locked) { ... } part of the setScrollLeft function that throws the Exception. Here's the link to the file HeaderRow.js at line 147 where I found the setScrollLeft problem.
setScrollLeft(scrollLeft: number) {
this.props.columns.forEach( (column, i) => {
if (column.locked) {
console.log(column); // My own console.log to debug this portion of the code
this.cells[i].setScrollLeft(scrollLeft);
}
});
},
Adding this fix to this function worked for me! If this is a hacky way though, the root cause may be associated with @supamanda 's suggestion, so any advice is much appreciated!!
setScrollLeft(scrollLeft: number) {
this.props.columns.forEach( (column, i) => {
if (column.locked) {
if (!this.cells[i]) return; // Added this little check
this.cells[i].setScrollLeft(scrollLeft);
}
});
},
@NatashaKSS's fix worked for me, but please @adazzle, push a fix!
@giacomoguerci your are right!
@adazzle please fix the bug!
Putting if (!this.cells[i]) return; fixes a symptom, but not the real problem.
Hey guys,
Considering I'm the one that broke this, I think it only reasonable that I'm the one to fix it. I'm picking this up right now, and I'll have a fix later today. I hope this can be merged ASAP. Sorry about the inconvenience!
Right, so I'm tracking the issue now, and I've been able to replicate it. My problem right now, however, is that if the filter bar is removed, the node in the ref of HeaderCell in HeaderRow.js:126 is null, as the element is unmounted.
@supamanda Could you explain why you believe this error was caused by Header.js:110?
@michielboekhoff maybe I have confused us all!
I've set up a sample at https://github.com/supamanda/react-data-grid-samples
It displays 2 tables. You can generate new data for each table by clicking on the 'New data' button below the table. You can display & hide the filter row by clicking on the filter button above the table.
To demonstrate why I think there's a problem at Header.js:110:
1) Run the above sample code
2) click on the filter button for the first table
3) in the React dev tools, check the keys of the grid's header rows
Actual:
The HeaderRow key is a function or a string representation of a function
A: the key for the HeaderRow with the column names is function ref(node) { return _this4.row ...
B: the key for the HeaderRow with the filter is function ref(node) { return _this4.fil ...
Expected:
The HeaderRow key should be a string- it's what react uses to work out if it needs to be re-rendered.
Having said that, it may not be the cause of this issue, but it does look wrong. See attached image
More to come...
@michielboekhoff ...continuing :)
So now what is more likely to be causing this problem, is that it appears to be sharing the cells[] between header row instances. It's a bit convoluted to show you but I'll do my best -
Still using the above sample code:
1) Add a log line to node_modules/react-data-grid/dis/react-data-grid.js in the HeaderRow.setScrollLeft function, for me it is at line 10250, so it should look something like:
setScrollLeft: function setScrollLeft(scrollLeft) {
var _this2 = this;
this.props.columns.forEach(function (column, i) {
if (column.locked) {
console.log("setting scroll left", _this2.cells, _this2.props) // my added log line
_this2.cells[i].setScrollLeft(scrollLeft);
} else {
if (_this2.cells[i] && _this2.cells[i].removeScroll) {
_this2.cells[i].removeScroll();
}
}
});
},
2) Make sure there are fewer columns in the top table than the bottom table (click on new data button)
3) Click on the filter button for the first table to display the filter
What happens:
In the console, a log line is printed out as per our added log line above - the cell[] length does not match the number of columns in the table.
See image below, where the first table has 4 columns, but the log line says cells[] has a length of 9
If you drill down into the values of in the cell[], cell[0-3].props.column are the columns for the first table, with a rowType filter, as expected; see attachment
However, cell[4-8].props.column, are the columns for the second table, with a rowType header, which really makes no sense.
So I suspect the cell[] is being shared between all instances of the HeaderRow.
4) Now click on the first table filter to hide the filter row.
What happens:
In the console, a log line is printed out as per our added log line above and then the error shows 'Cannot read property 'setScrollLeft' of null
In our custom log line, the cell[] length still does not match the number of columns in the first table. It says it is running the setScrollLeft function for the header row, cells[0-3] have been set to null and cells[4-8] still correspond to the values for table two
So it looks like hiding the 'filter' HeaderRow sets the 'header' HeaderRow cells[0-3] to null, and it looks like this happens because the cells[] is shared between all of the HeaderRow instances when they should be independent.
@michielboekhoff
I think maybe the problem is the way cells[] is defined as a class variable, but won't be honoured as one by react see
http://stackoverflow.com/questions/29697268/member-variables-in-react-class-are-shared-by-reference
and I think you could fix it by removing cells: [] at HeaderRow.js:60, and adding this.cells = []; at HeaderRow.getCells:115
Also, in HeaderRow.js:111 I think ref={(node) => this.row = node} should be something like
ref={(node) => {
if (row.rowType === 'filter') {
this.filterRow = node
} else {
this.row = node
}
}
Most helpful comment
Hey guys,
Considering I'm the one that broke this, I think it only reasonable that I'm the one to fix it. I'm picking this up right now, and I'll have a fix later today. I hope this can be merged ASAP. Sorry about the inconvenience!