Skip to content

Commit 50e21d6

Browse files
authored
Merge pull request #454 from BSd3v/fix-infinite-no-rows
2 parents 87233d5 + e8f61c5 commit 50e21d6

3 files changed

Lines changed: 136 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Links "DE#nnn" prior to version 2.0 point to the Dash Enterprise closed-source D
1313
- Added test for `OBJ_MAYBE_FUNCTION_OR_MAP_MAYBE_FUNCTIONS` to test that the value is an object before parsing,
1414
- this allows for reused keys to not comply with being an object
1515

16+
### Fixed
17+
- [#454](https://github.com/plotly/dash-ag-grid/pull/454) fixes issue where a rowCount of 0 would cause the grid not to display new data
1618

1719
## [35.2.0] - 2026-04-03
1820
### Added

src/lib/fragments/AgGrid.react.js

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ export function DashAgGrid(props) {
274274
const [openGroups, setOpenGroups] = useState({});
275275
const [columnState_push, setColumnState_push] = useState(true);
276276
const [rowTransactionState, setRowTransactionState] = useState(null);
277+
const resettingCount = useRef(false);
278+
const prevRowCountRef = useRef(null);
279+
const resetTimeoutRef = useRef(null);
277280

278281
const components = useMemo(
279282
() => ({
@@ -841,9 +844,11 @@ export function DashAgGrid(props) {
841844
return {
842845
getRows(params) {
843846
getRowsParams.current = params;
847+
if (resettingCount.current) {
848+
return;
849+
}
844850
customSetProps({getRowsRequest: params});
845851
},
846-
847852
destroy() {
848853
getRowsParams.current = null;
849854
},
@@ -1408,13 +1413,50 @@ export function DashAgGrid(props) {
14081413
}
14091414
}, [props.id]);
14101415

1411-
// Handle infinite scrolling datasource
1416+
// handle getRowsResponse
14121417
useEffect(() => {
1413-
if (isDatasourceLoadedForInfiniteScrolling()) {
1418+
if (isDatasourceLoadedForInfiniteScrolling() && getRowsParams.current) {
1419+
const params = getRowsParams.current;
1420+
14141421
const {rowData, rowCount} = props.getRowsResponse;
1415-
getRowsParams.current.successCallback(rowData, rowCount);
1422+
1423+
// If we were previously at 0 rows, tell ag‑Grid the new count first,
1424+
// then defer the successCallback so ag‑Grid has processed setRowCount.
1425+
// This avoids an edge case where ag‑Grid ignores the successCallback because it thinks the
1426+
// request is already fulfilled, since the row count is >0, but then doesn't render any rows
1427+
// because it hasn't processed the new row count yet.
1428+
// We do not use purge, reset on the cache or datasource refresh here,
1429+
// since those would trigger a new getRows request, which we do not want since we already have the new data
1430+
// and just need to get ag‑Grid to process the new row count and render it.
1431+
if (
1432+
prevRowCountRef.current !== null &&
1433+
prevRowCountRef.current === 0
1434+
) {
1435+
resettingCount.current = true;
1436+
params.api.setRowCount(rowCount, false);
1437+
1438+
resetTimeoutRef.current = setTimeout(() => {
1439+
resettingCount.current = false;
1440+
const p = getRowsParams.current;
1441+
if (p) {
1442+
p.successCallback(rowData, rowCount);
1443+
}
1444+
}, 0);
1445+
} else {
1446+
params.successCallback(rowData, rowCount);
1447+
}
1448+
1449+
prevRowCountRef.current = rowCount;
14161450
customSetProps({getRowsResponse: null});
14171451
}
1452+
1453+
return () => {
1454+
if (resetTimeoutRef.current) {
1455+
clearTimeout(resetTimeoutRef.current);
1456+
resetTimeoutRef.current = null;
1457+
}
1458+
resettingCount.current = false;
1459+
};
14181460
}, [props.getRowsResponse]);
14191461

14201462
// Handle master detail response

tests/test_infinite_scroll.py

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
"""
44

55
import dash_ag_grid as dag
6-
import dash
7-
from dash import Input, Output, html, dcc, Dash, no_update
8-
from . import utils
9-
import pandas as pd
6+
from dash import Input, Output, html, dcc, Dash, no_update, ctx
107
from dash.testing.wait import until
8+
from . import utils
119
import time
10+
import numpy as np
11+
import pandas as pd
1212

1313

1414
def test_is001_infinite_scroll(dash_duo):
@@ -247,4 +247,87 @@ def scroll(n):
247247
for x in range(8):
248248
dash_duo.find_element("#scroll").click()
249249
time.sleep(3) # pausing to emulate separation because user inputs
250-
assert list(filter(lambda i: i.get("level") != "WARNING", dash_duo.get_logs())) == []
250+
assert list(filter(lambda i: i.get("level") != "WARNING", dash_duo.get_logs())) == []
251+
252+
def test_is003_infinite_scroll_clear(dash_duo):
253+
app = Dash(__name__)
254+
data = pd.DataFrame(
255+
{
256+
"id": [str(x) for x in range(1000)],
257+
"value": np.random.rand(1000),
258+
}
259+
)
260+
261+
clear_button = html.Button("Clear", id="clear")
262+
reset_button = html.Button("Reset", id='reset')
263+
264+
grid = dag.AgGrid(
265+
columnDefs=[
266+
{"field": "id"},
267+
{"field": "value"},
268+
],
269+
getRowId="params.data.id",
270+
rowModelType="infinite",
271+
id="grid"
272+
)
273+
274+
test_data = []
275+
276+
277+
@app.callback(
278+
Output(grid, "getRowsResponse"),
279+
Input(grid, "getRowsRequest"),
280+
Input(clear_button, "n_clicks"),
281+
Input(reset_button, "n_clicks"),
282+
prevent_initial_call=True,
283+
)
284+
def update_rfq_grid_rows(
285+
request,
286+
n_clicks_clear,
287+
n_clicks_reset,
288+
):
289+
if ctx.triggered_id == "clear":
290+
response = {
291+
"rowData": [], "rowCount": 0,
292+
}
293+
return response
294+
295+
if request is None:
296+
partial_df = data.head(100)
297+
else:
298+
partial_df = data.iloc[request["startRow"] : request["endRow"]]
299+
300+
response = {
301+
"rowData": partial_df.to_dict("records"),
302+
"rowCount": len(data.index),
303+
}
304+
test_data.append('response')
305+
return response
306+
307+
app.layout = html.Div(
308+
children=[
309+
clear_button,
310+
reset_button,
311+
grid,
312+
]
313+
)
314+
315+
dash_duo.start_server(app)
316+
317+
grid_dom = utils.Grid(dash_duo, 'grid')
318+
grid_dom.wait_for_cell_text(0, 0, "0")
319+
320+
for x in range(2, 5):
321+
dash_duo.find_element("#clear").click()
322+
until(
323+
lambda: len(
324+
dash_duo.find_elements(
325+
"#grid .ag-center-cols-container > *"
326+
)
327+
)
328+
== 0,
329+
timeout=3,
330+
)
331+
dash_duo.find_element("#reset").click()
332+
grid_dom.wait_for_cell_text(0, 0, "0")
333+
assert x == len(test_data) # make sure the callback was called the expected number of times

0 commit comments

Comments
 (0)