reflex icon indicating copy to clipboard operation
reflex copied to clipboard

Feature Request: Shared/Scoped State

Open abulvenz opened this issue 1 year ago β€’ 1 comments

As already proposed on discord, we see many good point to have something like shared state or state that is bound to a specific identifier. The use cases reach from user scope for authentication/settings over chat groups or collaborative list views to news feeds and on-the-fly application settings.

Just to illustrate the idea

class AppState(rx.State):  
  @rx.var
  def user_id(self) -> UUID:
     ...
   ...

@rx.scope(var=AppState.user_id)
class UserState(AppState):
  mySettings: ...
  
@rx.scope(var=AppState.group_id)
class ListState(AppState):
  @rx.cached_var
  def elements(self):
    with rx.session() as session:
      return load_objects_with_group_id(session, AppState.group_id)

# Maybe inheritance from AppState is not needed anymore #2678

From my current understanding of the framework I see e.g. this implementation strategy:

  • Add a scoped_tokenproperty to Event, that calculates from the scope-var (i.e. AppState.user_id or AppState.group_id in above examples).
  • When also the StateManagerMemory implements the same sharding mechanism as StateManagerRedis no further changes have to be made here.
  • In App.process the resulting events have to be sent to socket.io-rooms.
  • Socket room management has to be done on changes to the scope-var.
  • On connect and disconnect have to restore the subscriptions to the rooms.
  • For the case of multiple running app instances redis pubsub has to be implemented, where the scoped-token value can be used as topic name. On events in the subscribed topics those can be sent to the targeted rooms.
  • Events coming from the frontend need to have the scoped-token set by their respective frontend handlers.

When thinking about it, maybe the current state handling is only a special case, so no @rx.scope means the socket/tab scope. Maybe that is a better way to view this architecturally.

There might be many implications that I haven't thought through in detail, yet. E.g. what about @rx.background in those scoped states.

abulvenz avatar Mar 04 '24 09:03 abulvenz

This could also be described as "Dynamic State". Currently, it is not possible to reuse a State multiple times at runtime (f.e. ComponentState in a rx.foreach). All state names/classes must be known at compile time.

Maybe we should implement a DynamicState (still session-based) and later build a SharedState (spread across multiple sessions). DynamicState would not need any pubsub redis.

Edit: I created a PoC for dynamic and global state: https://github.com/reflex-dev/reflex/pull/3671

benedikt-bartscher avatar Jul 12 '24 12:07 benedikt-bartscher