React-data-grid: Bug - Custom rowRenderer & locked columns

Created on 17 Oct 2017  ·  12Comments  ·  Source: adazzle/react-data-grid

Which version of React JS are you using?

✅ Officially supported ✅

  • [ ] v15.4.x

⚠️ Not officially supported, expect warnings ⚠️

  • [X] v15.5.x
  • [ ] v15.6.x

☣️ Not officially supported, expect warnings and errors ☣️

  • [ ] v16.x.x

Which browser are you using?

✅ Officially supported ✅

  • [ ] IE 9 / IE 10 / IE 11
  • [ ] Edge
  • [X] Chrome

⚠️ Not officially supported, but "should work" ⚠️

  • [ ] Firefox
  • [ ] Safari

I'm submitting a ...

  • [X] 🐛 Bug Report
  • [ ] 💡 Feature Request

👋 Need general support? Not sure about how to use React itself, or how to get started with the Grid?
Please do not submit support request here. Instead see

https://github.com/adazzle/react-data-grid/blob/master/CONTRIBUTING.md


Issue Details

Current behavior: When using a custom rowRenderer and using Columns with the "locked" attribute,
The headers correctly "freezes" but not the rows.
Desired behavior: The rows generated from custom rowRenderer should support locked columns.

After debugging trough the code in react-data-grid.js, in the Canvas class ( line 9134 ), the function "setScrollLeft" (line 9310), "this.rows" contains an array of nulls instead of the instance of the row when using a custom rowRenderer.

My temp fix was to modify the "getRows" function (react-data-grid.js line 9248) by replacing "var rows" by "this.rows" like this:

...
getRows: function getRows(displayStart, displayEnd) {
        this._currentRowsRange = { start: displayStart, end: displayEnd };
        if (Array.isArray(this.props.rowGetter)) {
          return this.props.rowGetter.slice(displayStart, displayEnd);
        }
        this.rows = [];
        var i = displayStart;
        while (i < displayEnd) {
          var row = this.props.rowGetter(i);
          var subRowDetails = {};
          if (this.props.getSubRowDetails) {
            subRowDetails = this.props.getSubRowDetails(row);
          }
        this.rows.push({ row: row, subRowDetails: subRowDetails });
          i++;
        }
        return this.rows;
      },
...

And in mu custom rowRenderer I included the function "getDecoratedComponentInstance" to return the rowRenderer with the property "row" as a ref to the ReactDataGrid.Row like this:

import React from 'react';
import Fn from "../../../Classes/Fn";
const ReactDataGrid = require('react-data-grid');

export default class DatagridRowRenderer extends React.Component {

  static propTypes = {
    idx: React.PropTypes.number,
    onClick: React.PropTypes.func,
  };

  static defaultProps = {};

  static contextTypes = {};

  constructor(props) {
    super(props);
    this.getDecoratedComponentInstance = this.getDecoratedComponentInstance.bind(this);
  }

  getDecoratedComponentInstance() {
    return this;
  }

  render(){
    return (<div key={this.props.idx} onClick={(e)=> Fn.call(this.props.onClick, this.props.idx, e)}><ReactDataGrid.Row ref={(ref) => this.row = ref} {...this.props} /></div>);
  }

}

So that the function "getRowByRef" (react-data-grid.js line 9323) returns the real ReactDataGrid.Row from my custom RowRenderer.

Most helpful comment

`

class customCellRenderer extends Component {
    render () {
        return (
                   <Cell {...this.props}/>
          );
    }   
}
class CustomRowFormatter extends Component {
    setScrollLeft = (scrollBy) => {
        // if you want freeze columns to work, you need to make sure you implement this as apass through
        this.row.setScrollLeft(scrollBy);
    };

    render () {
        return (<ReactDataGrid.Row 
        ref={ (node) => this.row = node } 
        {...this.props}  
        cellRenderer={customCellRenderer} 
        forceUpdate={true} />);
    }
}

Main component's render fn below

render () {
         return (
                <DraggableContainer 
                        onHeaderDrop={this.onHeaderDrop}>
                        <ReactDataGrid
                            ref={grid => (this.grid = grid)}
                            rowRenderer={CustomRowFormatter} 
                            onGridSort={this.handleGridSort}
                            columns={this.state.columns.filter(column => column.visible === true)}
                            rowGetter={this.rowGetter}
                            rowsCount={this.state.rows.length}
                            onRowClick={this.onRowClick}
                           minHeight={500}
                        /> 
                </DraggableContainer> `
        ) };

×`
TypeError: _this2[column.key].setScrollLeft is not a function
(anonymous function)
node_modules/react-data-grid/dist/react-data-grid.js:6869
6866 | _this2.props.columns.forEach(function (column) {
6867 | if (column.locked) {
6868 | if (!_this2[column.key]) return;

6869 | _this2[column.key].setScrollLeft(scrollLeft);
6870 | }
6871 | });
6872 | }, _this2.getKnownDivProps = function () {
View compiled
Row._this2.setScrollLeft
node_modules/react-data-grid/dist/react-data-grid.js:6866

How to resolved.? This works fine without rowRender. With rowRender I get above error?

All 12 comments

im also getting same issue please give me some suggestion here my code
https://codesandbox.io/s/3x7zy1jyr1

@cklab - How specifically are we supposed to use setScrollLeft. I may just be missing some larger context here, but I don't know where, specifically, I'm implementing and passing this through.

@grahamlutz Create a ref to the Row component in your custom renderer. Create a function, setScrollLeft in your custom renderer where you call setScrollLeft on the ref. react-data-grid will call the setScrollLeft on your custom renderer.

@cklab Alright, so that's what I thought it was saying, and I did that.

class RowRenderer extends React.Component<any, any> {
  private row;

  setScrollLeft = (scrollBy) => {
    // if you want freeze columns to work, you need to make sure you implement this as apass through
    this.row.setScrollLeft(scrollBy);
  };

  render () {
    return this.props.items.map((item, index) => {
      return <Row
        {...this.props}
        ref={ (node) => this.row = node }
        key={index}
        columns={this.props.columns}
        rows={this.props.rows}
        handleRowClick={handleRowClick}
        rowIndex={index}
        item={item}
        model={this.props.model}
        itemVar={'item'}
      />;
    });
  }
}

But then I get _this.row.setScrollLeft is not a function...

Oh, I have to use the react-data-grid ...that totally explain why it made no sense to be how this setup could possible call that function appropriately, ha!

Thanks @cklab for providing the solution.

`

class customCellRenderer extends Component {
    render () {
        return (
                   <Cell {...this.props}/>
          );
    }   
}
class CustomRowFormatter extends Component {
    setScrollLeft = (scrollBy) => {
        // if you want freeze columns to work, you need to make sure you implement this as apass through
        this.row.setScrollLeft(scrollBy);
    };

    render () {
        return (<ReactDataGrid.Row 
        ref={ (node) => this.row = node } 
        {...this.props}  
        cellRenderer={customCellRenderer} 
        forceUpdate={true} />);
    }
}

Main component's render fn below

render () {
         return (
                <DraggableContainer 
                        onHeaderDrop={this.onHeaderDrop}>
                        <ReactDataGrid
                            ref={grid => (this.grid = grid)}
                            rowRenderer={CustomRowFormatter} 
                            onGridSort={this.handleGridSort}
                            columns={this.state.columns.filter(column => column.visible === true)}
                            rowGetter={this.rowGetter}
                            rowsCount={this.state.rows.length}
                            onRowClick={this.onRowClick}
                           minHeight={500}
                        /> 
                </DraggableContainer> `
        ) };

×`
TypeError: _this2[column.key].setScrollLeft is not a function
(anonymous function)
node_modules/react-data-grid/dist/react-data-grid.js:6869
6866 | _this2.props.columns.forEach(function (column) {
6867 | if (column.locked) {
6868 | if (!_this2[column.key]) return;

6869 | _this2[column.key].setScrollLeft(scrollLeft);
6870 | }
6871 | });
6872 | }, _this2.getKnownDivProps = function () {
View compiled
Row._this2.setScrollLeft
node_modules/react-data-grid/dist/react-data-grid.js:6866

How to resolved.? This works fine without rowRender. With rowRender I get above error?

Bump

After getting latest from this branch, this issue is happening again. I had implemented it by defining the setScrollLeft(scrollBy) {
this.row.setScrollLeft(scrollBy);
}
in my customRowRenderer component. It used to work. But, now, after getting latest to fix the Horizontal scroll bar issue on latest Chrome browsers, this fix is broken.

So now, only the locked header is locked. The row data is scrolling while using the horizontal scroll bar.

@stigmat4j @malonecj @srikanthpavuluri @stigmat4j @amanmahajan7

Please advise what might fix this.

Thanks,
Madhan

With the latest code, its fixed.

Your custom row-renderer needs to be looks like this.

return (

);

Thanks,
Madhan

@madhan-dhamodaran

When I use the renderBaseRow() to create a custom grid row it does make the set columns frozen. However when I try to add a custom Cell component, using the cellRenderer prop inside the props for renderBaseRow, the bug reappears.

`

const {Cell} = ReactDataGrid;

const customCell = ({...cellProps}) => {
    return  <Cell {...cellProps}>content</Cell>
}

const CustomRowRenderer = ({renderBaseRow, ...canvasProps}) => {

    return renderBaseRow({...canvasProps, cellRenderer: customCell});
};

`

This was after trying to use this.row.setScrollLeft(scrollBy) method identified above.

My solution to getting columns to freeze when using custom rows and cells is to wrap the desired Cells in a div with the css style position: fixed.

`

class DataGridCell extends React.Component {

constructor(props) {
    super(props);
}

onCellClick(e){
    return;
}

render() {
    const {cellMetaData} = this.props;
    const {Cell} = ReactDataGrid;

    if(this.props.idx === 0){
        return <div className={"position-fixed"}>
            <Cell {...this.props}/>
        </div>;
    } else {
        return <Cell {...this.props} >
            <CustomComponent />
        </Cell>;
    }

}

}

`

Was this page helpful?
0 / 5 - 0 ratings