|
| 1 | +```python exec |
| 2 | +import reflex as rx |
| 3 | +``` |
| 4 | + |
| 5 | +# Decentralized Event Handlers |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +Decentralized event handlers allow you to define event handlers outside of state classes, providing more flexible code organization. This feature was introduced in Reflex v0.7.10 and enables a more modular approach to event handling. |
| 10 | + |
| 11 | +With decentralized event handlers, you can: |
| 12 | +- Organize event handlers by feature rather than by state class |
| 13 | +- Separate UI logic from state management |
| 14 | +- Create more maintainable and scalable applications |
| 15 | + |
| 16 | +## Basic Usage |
| 17 | + |
| 18 | +To create a decentralized event handler, use the `@rx.event` decorator on a function that takes a state instance as its first parameter: |
| 19 | + |
| 20 | +```python demo exec |
| 21 | +import reflex as rx |
| 22 | + |
| 23 | +class MyState(rx.State): |
| 24 | + count: int = 0 |
| 25 | + |
| 26 | +@rx.event |
| 27 | +def increment(state: MyState, amount: int = 1): |
| 28 | + state.count += amount |
| 29 | + |
| 30 | +def decentralized_event_example(): |
| 31 | + return rx.vstack( |
| 32 | + rx.heading(f"Count: {MyState.count}"), |
| 33 | + rx.hstack( |
| 34 | + rx.button("Increment by 1", on_click=increment()), |
| 35 | + rx.button("Increment by 5", on_click=increment(5)), |
| 36 | + rx.button("Increment by 10", on_click=increment(10)), |
| 37 | + ), |
| 38 | + spacing="4", |
| 39 | + align="center", |
| 40 | + ) |
| 41 | +``` |
| 42 | + |
| 43 | +In this example: |
| 44 | +1. We define a `MyState` class with a `count` variable |
| 45 | +2. We create a decentralized event handler `increment` that takes a `MyState` instance as its first parameter |
| 46 | +3. We use the event handler in buttons, passing different amounts to increment by |
| 47 | + |
| 48 | +## Compared to Traditional Event Handlers |
| 49 | + |
| 50 | +Here's a comparison between traditional event handlers defined within state classes and decentralized event handlers: |
| 51 | + |
| 52 | +```python box |
| 53 | +# Traditional event handler within a state class |
| 54 | +class TraditionalState(rx.State): |
| 55 | + count: int = 0 |
| 56 | + |
| 57 | + @rx.event |
| 58 | + def increment(self, amount: int = 1): |
| 59 | + self.count += amount |
| 60 | + |
| 61 | +# Usage in components |
| 62 | +rx.button("Increment", on_click=TraditionalState.increment(5)) |
| 63 | + |
| 64 | +# Decentralized event handler outside the state class |
| 65 | +class DecentralizedState(rx.State): |
| 66 | + count: int = 0 |
| 67 | + |
| 68 | +@rx.event |
| 69 | +def increment(state: DecentralizedState, amount: int = 1): |
| 70 | + state.count += amount |
| 71 | + |
| 72 | +# Usage in components |
| 73 | +rx.button("Increment", on_click=increment(5)) |
| 74 | +``` |
| 75 | + |
| 76 | +Key differences: |
| 77 | +- Traditional event handlers use `self` to reference the state instance |
| 78 | +- Decentralized event handlers explicitly take a state instance as the first parameter |
| 79 | +- Both approaches use the same syntax for triggering events in components |
| 80 | +- Both can be decorated with `@rx.event` respectively |
| 81 | + |
| 82 | +## Best Practices |
| 83 | + |
| 84 | +### When to Use Decentralized Event Handlers |
| 85 | + |
| 86 | +Decentralized event handlers are particularly useful in these scenarios: |
| 87 | + |
| 88 | +1. **Large applications** with many event handlers that benefit from better organization |
| 89 | +2. **Feature-based organization** where you want to group related event handlers together |
| 90 | +3. **Separation of concerns** when you want to keep state definitions clean and focused |
| 91 | + |
| 92 | +### Type Annotations |
| 93 | + |
| 94 | +Always use proper type annotations for your state parameter and any additional parameters: |
| 95 | + |
| 96 | +```python box |
| 97 | +@rx.event |
| 98 | +def update_user(state: UserState, name: str, age: int): |
| 99 | + state.name = name |
| 100 | + state.age = age |
| 101 | +``` |
| 102 | + |
| 103 | +### Naming Conventions |
| 104 | + |
| 105 | +Follow these naming conventions for clarity: |
| 106 | + |
| 107 | +1. Use descriptive names that indicate the action being performed |
| 108 | +2. Use the state class name as the type annotation for the first parameter |
| 109 | +3. Name the state parameter consistently across your codebase (e.g., always use `state` or the first letter of the state class) |
| 110 | + |
| 111 | +### Organization |
| 112 | + |
| 113 | +Consider these approaches for organizing decentralized event handlers: |
| 114 | + |
| 115 | +1. Group related event handlers in the same file |
| 116 | +2. Place event handlers near the state classes they modify |
| 117 | +3. For larger applications, create a dedicated `events` directory with files organized by feature |
| 118 | + |
| 119 | +```python box |
| 120 | +# Example organization in a larger application |
| 121 | +# events/user_events.py |
| 122 | +@rx.event |
| 123 | +def update_user(state: UserState, name: str, age: int): |
| 124 | + state.name = name |
| 125 | + state.age = age |
| 126 | + |
| 127 | +@rx.event |
| 128 | +def delete_user(state: UserState): |
| 129 | + state.name = "" |
| 130 | + state.age = 0 |
| 131 | +``` |
| 132 | + |
| 133 | +### Combining with Other Event Features |
| 134 | + |
| 135 | +Decentralized event handlers work seamlessly with other Reflex event features: |
| 136 | + |
| 137 | +```python box |
| 138 | +# Background event |
| 139 | +@rx.event(background=True) |
| 140 | +async def long_running_task(state: AppState): |
| 141 | + # Long-running task implementation |
| 142 | + pass |
| 143 | + |
| 144 | +# Event chaining |
| 145 | +@rx.event |
| 146 | +def process_form(state: FormState, data: dict): |
| 147 | + # Process form data |
| 148 | + return validate_data # Chain to another event |
| 149 | +``` |
0 commit comments