ag-grid CellRenderer - how to render when other row data (props.data) changes?

Karen picture Karen · Mar 16, 2018 · Viewed 9.2k times · Source

I would like to have a cell rendered a certain way based on other data that is available in the props.data object that is tied to the row. But I can't figure out how to get the cell to re-render when that other data changes.

I have a pretty simple data structure I'm trying to display in ag-grid, basically an array of properties, and each has a name, a value, and an isLoading flag that indicates if the property is being updated (like a background refresh could be happening, the data in the grid cell is read-only).

I want the grid to display the name and value columns, and I want the value column to show a spinner icon if isLoading == true.
I am trying to build a custom cell renderer for this, but the cell only re-renders when the value is changed, not when the isLoading flag changes.

In my code's flow, the isLoading flag is set to true, then the value is updated. This triggers the render method and the cell will now show the updated value and the loading spinner. Then the isLoading flag is set to false, but the render method is not being called again, so the cell continues to show the loading spinner.

So my question is, how can I get a cellrenderer to respond to changes in props.data that are not for the field whose value it is showing? In this case, I need to re-render the cell when the props.data.isLoading flag changes.

I am using ag-grid v17 with react and redux (and attempting typescript).
My code looks very similar to the examples here: https://www.ag-grid.com/react-redux-integration-pt1/

My cell renderer was built from the currency renderer in this example: https://www.ag-grid.com/javascript-grid-cell-rendering-components/#example-rendering-using-react-components

Note: I have the grid's deltaRowDataMode set to true, so it is only re-rendering the data that has changed.

This is my column definition:

{
    colId: 'value',
    headerName: 'Value',
    field: 'value',  //the property's value on my data structure
    cellRendererFramework: MyCellRenderer
}

This is my cell renderer:

import React from 'react';
import { ICellRendererParams } from 'ag-grid';

interface RendererState {
    currentValue: string;
    isLoading: boolean;
}

export class MyCellRenderer extends React.Component<ICellRendererParams, RendererState> {
    constructor(props: ICellRendererParams) {
        super(props);
        this.state = { currentValue: props.data.value, isLoading: props.data.isLoading };
    }

    //refresh is only called when the value changes, not when params.data.isLoading changes, so render does not get called
    refresh(params: ICellRendererParams) {
        if (params.value !== this.state.currentValue) {
            this.setState({ currentValue: params.value });
        }
        if (params.data.isLoading !== this.state.isLoading) {
            this.setState({ isLoading: params.data.isLoading });
        }
        return true;
    }

    render() {
        var loadingStyle = {
            display: (this.state.isLoading) ? '' : 'none'
        };
        return (
            <div>
                {this.state.currentValue}
                <div className="pull-right">
                    <span className="fa fa-refresh fa-spin" style={loadingStyle}></span>&nbsp;
                </div>
            </div>
        );
    }

}

This may also be possible using the cell class or styles, but there are other things I'd like to be able to handle in a similar way as well, such as having a cell renderer with a button, and disabling that button if isLoading is true.

Update:
Added a working example here: https://react-owixbq.stackblitz.io/

Answer

Ishwor Timilsina picture Ishwor Timilsina · Mar 26, 2018

Instead of updating state inside refresh(), try using componentWillReceiveProps.

    componentWillReceiveProps(nextProps){
        if(nextProps.data.yourField !== this.props.data.yourField){
            this.setState({ isLoading: false, currentValue: nextProps.data.yourField });
        }
    }