State Management
Reflex state patterns for Syntropy-Journals
Quick Reference
on_mount vs on_load
| Hook | Scope | Timing | Use Case |
|---|---|---|---|
on_load | Page | Before render, on route navigation | Data fetching, auth checks |
on_mount | Component | After DOM mount | UI initialization, tab defaults, starting background loops |
Route -> on_load Mapping
| Route | on_load Handlers | States Initialized |
|---|---|---|
/ | - | - |
/login | - | - |
/dashboard | CheckinState.load_checkins | CheckinState |
/ai-chat | ChatState.load_chat_sessions | ChatState |
/catalog | CatalogState.load_catalog | CatalogState |
/profile | ProfileState.load_profile | ProfileState |
/settings | SettingsState.load_settings | SettingsState |
/notifications | NotificationState.load_notifications | NotificationState |
/partners/dashboard | PartnerPortalState.on_load | PartnerPortalState |
Modular State Chain Pattern
Each complex state is decomposed into a chain of mixins for maintainability:| Layer | Responsibility | Example |
|---|---|---|
_base.py | Imports, logger, type aliases | class CheckinBase(rx.State) |
_vars.py | State variables and computed vars | checkins: list[dict], @rx.var |
_ui_handlers.py | Sync UI event handlers | Modal open/close, tab switching |
_processes.py | Background business logic | @rx.event(background=True) loaders |
_on_load.py | Page on_load orchestration | Calls into _processes methods |
state.py | Final export, combines all layers | class CheckinState(OnLoadMixin) |
syntropy_journals/app/states/shared/checkin/ (and similar for other states)
Event Handler Types
Sync Events (@rx.event)
For fast UI updates that do not perform I/O:
- Runs on the main event loop
- Has exclusive access to
self(noasync with self:needed) - Use for: toggling booleans, setting form values, pagination
Background Events (@rx.event(background=True))
For network calls, DB queries, LLM invocations:
async with self:is the state lock — keep it shortawait self.get_state(OtherState)ONLY works inside background tasks- Do NOT
awaitevent handlers (they are async generators); useyieldinstead - Heavy work (DB, HTTP, LLM) goes outside the lock
yield vs return
Loading Guard Pattern
All data-loading states use a_data_loaded flag to prevent duplicate fetches:
Force Reload Pattern
Reset the guard without clearing data (prevents UI flicker):Periodic Background Sync Pattern
Used byCheckinState for CDC polling (call log sync):
on_mount->start_periodic_sync()(background loop begins)- Loop runs every
poll_intervalseconds on_unmount->stop_periodic_sync()(sets flag, loop exits)
CDC Pipeline Flow
Change Data Capture for external data (e.g., call logs, DIET API):- Fetch: Pull new records from external API
- Sync: Upsert raw records into local DB (idempotent)
- Process: Claim unprocessed records, run through LLM extraction
- Save: Persist structured health entries (medications, food, symptoms)
- Reload: Reset
_data_loadedflags, re-fetch dashboard data
Core State Classes
| State | Location | Purpose |
|---|---|---|
AuthState | states/shared/auth.py | Clerk authentication, user session |
CheckinState | states/shared/checkin/ | Health check-ins, CDC sync loop |
ChatState | states/chat/ | AI chat sessions, streaming, message history |
CatalogState | states/app/catalog/ | Product catalog browsing |
NotificationState | states/app/notification/ | Alerts, reminders |
ProfileState | states/app/profile/ | User health profile |
SettingsState | states/app/settings/ | User preferences |
PartnerPortalState | states/partner/portal.py | Partner portal tabs, store referral stats |
NavbarState | states/layout/navbar.py | Top nav UI state |
SidebarState | states/layout/sidebar.py | Side nav UI state |
Design Principles
- Composition over inheritance — States share patterns via copy-paste, not base classes. Reflex state inheritance creates parent-child coupling in the state tree.
- Thin states, fat functions — States delegate to
functions/db_utils/andfunctions/llm/for business logic. - Guard everything — Every background loader checks
_data_loadedbefore fetching. - User ID from AuthState — Always
await self.get_state(AuthState)for the authenticated user’s ID.
Related Documentation
- Store Referral — Store referral attribution pipeline and portal integration
- Lifecycle — Startup sequence and service registry
- Debug Guide — Common issues and fixes