Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion reflex/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def substate_token(self) -> str:
_EVENT_FIELDS: set[str] = {f.name for f in dataclasses.fields(Event)}

BACKGROUND_TASK_MARKER = "_reflex_background_task"
EVENT_ACTIONS_MARKER = "_rx_event_actions"


@dataclasses.dataclass(
Expand Down Expand Up @@ -2311,6 +2312,7 @@ class EventNamespace:

# Constants
BACKGROUND_TASK_MARKER = BACKGROUND_TASK_MARKER
EVENT_ACTIONS_MARKER = EVENT_ACTIONS_MARKER
_EVENT_FIELDS = _EVENT_FIELDS
FORM_DATA = FORM_DATA
upload_files = upload_files
Expand Down Expand Up @@ -2461,7 +2463,7 @@ def wrapper(
# Store decorator event actions on the function for later processing
event_actions = _build_event_actions()
if event_actions:
func._rx_event_actions = event_actions # pyright: ignore [reportFunctionMemberAccess]
setattr(func, EVENT_ACTIONS_MARKER, event_actions)
return func # pyright: ignore [reportReturnType]

if func is not None:
Expand Down
6 changes: 5 additions & 1 deletion reflex/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from reflex.environment import PerformanceMode, environment
from reflex.event import (
BACKGROUND_TASK_MARKER,
EVENT_ACTIONS_MARKER,
Event,
EventHandler,
EventSpec,
Expand Down Expand Up @@ -686,6 +687,9 @@ def _copy_fn(fn: Callable) -> Callable:
newfn.__annotations__ = fn.__annotations__
if mark := getattr(fn, BACKGROUND_TASK_MARKER, None):
setattr(newfn, BACKGROUND_TASK_MARKER, mark)
# Preserve event_actions from @rx.event decorator
if event_actions := getattr(fn, EVENT_ACTIONS_MARKER, None):
object.__setattr__(newfn, EVENT_ACTIONS_MARKER, event_actions)
return newfn

@staticmethod
Expand Down Expand Up @@ -1163,7 +1167,7 @@ def _create_event_handler(
The event handler.
"""
# Check if function has stored event_actions from decorator
event_actions = getattr(fn, "_rx_event_actions", {})
event_actions = getattr(fn, EVENT_ACTIONS_MARKER, {})

return event_handler_cls(
fn=fn, state_full_name=cls.get_full_name(), event_actions=event_actions
Expand Down
15 changes: 15 additions & 0 deletions tests/units/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -3737,6 +3737,21 @@ def test_bare_mixin_state() -> None:
assert ChildBareMixinState.get_root_state() == State


def test_mixin_event_handler_preserves_event_actions() -> None:
"""Test that event_actions from @rx.event decorator are preserved when inherited from mixins."""

class EventActionsMixin(BaseState, mixin=True):
@rx.event(prevent_default=True, stop_propagation=True)
def handle_with_actions(self):
pass

class UsesEventActionsMixin(EventActionsMixin, State):
pass

handler = UsesEventActionsMixin.handle_with_actions
assert handler.event_actions == {"preventDefault": True, "stopPropagation": True}


def test_assignment_to_undeclared_vars():
"""Test that an attribute error is thrown when undeclared vars are set."""

Expand Down
Loading