Reactive user interfaces with pure Python
—
Event system for handling user interactions with proper event delegation and prevention. ReactPy provides a decorator-based approach to event handling that works seamlessly with HTML elements.
The @event decorator creates event handlers with optional event behavior control:
@event(prevent_default: bool = False, stop_propagation: bool = False)
def event_handler(event_data: dict) -> None: ...Parameters:
prevent_default: Whether to prevent the default browser actionstop_propagation: Whether to stop event bubblingReturns: EventHandler instance
The EventHandler class wraps event functions with metadata:
class EventHandler:
function: Callable[[dict], None]
prevent_default: bool
stop_propagation: boolfrom reactpy import component, html, event
@component
def ClickableButton():
@event
def handle_click(event_data):
print(f"Button clicked! Event: {event_data}")
return html.button(
{"onClick": handle_click},
"Click Me"
)@component
def FormWithValidation():
@event(prevent_default=True) # Prevent form submission
def handle_submit(event_data):
# Custom validation logic
if not validate_form():
print("Form invalid!")
return
# Process form submission
submit_form()
return html.form(
{"onSubmit": handle_submit},
html.input({"type": "text", "required": True}),
html.button({"type": "submit"}, "Submit")
)@component
def NestedClickHandlers():
@event
def outer_click(event_data):
print("Outer clicked")
@event(stop_propagation=True) # Stop bubbling
def inner_click(event_data):
print("Inner clicked - won't bubble")
return html.div(
{"onClick": outer_click, "style": {"padding": "20px", "border": "1px solid"}},
"Outer div",
html.button(
{"onClick": inner_click},
"Inner button"
)
)ReactPy supports all standard DOM events:
@component
def EventExamples():
@event
def handle_input(event_data):
value = event_data.get("target", {}).get("value", "")
print(f"Input value: {value}")
@event
def handle_focus(event_data):
print("Input focused")
@event
def handle_blur(event_data):
print("Input blurred")
@event
def handle_keydown(event_data):
key = event_data.get("key", "")
if key == "Enter":
print("Enter key pressed")
return html.div(
html.input({
"onInput": handle_input,
"onFocus": handle_focus,
"onBlur": handle_blur,
"onKeyDown": handle_keydown,
"placeholder": "Type here..."
})
)Event data contains standard DOM event properties:
@component
def EventDataExample():
@event
def handle_mouse_event(event_data):
# Common properties available in event_data:
event_type = event_data.get("type") # "click", "mousedown", etc.
# Mouse events
x = event_data.get("clientX")
y = event_data.get("clientY")
button = event_data.get("button") # 0=left, 1=middle, 2=right
# Keyboard events
key = event_data.get("key")
alt_key = event_data.get("altKey")
ctrl_key = event_data.get("ctrlKey")
shift_key = event_data.get("shiftKey")
# Target element
target = event_data.get("target", {})
value = target.get("value") # For input elements
tag_name = target.get("tagName")
print(f"Event: {event_type} at ({x}, {y})")
return html.div(
{"onMouseDown": handle_mouse_event},
"Click me to see event data"
)Event handlers commonly update component state:
@component
def Counter():
count, set_count = use_state(0)
@event
def increment(event_data):
set_count(count + 1)
@event
def decrement(event_data):
set_count(count - 1)
@event
def reset(event_data):
set_count(0)
return html.div(
html.h2(f"Count: {count}"),
html.button({"onClick": increment}, "+"),
html.button({"onClick": decrement}, "-"),
html.button({"onClick": reset}, "Reset")
)Special patterns for form events:
@component
def ContactForm():
form_data, set_form_data = use_state({"name": "", "email": ""})
@event
def handle_input_change(event_data):
field_name = event_data["target"]["name"]
field_value = event_data["target"]["value"]
set_form_data({
**form_data,
field_name: field_value
})
@event(prevent_default=True)
def handle_submit(event_data):
print(f"Submitting: {form_data}")
# Process form submission
return html.form(
{"onSubmit": handle_submit},
html.input({
"name": "name",
"value": form_data["name"],
"onInput": handle_input_change,
"placeholder": "Name"
}),
html.input({
"name": "email",
"type": "email",
"value": form_data["email"],
"onInput": handle_input_change,
"placeholder": "Email"
}),
html.button({"type": "submit"}, "Submit")
)Install with Tessl CLI
npx tessl i tessl/pypi-reactpy