Repository navigation
Hydration drops cached dependent updates from get_delta storage assignments #7571
Description
Regression from #7568, reproduced on current main at 7ccaa4e213fa1f67374002cb28709272866c3d53.
When a get_delta() override assigns a corrected storage value before calling super().get_delta(), the backend value and cached dependents update, but hydration never sends the corrected cached dependent to the client.
Reproduction
Add this test to tests/units/reflex_base/event/processor/test_base_state_processor.py (it uses that module's existing imports and fixtures), then run:
uv run pytest tests/units/reflex_base/event/processor/test_base_state_processor.py -k hydrate_sends_override_mutation_dependents -qasync def test_hydrate_sends_override_mutation_dependents(
wired_app, real_base_state_processor, emitted_deltas, token
):
class StorageState(State):
value: str = rx.LocalStorage("")
@_override_base_method
def get_delta(self) -> Delta:
if "value" in self.dirty_vars and self.value == "stale":
self.value = "fresh"
return super().get_delta()
class DependentState(State):
@rx.var(cache=True, auto_deps=False)
async def copy(self) -> str:
return (await self.get_state(StorageState)).value
DependentState.computed_vars["copy"].add_dependency(
DependentState, StorageState.value
)
wired_app.add_page(
lambda: rx.text(StorageState.value, DependentState.copy), route="/"
)
wired_app._compile_page("index")
name = StorageState.get_full_name()
key = "value" + FIELD_MARKER
boot_name = Event.from_event_type(State.hydrate_and_load())[0].name
payload = {"vars": {f"{name}.{key}": "stale"}}
async with real_base_state_processor as processor:
await (
await processor.enqueue(token, _boot_event(boot_name, payload))
).wait_all()
async with _read_back(real_base_state_processor, token) as root:
assert (await root.get_state(StorageState)).value == "fresh"
assert await (await root.get_state(DependentState)).copy == "fresh"
storage_values = [
delta[name][key]
for _, delta in emitted_deltas
if key in delta.get(name, {})
]
dependent_values = [
delta[DependentState.get_full_name()]["copy" + FIELD_MARKER]
for _, delta in emitted_deltas
if "copy" + FIELD_MARKER in delta.get(DependentState.get_full_name(), {})
]
assert storage_values[-1] == "fresh"
assert dependent_values[-1] == "fresh" # Actual: "stale"Expected / actual
Expected: the correction delta delivers both the changed storage field and its changed cached dependents.
Actual: emitted storage values are ["stale", "fresh"] (snapshot, then correction); the dependent is emitted only as ["stale"]. Reading the backend storage and dependent after hydration returns "fresh".
The fields-only correction pass suppresses computed vars even when a real assignment invalidates them. The following self._clean() then drops their dirty marks.
Verification
The parameterized regression passes 48/48 on the pre-merge base 2fc4a9caa5ce125acd9ce1f2e13cf5910ca5f8d8. On merged main, all 24 stale-input cases fail and all 24 fresh-input controls pass.
Coverage: LocalStorage / SessionStorage / Cookie × hashes on/off × on-load on/off × in-process / Redis state managers. Redis uses the repository's mocked transport. These are event-processor tests inspecting emitted deltas, not a browser reproduction. Release adoption has not been checked.
This is a direct state assignment, not an override that only rewrites its returned delta. The fix should retain #7568's single cached evaluation for unchanged storage and preserve output-only override behavior.
Activity
linear-code commentedon Oct 14, 2026
ENG-13352
fix(state): deliver hydration override mutation dependents