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.jsx → 0). 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-core — core/match.py)
- Python 3.13
- Reproduces in a local
reflex run and on Reflex Cloud deploys.
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 matchinguseContext(...)binding. At render the browser throws: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:
Minimal reproduction
rxconfig.py:rx_match_repro/rx_match_repro.py:reflex runand open/→ blank page, and the browser console shows:Compiled output (the bug)
.web/app/routes/_index.jsx(reflex0.9.5.post2):Component()referencesreflex___state____state__...____match_statein theswitchcases but contains noconst ... = useContext(StateContexts....)binding (grep -c useContext _index.jsx→0). Hence theReferenceErroron first render.For contrast,
rx.cond/rx.foreachare extracted into memoized child components that each emit their ownuseContextbinding, 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:
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:So for component branches, the var-data (hooks/imports — including the
useContextfor any substate referenced in thecase[0]conditions, and the subject) is never hoisted into the pageComponent. 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
useContextbinding for any substate referenced inrx.matchcase 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 aReferenceError. Alternatively, reject a constant subject combined with state-var case conditions at compile time.Workaround
Replace the
rx.matchwith chainedrx.cond(which compiles to child components that each binduseContext):Environment
0.9.5.post2(reflex-components-core—core/match.py)reflex runand on Reflex Cloud deploys.