Skip to content

rx.match: state vars in case conditions (component branches) emit unbound useContext → ReferenceError: Can't find variable: <substate> #6675

Description

@picklelo

Description

When rx.match(...) returns components in its cases and a state var is used in a case condition (e.g. (MyState.x > 0, some_component)), the compiled page references the substate's React context variable but never emits the matching useContext(...) binding. At render the browser throws:

ReferenceError: Can't find variable: reflex___state____state__<app>___states___<module>____<state>

This happens whenever a state var appears only in the match conditions (not in the subject) with component return values — most commonly via the idiomatic "match on a literal subject" pattern:

rx.match(True, (State.x > 0, comp_a), (State.y > 0, comp_b), default_comp)

Minimal reproduction

rxconfig.py:

import reflex as rx

config = rx.Config(app_name="rx_match_repro")

rx_match_repro/rx_match_repro.py:

import reflex as rx


class MatchState(rx.State):
    a: int = 0
    b: int = 0


def index() -> rx.Component:
    return rx.match(
        True,
        (MatchState.a > 0, rx.text("a positive")),
        (MatchState.b > 0, rx.text("b positive")),
        rx.text("neither"),
    )


app = rx.App()
app.add_page(index)

reflex run and open / → blank page, and the browser console shows:

ReferenceError: Can't find variable: reflex___state____state__rx_match_repro___rx_match_repro____match_state

Compiled output (the bug)

.web/app/routes/_index.jsx (reflex 0.9.5.post2):

export default function Component() {
  return (
    jsx(Fragment,{},jsx(Fragment,{},(() => {
  switch (JSON.stringify(true)) {
    case JSON.stringify((reflex___state____state__rx_match_repro___rx_match_repro____match_state.a_rx_state_ > 0)):
      return jsx(RadixThemesText,{as:"p"},"a positive");
    case JSON.stringify((reflex___state____state__rx_match_repro___rx_match_repro____match_state.b_rx_state_ > 0)):
      return jsx(RadixThemesText,{as:"p"},"b positive");
    default:
      return jsx(RadixThemesText,{as:"p"},"neither");
  }
})()), ... )
  )
}

Component() references reflex___state____state__...____match_state in the switch cases but contains no const ... = useContext(StateContexts....) binding (grep -c useContext _index.jsx0). Hence the ReferenceError on first render.

For contrast, rx.cond/rx.foreach are extracted into memoized child components that each emit their own useContext binding, so they work.

Root cause

reflex_components_core/core/match.py, Match._create_match_cond_var_or_component:

The Var-return branch merges the case-condition var-data correctly:

return Var(
    _js_expr=format.format_match(...),
    _var_type=default._var_type,
    _var_data=VarData.merge(
        match_cond_var._get_all_var_data(),
        *[el._get_all_var_data() for case in match_cases for el in case[0]],  # <-- conditions
        *[case[1]._get_all_var_data() for case in match_cases],
        default._get_all_var_data(),
    ),
)

But the BaseComponent-return branch does not propagate the subject/condition var-data to the rendered Match component — it only forwards the branch components as children:

if isinstance(match_cases[0][1], BaseComponent):
    if default is None:
        default = Fragment.create()
    return Fragment.create(
        cls._create(
            cond=match_cond_var,
            match_cases=match_cases,
            default=default,
            children=[case[1] for case in match_cases] + [default],  # only branch components
        )
    )

So for component branches, the var-data (hooks/imports — including the useContext for any substate referenced in the case[0] conditions, and the subject) is never hoisted into the page Component. With a literal subject the subject contributes no var-data either, so nothing is emitted at all and the condition references are left unbound.

Likely related to the earlier #2393 ("Need to recursively enumerate VarData for all rx.match components"), which addressed return values; the condition var-data in the component branch still appears to be dropped.

Expected behavior

The compiler should emit the useContext binding for any substate referenced in rx.match case conditions when the branches are components (e.g. include the condition vars in the Match component's _get_vars() / var-data), so the page renders instead of throwing a ReferenceError. Alternatively, reject a constant subject combined with state-var case conditions at compile time.

Workaround

Replace the rx.match with chained rx.cond (which compiles to child components that each bind useContext):

rx.cond(
    MatchState.a > 0,
    rx.text("a positive"),
    rx.cond(MatchState.b > 0, rx.text("b positive"), rx.text("neither")),
)

Environment

  • Reflex 0.9.5.post2 (reflex-components-corecore/match.py)
  • Python 3.13
  • Reproduces in a local reflex run and on Reflex Cloud deploys.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions