4

I'm using latest agGrid enterprise in react + redux app.

The issue is that I'm using built in editing of agGrid and this is modifying directly underlying data i.e. array that is returned from redux store. This is breaking immutability principle. Is there way to use/configure agGrid no to modify the data but to:

  • react to change and call some callback with info about the change
  • then I could update redux store (one item in the array)
  • then agGrid could detect that only single object in array got changed and it would refresh only single row

Thanks

dragonfly
  • 17,407
  • 30
  • 110
  • 219
  • 1
    I have a similar problem here. Are you able to refresh Ag-Grid with new rows once your state (or props) were changed ? – Zied Hf May 23 '19 at 09:52
  • 1
    If you wire up your cell to provide fieldName from which to get data from, editing the same will edit that. AG-grid dont refresh, as it hardcoded to bail out of refreshing by React using `shouldComponentUpdate` to always return false. – Rikin May 30 '19 at 13:42

1 Answers1

10

EDIT: Expanding my answer with more detail.

You have to set a "valueSetter" function, and set it on each column. Easiest way to do that is with the defaultColDef. Quick note, each row in my table is a 'transaction', so you'll see reference to transactions, versus some other form of identification.

For example:

<AgGridReact
      // OPTIONS REQUIRED FOR OPTIMIZATION WITH REACT
      reactNext
      deltaRowDataMode
      getRowNodeId={data => data.transaction_id}

      // OTHER OPTIONS
      rowSelection="multiple"
      editType="fullRow"

      // TABLE DATA
      defaultColDef={{ ...defaultColDef, valueSetter }}
      columnDefs={columns}
      rowData={transactions}

      // DISPLAY
      rowClassRules={rowClassRules}

       // EVENTS
      onGridReady={onGridReady}
      onRowEditingStarted={onRowEditingStarted}
      onRowEditingStopped={onRowEditingStopped}

    />

Don't forget the optimization items required for react and redux. (reactNext, deltaRowDataMode, getRowNodeId).

The defaultColDef I have defined is simple:

defaultColDef: {
  resizable: true,
  sortable: true,
  filter: true,
  editable: true,
},

And the valueSetter is a function defined very simply as:

const valueSetter = (params) => {
  allActions.columnEdit(params.data, params.oldValue, params.newValue, params.colDef);
  return false;
};

Where the allActions.columnEdit is just calling my Redux action I have defined in an import and then bound with connect() and mapDispatchToProps. The return false is what's required to prevent the mutating of the state. Make sure that your redux action handles whatever you need to do. Here's my example:

  case actionTypes.COLUMN_EDIT: {
  // FOR EACH COLUMN EDITED, CHECK IF ANYTHING CHANGED
  // POST CHANGES TO NEW STATE
  if (payload.oldValue !== payload.newValue) {
    const account = 'acct01';
    const column = payload.colDef.field;
    const index = state[account].transactions.findIndex(
      t => t.transaction_id === payload.t.transaction_id,
    );
    return produce(state, (draft) => {
      const transaction = draft[account].transactions[index];
      transaction[column] = payload.newValue;
    });
  }
  return state;
}

Hope this helps. Seems to be a LOT of extra workaround for what should be handled directly by Ag-Grid, but maybe they'll put some future development into it.

https://www.ag-grid.com/javascript-grid-value-setters/

Scott F
  • 116
  • 1
  • 5
  • 1
    Thanks Scott, saved me a bit of time :) – J_P Nov 20 '19 at 14:26
  • Thank you for this. I was wondering did you run into any issues pasting data? As the value setter is run for each cell which sends a dispatch. Been trying to find a solution for it but I can't seem to find anything.... – KevinAdu Mar 11 '22 at 08:00