Skip to content

fix(event): handle mixed rx.cond event/function returns in event lambdas#6354

Open
BABTUNA wants to merge 2 commits intoreflex-dev:mainfrom
BABTUNA:fix-6204-cond-event-trigger
Open

fix(event): handle mixed rx.cond event/function returns in event lambdas#6354
BABTUNA wants to merge 2 commits intoreflex-dev:mainfrom
BABTUNA:fix-6204-cond-event-trigger

Conversation

@BABTUNA
Copy link
Copy Markdown
Contributor

@BABTUNA BABTUNA commented Apr 21, 2026

All Submissions:

  • Have you followed the guidelines stated in [CONTRIBUTING.md](https://
    github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) file?
  • Have you checked to ensure there aren't any other open Pull
    Requests
    for the desired
    changed?

Type of change

  • Bug fix (non-breaking change which fixes an issue)

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd
    like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Description

This fixes event-lambda handling for rx.cond(...) expressions that return
mixed event-like values (frontend function branch vs backend event branch).

Before this change, call_event_fn rejected this pattern because the
lambda return was a Var (CustomVarOperation) rather than one of:

  • EventSpec
  • EventHandler
  • EventVar
  • FunctionVar
    That made valid mixed conditional handlers fail during event-chain
    creation.

handles issue #6204

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 21, 2026

Greptile Summary

This PR fixes call_event_fn to accept rx.cond(...) expressions whose branches return heterogeneous event-like types (e.g. a FunctionVar on one branch and an EventSpec on the other). A new inner helper _dispatch_mixed_event_var wraps the runtime-ambiguous Var into a JS arrow function that branches on typeof to either call the frontend function directly or queue it through addEvents.

Confidence Score: 5/5

Safe to merge — the fix is correct and all remaining findings are P2 style/quality suggestions that do not block the primary use case.

No P0 or P1 findings. The over-broad type guard and missing event_actions propagation are both P2: the former is harmless in practice, and the latter is consistent with pre-existing FunctionVar behavior rather than a regression.

packages/reflex-base/src/reflex_base/event/init.py — the new type guard condition and hard-coded event_actions warrant a second look, but neither blocks merge.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/event/init.py Adds _dispatch_mixed_event_var helper and a broadened type-guard in call_event_fn to wrap rx.cond-produced union-type vars into a runtime-dispatching JS arrow function; minor concerns around over-broad type check and hard-coded empty event_actions.
tests/units/test_event.py Adds test_event_chain_create_lambda_allows_conditional_mixed_function_and_event covering the new rx.cond mixed-type lambda path; assertions are appropriate and reuse the existing make_timeout_logger helper.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["call_event_fn(fn, arg_spec)"] --> B["Invoke fn with parsed args"]
    B --> C["Normalize to list: out = [...]"]
    C --> D["For each e in out"]
    D --> E{"isinstance(e, EventHandler)?"}
    E -- Yes --> F["call_event_handler(e, ...)"]
    E -- No --> G{"isinstance(e, EventChain)?"}
    F --> G
    G -- Yes --> H["e = Var.create(e)"]
    G -- No --> I{"isinstance(e, Var) and\nnot EventVar/FunctionVar and\ntypehint_issubclass(type, EventSpec|Callable)?"}
    H --> I
    I -- Yes --> J["_dispatch_mixed_event_var(e)\n→ JS arrow fn with typeof branch"]
    I -- No --> K{"isinstance(e, EventSpec|FunctionVar|EventVar)?"}
    J --> K
    K -- No --> L["Raise EventHandlerValueError"]
    K -- Yes --> M["events.append(e)"]
    M --> D
    J --> N["JS: (...args) => {\n  const __event_or_fn = ...;\n  if (typeof __event_or_fn === 'function')\n    return __event_or_fn(...args);\n  return addEvents([__event_or_fn], args, {});\n}"]
Loading

Reviews (1): Last reviewed commit: "Handle mixed rx.cond event/function retu..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/event/__init__.py
f'if (typeof {event_like_name} === "function") {{'
f"return {event_like_name}(...args);"
"}"
f"return {CompileVars.ADD_EVENTS}([{event_like_name}], args, ({{ }}));"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Chain-level event_actions not propagated to conditional backend events

_dispatch_mixed_event_var hard-codes ({ }) (an empty object) as the event_actions argument to addEvents. If this dispatcher ends up in an EventChain carrying chain-level event_actions (e.g. preventDefault: True), those actions will be silently dropped for any conditional branch that resolves to a backend event. The existing EventVarinvocation.call(...) path threads those actions correctly, but the new FunctionVar wrapper bypasses that path. This is consistent with pre-existing FunctionVar behavior (not a regression), but worth noting for future callers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bit out of the scope of this PR imo

f'if (typeof {event_like_name} === "function") {{'
f"return {event_like_name}(...args);"
"}"
f"return {CompileVars.ADD_EVENTS}([{event_like_name}], args, ({{ }}));"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Extraneous spaces in empty object literal

({{ }}) produces ({ }) — a parenthesised empty object with two interior spaces — in the emitted JavaScript. This is valid but inconsistent with idiomatic {}. Consider {{}} instead.

Suggested change
f"return {CompileVars.ADD_EVENTS}([{event_like_name}], args, ({{ }}));"
f"return {CompileVars.ADD_EVENTS}([{event_like_name}], args, ({{}}));"

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ight will fix

@BABTUNA BABTUNA requested a review from a team as a code owner April 29, 2026 02:12
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 29, 2026

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing BABTUNA:fix-6204-cond-event-trigger (1fb9a92) with main (8f7ad9a)

Open in CodSpeed

@BABTUNA
Copy link
Copy Markdown
Contributor Author

BABTUNA commented Apr 29, 2026

@masenf plz review when you get a chance 🙏🙏🙏

Copy link
Copy Markdown
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this behavior is very dependent on how the generated code actually works, we need to see an integration test (or extend an existing integration test) that exercises this behavior end to end.

Comment on lines +2069 to +2087
return Var(
_js_expr=(
"(...args) => {"
f"const {event_like_name} = {event_like_var!s};"
f'if (typeof {event_like_name} === "function") {{'
f"return {event_like_name}(...args);"
"}"
f"return {CompileVars.ADD_EVENTS}([{event_like_name}], args, ({{}}));"
"}"
),
_var_type=Callable,
_var_data=VarData.merge(
event_like_var._get_all_var_data(),
VarData(
imports=Imports.EVENTS,
hooks={Hooks.EVENTS: None},
),
),
).to(FunctionVar)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer to use ArgsFunctionOperations.create helper with rest and explicit_return arguments.

using a structured Var is helpful because it allows the compiler to optimize the form down the road without changing user-facing code and it tends to be less brittle w.r.t composition, carry var data, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants