Compute client storage dependents once per hydrate

Merged
FA
2d ago
+179-10

Fixes #7508. A smaller alternative to #7515.

Problem

hydrate_and_load sends the snapshot first. Then it passes the browser's client storage values through get_delta overrides again. It marked those values dirty with _mark_dirty. That invalidated their computed dependents, so each dependent was evaluated a second time. The snapshot had already sent them, computed from the same values. In reflex-local-auth, this runs the authenticated_user database query twice on every page load.

Fix

The second pass now builds a delta that holds only the dirty fields. While _send_fields_only is set, build_delta does not invalidate or send computed vars. The pass runs inside the handler, so the flag does not reach any other delta.

The behavior stays the same as on main:

  • Overrides see each browser value once.
  • What an override returns still reaches browser storage.
  • An override still changes only the value sent to the browser, as on every other event.

Testing

A new test, test_hydrate_computes_client_storage_dependents_once, fails on main and passes with the fix. It checks a cached dependent in the same state and a cached dependent in a sibling state. It also checks that an uncached var is not evaluated more often than in a hydrate without stored values.

I also ran an example app in Chrome against main, with stored values set. The app has a cached dependent, a sibling dependent, an uncached var, an override that rewrites a value, and an override that drops a key. Per reload:

mainthis PR
Cached dependent, same state21
Cached dependent, sibling state21
Uncached var33
Override calls11

The displayed values, the stored values and the backend values stay the same as on main.

The pass now sends its own delta, so a hydrate with stored values sends one more websocket message than on main. This PR does not change that.