Skip to content

Commit 0f8c19d

Browse files
authored
ENG-8050: background event updates set final=None (#5898)
* ENG-8050: background event updates set `final=None` When `final=None`, the frontend does not update the `event_processing` flag. * Fix unit tests
1 parent 4ed8ed2 commit 0f8c19d

File tree

5 files changed

+23
-12
lines changed

5 files changed

+23
-12
lines changed

reflex/.templates/web/utils/state.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,9 @@ export const connect = async (
665665
}
666666
}
667667
applyClientStorageDelta(client_storage, update.delta);
668-
event_processing = !update.final;
668+
if (update.final !== null) {
669+
event_processing = !update.final;
670+
}
669671
if (update.events) {
670672
queueEvents(update.events, socket, false, navigate, params);
671673
}

reflex/app.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,11 +1556,14 @@ def all_routes(_request: Request) -> Response:
15561556
)
15571557

15581558
@contextlib.asynccontextmanager
1559-
async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
1559+
async def modify_state(
1560+
self, token: str, background: bool = False
1561+
) -> AsyncIterator[BaseState]:
15601562
"""Modify the state out of band.
15611563
15621564
Args:
15631565
token: The token to modify the state for.
1566+
background: Whether the modification is happening in a background task.
15641567
15651568
Yields:
15661569
The state to modify.
@@ -1581,7 +1584,10 @@ async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
15811584
# When the state is modified reset dirty status and emit the delta to the frontend.
15821585
state._clean()
15831586
await self.event_namespace.emit_update(
1584-
update=StateUpdate(delta=delta),
1587+
update=StateUpdate(
1588+
delta=delta,
1589+
final=True if not background else None,
1590+
),
15851591
token=token,
15861592
)
15871593

reflex/istate/proxy.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ async def __aenter__(self) -> StateProxy:
131131

132132
await self._self_actx_lock.acquire()
133133
self._self_actx_lock_holder = current_task
134-
self._self_actx = self._self_app.modify_state(token=self._self_substate_token)
134+
self._self_actx = self._self_app.modify_state(
135+
token=self._self_substate_token, background=True
136+
)
135137
mutable_state = await self._self_actx.__aenter__()
136138
super().__setattr__(
137139
"__wrapped__", mutable_state.get_substate(self._self_substate_path)

reflex/state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,7 @@ async def _as_state_update(
17801780
return StateUpdate(
17811781
delta=delta,
17821782
events=fixed_events,
1783-
final=final if not handler.is_background else True,
1783+
final=final if not handler.is_background else None,
17841784
)
17851785
except Exception as ex:
17861786
state._clean()
@@ -2714,7 +2714,7 @@ class StateUpdate:
27142714
events: list[Event] = dataclasses.field(default_factory=list)
27152715

27162716
# Whether this is the final state update for the event.
2717-
final: bool = True
2717+
final: bool | None = True
27182718

27192719
def json(self) -> str:
27202720
"""Convert the state update to a JSON string.

tests/units/test_state.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,7 +2102,8 @@ async def test_state_proxy(
21022102
GrandchildState3.get_full_name(): {
21032103
"computed" + FIELD_MARKER: "",
21042104
},
2105-
}
2105+
},
2106+
final=None,
21062107
)
21072108
assert mcall.kwargs["to"] == grandchild_state.router.session.session_id
21082109

@@ -2273,7 +2274,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
22732274
"other",
22742275
],
22752276
}
2276-
}
2277+
},
22772278
)
22782279

22792280
# Explicit wait for background tasks
@@ -2313,7 +2314,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
23132314
}
23142315
},
23152316
events=[],
2316-
final=True,
2317+
final=None,
23172318
)
23182319
for call in emit_mock.mock_calls[1:5]: # pyright: ignore [reportAttributeAccessIssue]
23192320
assert call.args[1] == StateUpdate(
@@ -2323,7 +2324,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
23232324
}
23242325
},
23252326
events=[],
2326-
final=True,
2327+
final=None,
23272328
)
23282329
assert emit_mock.mock_calls[-2].args[1] == StateUpdate( # pyright: ignore [reportAttributeAccessIssue]
23292330
delta={
@@ -2334,7 +2335,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
23342335
}
23352336
},
23362337
events=[],
2337-
final=True,
2338+
final=None,
23382339
)
23392340
assert emit_mock.mock_calls[-1].args[1] == StateUpdate( # pyright: ignore [reportAttributeAccessIssue]
23402341
delta={
@@ -2343,7 +2344,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
23432344
},
23442345
},
23452346
events=[],
2346-
final=True,
2347+
final=None,
23472348
)
23482349

23492350

0 commit comments

Comments
 (0)