Skip to content

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

@timegates-code

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
    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