Skip to content

Uncaught TypeError "Cannot read properties of null (reading 'isDestroyed')" in onSortChanged when a grid initializes with initialState sortModel #466

Description

@timegates-code

Description

When a grid is created with a sort applied at init time (e.g. via
dashGridOptions.initialState.sort.sortModel), the browser console
shows an uncaught TypeError at grid creation:

AgGrid.react.js:735 Uncaught TypeError: Cannot read properties of
null (reading 'isDestroyed')
    at AgGrid.react.js:735:22
    at main.esm.mjs:41889:13
    at qS.wrapOutgoing (main.esm.mjs:34897:39)
    ...
    at j.flushAsyncQueue (main.esm.mjs:114:15)

One error is emitted per grid that has an initial sort. The error is
benign in practice (only the first columnState sync back to Dash is
skipped for that event; subsequent sortChanged events work normally),
but it pollutes the console on every page load.

Environment

  • dash-ag-grid 32.3.2 (also verified present in 32.3.4 and 35.2.0
    sources, see below)
  • dash 4.0.0
  • AG Grid community v32 (bundled)
  • Chrome, macOS

Root cause

In src/lib/fragments/AgGrid.react.js, the onSortChanged handler is
the only event handler that dereferences gridApi without a null
guard (v32.3.2 line 735; still identical in v35.2.0 line 754):

const onSortChanged = useCallback(() => {
    const {rowModelType} = props;
    const propsToSet = {};
    if (rowModelType === 'clientSide') {
        propsToSet.virtualRowData = virtualRowData();
    }
    if (!gridApi.isDestroyed()) {        // <-- gridApi can be null here
        propsToSet.columnState = JSON.parse(
            JSON.stringify(gridApi.getColumnState())
        );
    }
    customSetProps(propsToSet);
}, [props.rowModelType, virtualRowData, gridApi, customSetProps]);

All sibling handlers use the guarded form
gridApi && !gridApi?.isDestroyed() (e.g. onPaginationChanged,
onRowDataUpdated), and updateColumnState has an early
if (!gridApi) return;.

Why gridApi is null when the handler fires: AG Grid v32 dispatches
grid events asynchronously (dispatchAsync -> setTimeout ->
flushAsyncQueue). When a grid initializes with a sort (initialState
sortModel), a sortChanged event is queued during grid creation
(ColumnApplyStateService dispatches it when applying the initial sort
state). The queue is flushed on a setTimeout tick that can run before
React has committed the setGridApi state update from onGridReady,
so the onSortChanged closure still sees gridApi === null and the
unguarded gridApi.isDestroyed() throws.

Reproduction

import dash_ag_grid as dag
from dash import Dash

app = Dash(__name__)

app.layout = dag.AgGrid(
    id="grid",
    rowData=[{"a": i, "b": 10 - i} for i in range(5)],
    columnDefs=[{"field": "a"}, {"field": "b"}],
    dashGridOptions={
        "initialState": {
            "sort": {"sortModel": [{"colId": "b", "sort": "asc"}]}
        },
    },
)

if __name__ == "__main__":
    app.run(debug=True)

Load the page with the browser console open: the TypeError appears
once at grid creation. With two such grids in the layout, it appears
twice, with identical stacks.

Note: the error does NOT depend on persisted column state
(localStorage cleared -> error unchanged); any sort applied at grid
init triggers it.

Suggested fix

Align onSortChanged with the guard used by every other handler:

if (gridApi && !gridApi?.isDestroyed()) {

The same unguarded line is still present in the latest sources
(v35.2.0, src/lib/fragments/AgGrid.react.js:754), so the fix applies
to the current line as well as the 32.x maintenance branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions