AG-Grid React, trouble getting custom cell renderer to update when data changes. Function components are behaving differently than class components
Asked Answered
L

1

6

I'm using AG-Grid throughout a react application, and in several instances I want a cellRenderer to update when grid data changes. In both of the following cases, the cellRenderer loads correctly initially, but doesn't update when data changes:

  1. A user edits an editable cell and changes the value.
  2. The server updates the grid data in redux

Check out this codesandbox

In this example I recreated the first use case with an editable cell, and used a class component and a function component. Using onCellValueChanged with params.api.refreshCells() I was able to get the class component to update, but not the function component.

First question: How do I get react function components to re-render with new props, the same way the class component re-renders in the codesandbox example?

Second question: Is there a better way to update a cellRenderer without un-mounting and re-mounting every cell in the column any time data updates?

Thanks in advance for the help and guidance!

...
    this.state = {
      columnDefs: [
        {
          headerName: "Editable Country",
          editable: true,
          field: "country",
          width: 200
        },
        {
          headerName: "Class Render",
          cellRendererFramework: GridCellClass,
          colId: "class-renderer",
          width: 200
        },
        {
          headerName: "Function Render",
          cellRendererFramework: GridCellFunction,
          colId: "function-renderer",
          width: 200
        }
      ],
      rowData: []
    };
  }

...

            <AgGridReact
              rowModelType="infinite"
              columnDefs={this.state.columnDefs}
              enableColResize={true}
              rowClassRules={{
                california: function(params) {
                  return params.data.country === "California";
                }
              }}
              onCellValueChanged={function(params) {
                return params.api.refreshCells({
                  force: true,
                  columns: ["class-renderer", "function-renderer"]
                });
              }}
              onGridReady={this.onGridReady.bind(this)}
              rowData={this.state.rowData}
            />
...
// This one updates!
import React, { Component } from "react";

class GridCellClass extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    const { data } = this.props;

    return (
      <div>
        {data.country === "California" ? "CALIFORNIA!!!" : "Not California"}
      </div>
    );
  }
}

export default GridCellClass;
// This one does not update.
import React from "react";

function GridCellFunction(props) {
  const { data } = props;

  return (
    <div>
      {data.country === "California" ? "CALIFORNIA!!!" : "Not California"}
    </div>
  );
}

export default GridCellFunction;
Leakage answered 14/5, 2020 at 21:28 Comment(0)
A
5

1.Cell Renderer Function

A cell renderer function should be used when you dont have refresh requirements and this is mentioned in the ag-grid docs.

As per docs-

Use the function variant of a cell renderer if you have no refresh or cleanup requirements (ie you don't need to implement the refresh or destroy functions).

This is the reason why your cell renderer function does not refresh.

To solve your problem, you can do this -

      onCellValueChanged={function(params) {
        if(params.column.getId() === 'country') {
          params.api.redrawRows();
        }
      }}

2.Cell Renderer Component

The reason your GridCellClass works on api.refreshCells() is because the grid handles the refresh() for you since you have not implemented refresh() in your GridCellClass component.

What that means is your component will be destroyed and recreated if the underlying data changes.

3.RefreshCells

Also note that using api.refreshCells() won't work as is because ag-grid uses change detection to refresh cells while determining what cells to refresh and since essentially the value of your other 2 columns do not change but infact they are changed in the cellRenderer itself.

However the below works for GridCellClass because you disable change detection by passing force:true,

       params.api.refreshCells({
          force: true
        });

From docs-

Change detection will be used to refresh only cells who's display cell values are out of sync with the actual value. If using a cellRenderer with a refresh method, the refresh method will get called.

Aiello answered 15/5, 2020 at 0:16 Comment(4)
redrawRows works for both, that’s great. Adding a refresh method in GridCellClass seems like a better idea that won’t cause unnecessary remounts. I found examples of refresh methods being used in class components, but couldn’t find any for function components. Do you know if that’s supported?Leakage
Refresh is not supported in functions as I mentioned in the answer as well as in the docs - Use the function variant of a cell renderer if you have no refresh or cleanup requirements (ie you don't need to implement the refresh or destroy functions).Aiello
Got it. I can see that being an issue if I want to use react hooks inside the cell renderer component, the component would need to be a function not a class. It’s still a react component, not a “simple function”. The docs say it requires getGui()Leakage
"params.api.refreshCells({ force: true });" works!!!Hilda

© 2022 - 2024 — McMap. All rights reserved.