Skip to content

Commit b425f01

Browse files
committed
Fix memo component ordering and Var-backed page title handling
Move memo component compilation after app_root resolution so app-wrap components are included. Fix DefaultPagePlugin to preserve Var-backed titles instead of replacing them with the default string.
1 parent 15f363b commit b425f01

4 files changed

Lines changed: 75 additions & 16 deletions

File tree

reflex/compiler/compiler.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,18 +1085,6 @@ def compile_app(
10851085
]
10861086
all_imports = compile_ctx.all_imports
10871087

1088-
(
1089-
memo_components_output,
1090-
memo_components_result,
1091-
memo_components_imports,
1092-
) = compile_memo_components(
1093-
dict.fromkeys(CUSTOM_COMPONENTS.values()),
1094-
tuple(EXPERIMENTAL_MEMOS.values()),
1095-
)
1096-
compile_results.append((memo_components_output, memo_components_result))
1097-
all_imports = utils.merge_imports(all_imports, memo_components_imports)
1098-
progress.advance(task)
1099-
11001088
if (
11011089
code_uses_state_contexts(compile_ctx.stateful_components_code)
11021090
and app._state is None
@@ -1117,6 +1105,18 @@ def compile_app(
11171105
app_root = app._app_root(app_wrappers)
11181106
all_imports = utils.merge_imports(all_imports, app_root._get_all_imports())
11191107

1108+
(
1109+
memo_components_output,
1110+
memo_components_result,
1111+
memo_components_imports,
1112+
) = compile_memo_components(
1113+
dict.fromkeys(CUSTOM_COMPONENTS.values()),
1114+
tuple(EXPERIMENTAL_MEMOS.values()),
1115+
)
1116+
compile_results.append((memo_components_output, memo_components_result))
1117+
all_imports = utils.merge_imports(all_imports, memo_components_imports)
1118+
progress.advance(task)
1119+
11201120
compile_results.append(
11211121
compile_document_root(
11221122
app.head_components,

reflex/compiler/plugins/builtin.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,13 @@ def eval_page(
5353
component = compiler.into_component(page_fn)
5454
component = Fragment.create(component)
5555

56+
title = getattr(page, "title", None)
5657
meta_args = {
57-
"title": getattr(page, "title", None)
58-
or make_default_page_title(get_config().app_name, page.route),
58+
"title": (
59+
title
60+
if title is not None
61+
else make_default_page_title(get_config().app_name, page.route)
62+
),
5963
"image": getattr(page, "image", ""),
6064
"meta": getattr(page, "meta", ()),
6165
}

tests/units/compiler/test_plugins.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
)
2626
from reflex_core.utils import format as format_utils
2727
from reflex_core.utils.imports import ImportVar, collapse_imports, merge_imports
28+
from reflex_core.vars.base import Var
2829

2930
from reflex.app import UnevaluatedPage
3031
from reflex.compiler import compiler
@@ -40,8 +41,8 @@
4041
class FakePage:
4142
route: str
4243
component: Callable[[], Component]
43-
title: str | None = None
44-
description: str | None = None
44+
title: Var | str | None = None
45+
description: Var | str | None = None
4546
image: str = ""
4647
meta: tuple[dict[str, Any], ...] = ()
4748

@@ -790,6 +791,38 @@ def test_compile_context_compiles_pages_and_matches_legacy_output() -> None:
790791
assert page_ctx.output_code == expected_output
791792

792793

794+
def test_default_page_plugin_handles_var_backed_title_like_legacy_compiler() -> None:
795+
page = UnevaluatedPage(
796+
component=lambda: Fragment.create(),
797+
route="/var-title",
798+
title=Var(_js_expr="pageTitle", _var_type=str),
799+
description=None,
800+
image="",
801+
on_load=None,
802+
meta=(),
803+
context={},
804+
)
805+
hooks = CompilerHooks(plugins=(DefaultPagePlugin(),))
806+
compile_ctx = create_compile_context(hooks)
807+
808+
with compile_ctx:
809+
page_ctx = hooks.eval_page(
810+
page.component,
811+
page=page,
812+
compile_context=compile_ctx,
813+
)
814+
815+
assert page_ctx is not None
816+
817+
legacy_component = compiler.compile_unevaluated_page(
818+
page.route,
819+
page,
820+
None,
821+
None,
822+
)
823+
assert page_ctx.root_component.render() == legacy_component.render()
824+
825+
793826
def test_compile_context_rejects_duplicate_routes() -> None:
794827
pages = [
795828
FakePage(route="/duplicate", component=lambda: Fragment.create()),

tests/units/test_app.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,28 @@ def test_app_wrap_compile_theme(
20852085
assert expected.split(",") == function_app_definition.split(",")
20862086

20872087

2088+
def test_compile_writes_app_wrap_memo_components(
2089+
compilable_app: tuple[App, Path],
2090+
mocker,
2091+
) -> None:
2092+
"""App-wrap memo components are emitted to the shared components module."""
2093+
conf = rx.Config(app_name="testing")
2094+
mocker.patch("reflex_core.config._get_config", return_value=conf)
2095+
app, web_dir = compilable_app
2096+
2097+
app.add_page(rx.box("Index"), route="/")
2098+
app._compile()
2099+
2100+
components_js = (
2101+
web_dir
2102+
/ constants.Dirs.UTILS
2103+
/ f"{constants.PageNames.COMPONENTS}{constants.Ext.JSX}"
2104+
).read_text()
2105+
2106+
assert "export const DefaultOverlayComponents" in components_js
2107+
assert "export const MemoizedToastProvider" in components_js
2108+
2109+
20882110
@pytest.mark.parametrize(
20892111
"react_strict_mode",
20902112
[True, False],

0 commit comments

Comments
 (0)