Skip to content

feat: DH-22062: add create_global_state and create_user_state shared state hooks#1324

Draft
mofojed wants to merge 6 commits intodeephaven:mainfrom
mofojed:DH-22062-user-user-state
Draft

feat: DH-22062: add create_global_state and create_user_state shared state hooks#1324
mofojed wants to merge 6 commits intodeephaven:mainfrom
mofojed:DH-22062-user-user-state

Conversation

@mofojed
Copy link
Copy Markdown
Member

@mofojed mofojed commented Mar 23, 2026

Add factory functions for creating shared state that synchronizes across
all components using the same store:

  • create_global_state: shared across all users
  • create_user_state: scoped per effective user (Enterprise), falls back
    to anonymous single store on Community

Includes unit tests (13 tests), e2e test app + Playwright specs,
and documentation with multiple examples.

…state hooks

Add factory functions for creating shared state that synchronizes across
all components using the same store:

- create_global_state: shared across all users
- create_user_state: scoped per effective user (Enterprise), falls back
  to anonymous single store on Community

Includes unit tests (13 tests), e2e test app + Playwright specs,
and documentation with multiple examples.
@mofojed mofojed self-assigned this Mar 23, 2026
@mofojed mofojed requested a review from jnumainville March 24, 2026 13:42

@ui.component
def ui_item_list():
items, _, clear = use_items()
Copy link
Copy Markdown
Member Author

@mofojed mofojed Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one thing I don't particularly like about Python vs. JS. In JS you could destructure this nicely, e.g. const { items, clear } = useItems()
I think closest thing we could do here is NamedTuple:

from typing import NamedTuple

class ItemsState(NamedTuple):
    items: list
    add: callable
    clear: callable

def use_items():
    items, set_items = _use_items()
    ...
    return ItemsState(items=items, add=add, clear=clear)

# Access by name (order-independent):
state = use_items()
state.items
state.clear()

# Or still unpack positionally if you want:
items, add, clear = use_items()

... I kind of like that a bit better, but then you have to define the ItemState and such, which is more verbose.

Or DataClass:

from dataclasses import dataclass

@dataclass
class ItemsState:
    items: list
    add: callable
    clear: callable

Though you still need to declare ItemState.

Or SimpleNamespace

from types import SimpleNamespace

def use_items():
    ...
    return SimpleNamespace(items=items, add=add, clear=clear)

state = use_items()
state.items

Seems to be the simplest. But doesn't give you any autocomplete/type suggestions. I don't think it's necessary for an example, and AI will be able to figure it out anyways... but should I put that in the example?

@dsmmcken @jnumainville any thoughts/comments about that?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically I think that should just be left up to the user when building their custom hook, and this example is fine for showing you can build a custom hook. AI should be able to figure it out anyways.

Copy link
Copy Markdown
Collaborator

@jnumainville jnumainville Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I wish there was something like destructuring in Python. I don't think it's worth adding any details on that to an example. I think most people with python are so used to ignoring args with _
That said, I think operator.itemgetter is something that gets close.

from operator import itemgetter

d = {"x": 1, "y": 2, "z": 3}
x, y = itemgetter("x", "y")(d)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You lose the type hints/inference with itemgetter I think

…_user_state

Allow passing a callable as initial_value, invoked once at store creation
time (matching the useState lazy-initializer pattern). Add unit and e2e
tests for the new behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants