6
votes

I am using ag-grid in a React application. We have pretty heavy duty tables with custom cells. There are performance issues when using custom cell renderers built as React components, which is the most intuitive discourse.

The ag-grid docs state that this is probably not a good idea:

Do NOT use a framework (eg Angular or React) for the cell renderers. The grid rendering is highly customised and plain JavaScript cell renderers will work faster than framework equivalents.

But it also indicates that plain JS can be used in conjunction with frameworks like React:

It is still fine to use the framework version of ag-Grid (eg for setting ag-Grid properties etc) however because there are so many cells getting created and destroyed, the additional layer the frameworks add do not help performance and should be provided if you are having performance concerns.

Am I misinterpreting this? This seems to me that I can use just a plain JS class as a cell renderer (somehow, maybe they'll handle the integration with React?)

So I took their example code and converted it to a class instead of a function to conform to their typescript definitions:

// function to act as a class
class MyCellRenderer {
    eGui: any;
    eButton: any;
    eValue: any;
    eventListener: any;

    init(params: any) {
        // create the cell
        this.eGui = document.createElement('div');
        this.eGui.innerHTML = 
            '<span class="my-css-class"><button class="btn-simple">Push Me</button><span class="my-value"></span></span>';

        // get references to the elements we want
        this.eButton = this.eGui.querySelector('.btn-simple');
        this.eValue = this.eGui.querySelector('.my-value');

        // set value into cell
        this.eValue.innerHTML = params.valueFormatted ? params.valueFormatted : params.value;

        // add event listener to button
        this.eventListener = function() {
            // tslint:disable-next-line
            console.log('button was clicked!!');
        };
        this.eButton.addEventListener('click', this.eventListener);
    }

    // gets called once when grid ready to insert the element
    getGui() {
        return this.eGui;
    }

    // gets called whenever the user gets the cell to refresh
    refresh(params: any) {
        // set value into cell again
        this.eValue.innerHTML = params.valueFormatted ? params.valueFormatted : params.value;
        // return true to tell the grid we refreshed successfully
        return true;
    }

    // gets called when the cell is removed from the grid
    destroy() {
        // do cleanup, remove event listener from button
        this.eButton.removeEventListener('click', this.eventListener);
    }
}

// gets called once before the renderer is used

export default MyCellRenderer;

This builds just fine. Now when I pull up my table in the app, I get the somewhat predictable error:

MyCellRenderer(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

So it was expecting a React component exclusively? It appears that I need to provide the rendering operation anyway.

Does anyone know what's going on here/how to resolve this issue? Am I misinterpreting the documentation?

Thanks!

p.s. ag-grid is awesome!

1
Could we see the React markup? Just the ag-Grid render and the column defs would be fine. Just finished with a scenario similar to yours, might be able to find something there for you.playtoh
Also having this problem- did you find a solution?Godwhacker

1 Answers

1
votes

Have just worked out how to do it. There's two sets of 'grid components' properties that you can make available for an instance of a grid for renderers and editors.

The frameworkComponents property contains ones that will be rendered using the framework you're using, such as React or Angular.

The components property contains ones that will be rendered using straight JS.

You can mix these however you wish, for example, assuming you have some renderers that have been exported using this pattern:

export const XRenderer = {
  id: 'someId',
  renderer: function() ... // or class, or whatever
}
// React components
const frameworkComponents = {
  [CheckboxRenderer.id]: CheckboxRenderer.renderer,
  [SelectRenderer.id]: SelectRenderer.renderer
};

// JavaScript components
const components = {
  [RateRenderer.id]: RateRenderer.renderer
};

<Grid
  columnDefs={columnDefinitions}
  theme={theme}
  rowData={rows}

  frameworkComponents={gridComponents} // React components
  components={components} // JavaScript components

  onGridReady={this.onGridReady}          
  context={this.gridContext}
  gridOptions={this.mainGridOptions}
  ...
/>

The information on how to do this is actually in the docs, but not in a particularly useful place: https://www.ag-grid.com/javascript-grid-components/#mixing-javascript-and-framework