reflex icon indicating copy to clipboard operation
reflex copied to clipboard

Support setting values at ComponentState creation time in `get_component`

Open TimChild opened this issue 1 year ago β€’ 2 comments

I'd like to be able to create re-usable components, but providing some initial default values when I create them.

For example:

def AppFunc():
    import reflex as rx

    class Counter(rx.ComponentState):
        # Define vars that change.
        count: int = 0

        # Define event handlers.
        def increment(self):
            self.count += 1

        def decrement(self):
            self.count -= 1

        @classmethod
        def get_component(cls, start_value: int = 1, **props):
            cls.count = start_value
            return rx.hstack(
                rx.button("Decrement", on_click=cls.decrement,
                          id="button-decrement"),
                rx.text(cls.count, id="counter-value"),
                rx.button("Increment", on_click=cls.increment,
                          id="button-increment"),
                **props,
            )

    app = rx.App(state=rx.State)

    @rx.page()
    def index() -> rx.Component:
        return rx.container(
            Counter.create(start_value = 5),
        )    

This would ideally create a counter that has a starting value of 5, but otherwise would work like a normal counter.

At the moment, while the counter does show 5, it also stops working.

I'm pretty sure this is because cls.count is actually an rx.Var, and it is being replaced with just an integer. So then the layout is being created with a fixed integer rather than the Var instance.

I think the solution to this will also relate to #3711 << Which is also something I would like to be able to do.

As a sort of workaround, you can instead do:

		...
		def handle_load(self):
		    self.count = self._initial_value
        
        @classmethod
        def get_component(cls, start_value: int = 1, **props):
            cls._initial_value = start_value
            
     	...
     	counter = Counter.create(start_value=5)
     	
     	@rx.page(on_load=counter.State.handle_load)
     	def index() ...

Which then does have the desired behaviour.

But, that's a lot of work to achieve something relatively simple.

Is there a different way of looking at this?

TimChild avatar Aug 09 '24 21:08 TimChild

It's not optimal, but you could try to adjust the default value like this:

cls.get_fields()["count"].default = start_value

benedikt-bartscher avatar Aug 09 '24 22:08 benedikt-bartscher

Thanks! That's a much better temporary solution than mine!

TimChild avatar Aug 09 '24 23:08 TimChild