Trying to understand state management

If I have a reusable component with an internal state value = use_reactive(42) linked to an IntSlider and I have multiple of these components on my page, how do I then get the values of all nested slider components to e.g. use in another component? e.g.
@solara.component
def SliderComponent():
slider_value = solara.use_reactive(42)
solara.IntSlider('', slider_value.value, min=0, max=50)

@solara.component
def MyApp():
SliderComponent()
SliderComponent()

SomeComponentThatUsesTheValuesOfAllSliders()
@solara.component
def SliderComponent():
slider_value = solara.use_reactive(42)
solara.IntSlider('', slider_value.value, min=0, max=50)

@solara.component
def MyApp():
SliderComponent()
SliderComponent()

SomeComponentThatUsesTheValuesOfAllSliders()
4 Replies
Dave Hirschfeld
Dave Hirschfeld•3mo ago
Is the recommended way to inject reactive variables into reusable components from the outer scope? e.g.
@solara.component
def SliderComponent(value: int | Reactive[int] = 0) -> None:
if not isinstance(value, Reactive):
slider_value = solara.use_reactive(value)
else:
slider_value = value
solara.IntSlider('', slider_value.value, min=0, max=50)

@solara.component
def MyApp():
slider1 = solara.use_reactive(21)
slider2 = solara.use_reactive(42)
SliderComponent(slider1)
SliderComponent(slider2)
SomeComponentThatUsesTheValuesOfAllSliders(
slider1, slider2,
)
@solara.component
def SliderComponent(value: int | Reactive[int] = 0) -> None:
if not isinstance(value, Reactive):
slider_value = solara.use_reactive(value)
else:
slider_value = value
solara.IntSlider('', slider_value.value, min=0, max=50)

@solara.component
def MyApp():
slider1 = solara.use_reactive(21)
slider2 = solara.use_reactive(42)
SliderComponent(slider1)
SliderComponent(slider2)
SomeComponentThatUsesTheValuesOfAllSliders(
slider1, slider2,
)
MaartenBreddels
MaartenBreddels•3mo ago
Hi Dave, good questions. use_reactive already does what you want to do, so you can remove the if (actually hooks - function prefixed with use_ - should not be conditional). I've put together a working example that demos a few of these ideas:
import solara

value_app = solara.reactive(3)

@solara.component
def SliderComponent(label, value: int | solara.Reactive[int] = 0) -> None:
# will convert to reactive, if not already a reactive
slider_value = solara.use_reactive(value)
solara.IntSlider(label, slider_value, min=0, max=50)


@solara.component
def ValueOnlyComponent(*values: list[int]):
solara.Text(repr(values))

# this component is not reusable (pratically speaking)
# because it uses application wide state.
# it's main use is seperation of concerns, code organization
# and possibly putting the same component in two places
@solara.component
def SliderApp() -> None:
solara.IntSlider('SliderApp', value_app, min=0, max=50)


@solara.component
def Page():
slider1 = solara.use_reactive(21)
slider2 = solara.use_reactive(42)
with solara.Card("State management"):
# note that slider2.value is never updated, because it's being pass by value
ValueOnlyComponent(slider1.value, slider2.value, value_app.value)
SliderComponent('reactive slider1', slider1)
SliderComponent('value slider2', slider2.value) # passes value, so will not sync back the value
# application wide state in combination with a reusable component that can take a reactive value
SliderComponent('using value_app', value_app)
SliderApp()
import solara

value_app = solara.reactive(3)

@solara.component
def SliderComponent(label, value: int | solara.Reactive[int] = 0) -> None:
# will convert to reactive, if not already a reactive
slider_value = solara.use_reactive(value)
solara.IntSlider(label, slider_value, min=0, max=50)


@solara.component
def ValueOnlyComponent(*values: list[int]):
solara.Text(repr(values))

# this component is not reusable (pratically speaking)
# because it uses application wide state.
# it's main use is seperation of concerns, code organization
# and possibly putting the same component in two places
@solara.component
def SliderApp() -> None:
solara.IntSlider('SliderApp', value_app, min=0, max=50)


@solara.component
def Page():
slider1 = solara.use_reactive(21)
slider2 = solara.use_reactive(42)
with solara.Card("State management"):
# note that slider2.value is never updated, because it's being pass by value
ValueOnlyComponent(slider1.value, slider2.value, value_app.value)
SliderComponent('reactive slider1', slider1)
SliderComponent('value slider2', slider2.value) # passes value, so will not sync back the value
# application wide state in combination with a reusable component that can take a reactive value
SliderComponent('using value_app', value_app)
SliderApp()
Run and edit this code snippet at PyCafe Hope this helps (btw, you can run and edit the code snippet if you click the link)
Dave Hirschfeld
Dave Hirschfeld•3mo ago
Thanks @MaartenBreddels! I'm just trying to figure out how best to structure a solara app and I think that aligns with what I'm envisaging - only use globals for application-wide state and otherwise inject reactive variables into the components 🤔
MaartenBreddels
MaartenBreddels•3mo ago
yeah, that's a good approach. When possible, it's even better to not pass reactive variables if they do not mutate them (just pass .value)