refactor(memo): promote memo out of rx._x.memo into rx.memo#6517
refactor(memo): promote memo out of rx._x.memo into rx.memo#6517FarhanAliRaza wants to merge 17 commits into
rx._x.memo into rx.memo#6517Conversation
Move the memo/custom-component machinery from ``reflex/experimental/memo.py`` and ``reflex_base.components.component`` into a dedicated ``reflex_base.components.memo`` module and expose it as ``rx.memo``. ``rx.experimental.memo`` becomes a deprecated alias, and the legacy ``CustomComponent`` index path in the compiler is dropped now that all memos declare their library per-file.
Component-returning `@rx._x.memo` functions can now declare `rx.EventHandler[...]` (and bare `rx.EventHandler`) parameters, which compile to destructured JSX prop callbacks and are wired through `EventChain` at the call site. Var-returning memos still reject event handlers. Refactors per-parameter behavior into a `_MemoParamSpec` table keyed by a new `MemoParamKind` enum (VALUE / CHILDREN / REST / EVENT_TRIGGER), so each kind owns its classification, validation, placeholder construction, call-site binding, and JSX signature emission. Adds a `_MemoCallBinding` accumulator so `_post_init` no longer special-cases prop vs. rest vs. event routing.
Add explicit rx.Component return annotations to memoized helpers across docs and internal packages, and narrow arrow_svg_component's class_name to Var[str] now that rx.memo handles the conversion.
Greptile SummaryThis PR promotes
Confidence Score: 4/5Safe to merge after fixing the misleading error messages in memo.py that still point users to the deprecated rx._x.memo namespace. The memo engine works correctly and the deprecation shim is well-structured. However, all user-facing TypeError messages in packages/reflex-base/src/reflex_base/components/memo.py still say @rx._x.memo — so every validation error a @rx.memo user hits will tell them to use the old, deprecated decorator instead of the canonical one. packages/reflex-base/src/reflex_base/components/memo.py — ~12 error-message strings reference @rx._x.memo instead of @rx.memo. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["@rx.memo decorator"] --> B{Return type annotation}
B -->|"rx.Component"| C["_create_component_definition()"]
B -->|"rx.Var[T]"| D["_create_function_definition()"]
B -->|"missing / other"| E["TypeError raised"]
C --> F["_register_memo_definition()\nMEMOS registry"]
D --> F
F --> G["_create_component_wrapper()\n→ _MemoComponentWrapper"]
F --> H["_create_function_wrapper()\n→ _MemoFunctionWrapper"]
G --> I["User calls wrapper(...)"]
H --> I
I --> J["_bind_value / _bind_event_trigger\nper MemoParam"]
J --> K["MemoComponent._create()"]
F --> L["compile_memo_components()\nMEMOS.values()"]
L -->|"MemoComponentDefinition"| M["compile_experimental_component_memo()"]
L -->|"MemoFunctionDefinition"| N["compile_experimental_function_memo()"]
M --> O[".jsx file per memo"]
N --> O
P["rx._x.memo property"] -->|"deprecation warning"| A
Q["from reflex.experimental.memo import memo\nsys.modules swap"] -->|"deprecation warning at import"| A
Reviews (2): Last reviewed commit: "chore(memo): tidy compile_app splats and..." | Re-trigger Greptile |
Restore reflex/experimental/memo.py as a thin module redirect to reflex_base.components.memo so existing rx.experimental.memo imports keep working with a deprecation warning.
Rename ExperimentalMemo* classes, EXPERIMENTAL_MEMOS registry, and related helpers/tests to plain Memo*/MEMOS now that memo is no longer experimental. Move integration and unit tests out of experimental/ to mirror the new module location.
…efaults Reusable Var-typed empty-value constants so memo signatures can spell strict defaults without per-call-site `Var.create(...)` (which trips B008) or bespoke module-level singletons. Updates the memo doc and the in-tree memos that previously rolled their own empty Var.
26b85b1 to
d5650cf
Compare
Drop redundant tuple() wraps around dict.values() splats in compile_app, and hoist `import inspect` to module scope in the memo unit tests. Refresh stale "experimental memo" wording in the memo test docstrings now that the rename has landed.
rx._x.memo into rx.memo
rx._x.memo into rx.memorx._x.memo into rx.memo
|
hitting a problem with recursive components from collections.abc import Sequence
from typing import TypedDict
import reflex as rx
class TreeNode(TypedDict):
name: str
children: Sequence["TreeNode"]
class State(rx.State):
"""The app state."""
tree: TreeNode = TreeNode(
name="root",
children=[
TreeNode(name="child1", children=[]),
TreeNode(
name="child2", children=[TreeNode(name="grandchild1", children=[])]
),
],
)
@rx._x.memo
def node_x(data: rx.vars.ObjectVar[TreeNode]) -> rx.Component:
return rx.vstack(
rx.text(data.name),
rx.foreach(data.children, lambda child: node_x(data=child)),
class_name="pl-4 border-l",
)
@rx.memo
def node(data: rx.vars.ObjectVar[TreeNode]) -> rx.Component:
return rx.vstack(
rx.text(data.name),
rx.foreach(data.children, lambda child: node(data=child)),
class_name="pl-4 border-l",
)
def index() -> rx.Component:
return rx.container(
rx.color_mode.button(position="top-right"),
rx.vstack(
rx.heading("Welcome to Reflex!", size="9"),
node(data=State.tree),
spacing="5",
justify="center",
min_height="85vh",
),
)
app = rx.App()
app.add_page(index)if you comment out the |
Bind the memo wrapper to the function's name (in both module globals and any matching free-variable cell) while the body is eagerly evaluated, so a memo can recursively call itself — e.g. a tree node component that renders its children through `rx.foreach`. Also refresh remaining `@rx._x.memo` references in error messages and docstrings now that the public name is `@rx.memo`.
# Conflicts: # packages/reflex-components-markdown/src/reflex_components_markdown/markdown.py # pyi_hashes.json # tests/units/components/markdown/test_markdown.py
…oring Missing return annotations default to `rx.Component`, and unannotated parameters default to `rx.Var[Any]` (or `rx.Var[Component]` for `children`), emitting a deprecation warning slated for removal in 1.0. Lets downstream users keep compiling while they migrate to explicit annotations.
Infer the body's return type during memo eval and surface the public `rx`/`rxe` qualname (e.g. `rxe.dnd.Draggable`) in the deprecation message so users get a copy-pasteable annotation instead of the generic hint. Update docs to use the inferred annotations.
|
|
||
| index_path = utils.get_components_path() | ||
| index_code = templates.memo_index_template(index_entries) | ||
| index_code = templates.memo_index_template([]) |
There was a problem hiding this comment.
seems this could just be removed?
Per-memo wrappers import from `$/utils/components/<name>` directly, so the empty top-level index module had no callers. Remove the emission and the now-unused `memo_index_template` and `get_components_path` helpers.
The bundled `$/utils/components` specifier emitted a root.jsx import from the now-removed index module, breaking the production build with `Could not load utils/components (Is a directory)`. The bare path no longer resolves — each memo lives at `$/utils/components/<name>` — so remove it from the default window bundle.

All Submissions:
Summary
Promotes the memo system out of
rx._xinto a first-classrx.memoAPI,with
rx._x.memokept as a thin deprecated alias so downstream appskeep working.
Move the memo implementation from reflex/experimental/memo.py into
packages/reflex-base/src/reflex_base/components/memo.py
and re-export it as
rx.memo; the old module becomes a deprecationshim that re-exports the same symbols.
Drop the
Experimentalprefix on the registry and public types sorx.memoreads cleanly without leaking the old namespace.Support
rx.EventHandlerparameters in component memos and annotatereturn types so
rx.memo-decorated callables type-check at call sites.Expose
EMPTY_VAR_STR/EMPTY_VAR_INTas memo-friendly sentineldefaults to replace ad-hoc empty-var construction in callers.
Migrate the existing Selenium memo integration test to Playwright
under tests/integration/tests_playwright/test_memo.py;
the legacy
test_experimental_memo.pyis retired and the remainingtest_memo.pyexercises the deprecation shim path.Update docs and internal component packages to use
rx.memodirectly.Have you followed the guidelines stated in CONTRIBUTING.md file?
Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
Please delete options that are not relevant.
New feature (non-breaking change which adds functionality)
This change requires a documentation update
New Feature Submission:
Changes To Core Features:
fixes ENG-9486