πŸŽƒ Reflex is in Hacktoberfest! Learn more here πŸ•ΈοΈ
DocsBlogChangelog

Search documentation...

/

Star

12k+

[ Learn ]

[ Concepts ]

[ Reference ]

Events


Events are how we modify the state and make the app interactive.

Event Triggers


Event triggers are component props that create an event to be sent to an event handler.
Each component supports a set of events triggers. They are described in each component's documentation in the event trigger section.
Lets take a look at an example below. Try mousing over the heading to change the word.

Welcome

from typing import List


class WordCycleState(rx.State):
    # The words to cycle through.
    text: List[str] = ["Welcome", "to", "Reflex", "!"]

    # The index of the current word.
    index: int = 0

    def next_word(self):
        self.index = (self.index + 1) % len(self.text)

    @rx.var
    def get_text(self) -> str:
        return self.text[self.index]


def index():
    return rx.heading(
        WordCycleState.get_text,
        on_mouse_over=WordCycleState.next_word,
        color="green",
    )
In this example, the heading component has the event trigger, on_mouse_over.
Whenever the user hovers over the heading, the next_word handler will be called to cycle the word. Once the handler returns, the UI will be updated to reflect the new state.

Event Arguments


In some use cases, you want to pass additional arguments to your event handlers. To do this you can bind an event trigger to a lambda, which can call your event handler with the arguments you want.
Try typing a color in an input below and clicking away from it to change the color of the input.
from typing import List


class ArgState(rx.State):
    colors: List[str] = [
        "rgba(222,44,12)",
        "white",
        "#007ac2",
    ]

    def change_color(self, color: str, index: int):
        self.colors[index] = color


def index():
    return rx.hstack(
        rx.input(
            default_value=ArgState.colors[0],
            on_blur=lambda c: ArgState.change_color(c, 0),
            bg=ArgState.colors[0],
        ),
        rx.input(
            default_value=ArgState.colors[1],
            on_blur=lambda c: ArgState.change_color(c, 1),
            bg=ArgState.colors[1],
        ),
        rx.input(
            default_value=ArgState.colors[2],
            on_blur=lambda c: ArgState.change_color(c, 2),
            bg=ArgState.colors[2],
        ),
    )
In this case, in we want to pass two arguments to the event handler change_color, the color and the index of the color to change.
The on_blur event trigger passes the text of the input as an argument to the lambda, and the lambda calls the change_color event handler with the text and the index of the input.

Setters


Every base var has a built-in event handler to set it's value for convenience, calledset_VARNAME.
Say you wanted to change the value of the select component. You could write your own event handler to do this:
1
from typing import List

options: List[str] = ["1", "2", "3", "4"]


class SetterState1(rx.State):
    selected: str = "1"

    def change(self, value):
        self.selected = value


def index():
    return rx.vstack(
        rx.badge(
            SetterState1.selected, color_scheme="green"
        ),
        rx.select(
            options,
            on_change=lambda value: SetterState1.change(
                value
            ),
        ),
    )
Or you could could use a built-in setter for conciseness.
1
from typing import List

options: List[str] = ["1", "2", "3", "4"]


class SetterState2(rx.State):
    selected: str = "1"


def index():
    return rx.vstack(
        rx.badge(
            SetterState2.selected, color_scheme="green"
        ),
        rx.select(
            options,
            on_change=SetterState2.set_selected,
        ),
    )
In this example, the setter for selected is set_selected. Both of these examples are equivalent.
Setters are a great way to make your code more concise. But if you want to do something more complicated, you can always write your own function in the state.

Yielding Multiple Updates


A regular event handler will send a StateUpdate when it has finished running. This works fine for basic event, but sometimes we need more complex logic. To update the UI multiple times in an event handler, we can yield when we want to send an update.
To do so, we can use the Python keyword yield. For every yield inside the function, a StateUpdatewill be sent to the frontend with the changes up to this point in the execution of the event handler.

0

import asyncio


class MultiUpdateState(rx.State):
    count: int = 0

    async def timed_update(self):
        for i in range(5):
            await asyncio.sleep(0.5)
            self.count += 1
            yield


def index():
    return rx.vstack(
        rx.text(MultiUpdateState.count),
        rx.button(
            "Start", on_click=MultiUpdateState.timed_update
        ),
    )
Here is another example of yielding multiple updates with a loading icon.

0

import asyncio


class ProgressExampleState(rx.State):
    count: int = 0
    show_progress: bool = False

    async def increment(self):
        self.show_progress = True
        yield
        # Think really hard.
        await asyncio.sleep(0.5)
        self.count += 1
        self.show_progress = False


def index():
    return rx.cond(
        ProgressExampleState.show_progress,
        rx.circular_progress(is_indeterminate=True),
        rx.heading(
            ProgressExampleState.count,
            on_click=ProgressExampleState.increment,
            _hover={"cursor": "pointer"},
        ),
    )

Calling Event Handlers From Event Handlers


You can call other event handlers from event handlers to keep your code modular. Just use the self.call_handler syntax to run another event handler. As always, you can yield within your function to send incremental updates to the frontend.
0
class CallHandlerState(rx.State):
    count: int = 0
    progress: int = 0

    async def run(self):
        # Reset the count.
        self.set_progress(0)
        yield

        # Count to 10 while showing progress.
        for i in range(10):
            # Wait and increment.
            await asyncio.sleep(0.5)
            self.count += 1

            # Update the progress.
            self.set_progress(i + 1)

            # Yield to send the update.
            yield


def index():
    return rx.vstack(
        rx.badge(
            CallHandlerState.count,
            font_size="1.5em",
            color_scheme="green",
        ),
        rx.progress(
            value=CallHandlerState.progress,
            max_=10,
            width="100%",
        ),
        rx.button("Run", on_click=CallHandlerState.run),
    )

Returning Events From Event Handlers


So far, we have only seen events that are triggered by components. However, an event handler can also return events.
In Reflex, event handlers run synchronously, so only one event handler can run at a time, and the events in the queue will be blocked until the current event handler finishes.The difference between returning an event and calling an event handler is that returning an event will send the event to the frontend and unblock the queue.
Try entering an integer in the input below then clicking out.
0
class CollatzState(rx.State):
    count: int = 0

    def start_collatz(self, count: str):
        """Run the collatz conjecture on the given number."""
        self.count = abs(int(count))
        return CollatzState.run_step

    async def run_step(self):
        """Run a single step of the collatz conjecture."""

        while self.count > 1:
            await asyncio.sleep(0.5)

            if self.count % 2 == 0:
                # If the number is even, divide by 2.
                self.count /= 2
            else:
                # If the number is odd, multiply by 3 and add 1.
                self.count = self.count * 3 + 1
            yield


def index():
    return rx.vstack(
        rx.badge(
            CollatzState.count,
            font_size="1.5em",
            color_scheme="green",
        ),
        rx.input(on_blur=CollatzState.start_collatz),
    )
In this example, we run the Collatz Conjecture on a number entered by the user.
When the on_blur event is triggered, the event handler start_collatz is called. It sets the initial count, then calls run_step which runs until the count reaches 1.

Special Events


Reflex also has built-in special events can be found in the reference.
For example, an event handler can trigger an alert on the browser.
class ServerSideState2(rx.State):
    def alert(self):
        return rx.window_alert("Hello World!")


def index():
    return rx.button(
        "Alert", on_click=ServerSideState2.alert
    )
← VarsSubstates β†’

Copyright Β© 2023 Pynecone, Inc.