[data grid] Fix crash in getRowValue when colDef is undefined#22838
Conversation
`getRowValue` read `colDef.field` before its own `if (!colDef)` guard, so the guard was dead code and any caller passing an undefined `colDef` threw `TypeError: Cannot read properties of undefined (reading 'field')`. This is reachable whenever a `colDef` is looked up by field and the field is not in the current column lookup (e.g. `getColumn(field)` returning `undefined` via `getCellParams`). Reorder the guard so the dereference is safe, and harden the row-spanning `getCellValue` the same way. The premium aggregation value-getter closure also passes a possibly `undefined` column to `getRowValue`; with the guard in place it now returns `undefined` (filtered out when aggregating) instead of crashing. Closes mui#22831 Co-Authored-By: Claude Opus 4.8 <[email protected]>
Deploy previewBundle size
Check out the code infra dashboard for more information about this PR. |
…alue` The Premium overridable param methods dereferenced `colDef.field` before delegating to the (now guarded) community `getRowValue`, so `DataGridPremium.getCellParams(id, unknownField)` still crashed with `TypeError: Cannot read properties of undefined (reading 'field')`. Guard `colDef` in both overrides so the Premium path is as tolerant as the community one. `DataGridPro` was already covered since it does not override these methods. Add regression tests for both Pro and Premium. Co-Authored-By: Claude Opus 4.8 <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR fixes a runtime crash in the Data Grid params/value resolution path when a column definition lookup returns undefined (e.g. apiRef.current.getCellParams(id, 'unknown-field')), by ensuring colDef is guarded before dereferencing colDef.field. It also adds regression tests across Community/Pro/Premium, and hardens related row-spanning and Premium aggregation paths that indirectly rely on getRowValue.
Changes:
- Reorders/strengthens
colDefguarding ingetRowValue(community) and related row-spanninggetCellValue. - Adds regression tests ensuring
getCellParams(id, unknownField)does not throw in Community, Pro, and Premium. - Guards Premium
getRowValue/getRowFormattedValueoverrides and documents why aggregation value-getters may receive anundefinedcolumn.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/x-data-grid/src/hooks/features/rows/gridRowsUtils.ts | Moves the !colDef guard before field dereference in getRowValue to prevent crashes. |
| packages/x-data-grid/src/hooks/features/rows/gridRowSpanningUtils.ts | Adds a colDef guard to row-spanning getCellValue to avoid null dereferences. |
| packages/x-data-grid-premium/src/hooks/features/rows/useGridParamsOverridableMethods.ts | Adds colDef guards to Premium params overrides to avoid dereferencing colDef.field when missing. |
| packages/x-data-grid-premium/src/hooks/features/aggregation/createAggregationLookup.ts | Documents that aggregation can see undefined columns and relies on guarded getRowValue. |
| packages/x-data-grid/src/tests/cells.DataGrid.test.tsx | Adds a regression test for getCellParams(id, unknownField) in Community. |
| packages/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx | Adds a regression test for getCellParams(id, unknownField) in Pro. |
| packages/x-data-grid-premium/src/tests/aggregation.DataGridPremium.test.tsx | Adds a regression test for getCellParams(id, unknownField) in Premium. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address review feedback: type the internal `getRowValue`/`getCellValue` `colDef` params as `GridColDef | undefined` so the guards are no longer dead code against the type, and return `undefined` directly in the Premium `getRowValue` override instead of delegating with an `undefined` colDef. Co-Authored-By: Claude Opus 4.8 <[email protected]>
| if (!colDef) { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
Kind of a Nitpick:
Just like this guard, would it be better to also add a guard on the community API method, that makes sure that the method is not invoked at all if the necessary params (in this case colDef) are not available?
Also, the interface GridParamsApi['getRowValue'] doesn't correctly point out the expected input type for colDef param that supports undefined too now. But changing it may touch the public API interface (possibly be considered a BC too).
So probably either of:
- Add guards on both the API entry points (premium and community), remove the redundant one from the util, and make sure all the entry points to this util go through the API.
- Add a guard only on the util with the updated TS interface that reflects that, and the API methods pass the param without any gate-keeping.
Having guards at both levels feels a little bit redundant.
Wdyt?
There was a problem hiding this comment.
Good point, went with your option 2.
Option 1 isn't quite feasible: the util getRowValue has callers that intentionally bypass the API, most notably row spanning's getCellValue, which calls it directly during state initialization (before apiRef.current.getRowValue is ready -- there's a comment to that effect), plus row grouping and the Premium filter value-getter config. So the util has to stay the defensive layer.
So the single guard now lives in the util (typed colDef: GridColDef | undefined), and the Premium overrides just pass through via optional chaining (colDef?.field) instead of duplicating the guard. The aggregation result selector now accepts an undefined field and returns null for it, which is correct -- no column means no aggregation result, and it falls through to the guarded util.
I propose leaving the GridParamsApi['getRowValue'] interface as GridColDef to avoid the BC concern you also raised; the internal util is the safety net.
Address review feedback: instead of guarding `colDef` in both the Premium param overrides and the community util, keep the single guard in the util (which is also called directly, e.g. during row-spanning state init, so it must stay defensive) and let the Premium overrides pass through via optional chaining. The aggregation result selector now accepts an `undefined` field and returns `null` for it. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…i#22838) Co-authored-by: Claude Opus 4.8 <[email protected]> Co-authored-by: Michel Engelen <[email protected]>
Fixes #22831
What
getRowValue(gridRowsUtils.ts) readcolDef.fieldbefore its ownif (!colDef)guard, so the guard was dead code. Any caller passing anundefinedcolDefcrashed with:This reorders the guard so the dereference is safe, hardens the row-spanning
getCellValuethe same way, documents the premium aggregation value-getter closure (now safe via the guardedgetRowValue), and applies the same guard to the PremiumgetRowValue/getRowFormattedValueoverrides.Why / how it is reachable
A
colDeflooked up by field isundefinedwhen the field is not in the current column lookup -- e.g.getColumn(field)returningundefined, which flows intogetRowValueviagetCellParams->getCellParamsForRow. The minimal reproduction:The fix lives in
getRowValueso it holds regardless of which caller trips it.DataGridProroutes through the communitygetRowValue, so it is covered by the community fix.DataGridPremiumoverridesgetRowValue/getRowFormattedValue(for aggregation results) and dereferencedcolDef.fieldbefore delegating, so it still crashed on the same input -- this PR guards those overrides too.Note on the premium aggregation closure: adding
if (!column) continuethere would have been wrong, as it would move the crash to thevalueGetters[field]!call site. With the guard in place,getRowValue(row, undefined)returnsundefined, which is already filtered out when aggregating.Testing
Added regression tests calling
getCellParams(id, unknownField)forDataGrid(cells.DataGrid.test.tsx),DataGridPro(rows.DataGridPro.test.tsx), andDataGridPremium(aggregation.DataGridPremium.test.tsx). Verified red -> green for each: they throw the reportedTypeErrorbefore the fix and pass after.Verified:
x-data-gridjsdom suite,rowSpanningbrowser tests, fullx-data-grid-premiumunit suite, TypeScript (community + pro + premium), ESLint, and Prettier all pass.