Skip to content

fix: avoid duplicating inner tag in MemoComponent wrapper tag - #7004

Open
LeonxLJX wants to merge 3 commits into
reflex-dev:mainfrom
LeonxLJX:fix/memo-wrapper-tag-duplication
Open

fix: avoid duplicating inner tag in MemoComponent wrapper tag#7004
LeonxLJX wants to merge 3 commits into
reflex-dev:mainfrom
LeonxLJX:fix/memo-wrapper-tag-duplication

Conversation

@LeonxLJX

@LeonxLJX LeonxLJX commented Aug 29, 2026

Copy link
Copy Markdown

Override compute_memo_tag on MemoComponent to skip the self.tag segment, which is already embedded in the dynamic subclass qualname (MemoComponent). Previously the wrapper tag contained the inner tag twice (e.g. Memocomponent_card_98ffd1e1_card_98ffd1e1_).

Adds a regression test asserting the inner tag appears exactly once.

Closes #6955

All Submissions:

  • 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.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

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?

After these steps, you're ready to open a pull request.

a. Give a descriptive title to your PR.

b. Describe your changes.

c. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such).

Review in cubic

Override _compute_memo_tag on MemoComponent to skip the self.tag segment,
which is already embedded in the dynamic subclass __qualname__
(MemoComponent_<tag>). Previously the wrapper tag contained the inner tag
twice (e.g. Memocomponent_card_98ffd1e1_card_98ffd1e1_<hash>).

Adds a regression test asserting the inner tag appears exactly once.

Closes reflex-dev#6955
@LeonxLJX
LeonxLJX requested a review from a team as a code owner August 29, 2026 03:57
@greptile-apps

greptile-apps Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR removes the duplicated inner tag from generated memo-wrapper names and adds regression coverage. It also adds root and reflex-base release notes, although the package fragment is currently associated with the wrong pull-request number.

  • Overrides memo-wrapper tag computation to rely on the tag already embedded in the dynamic class qualname.
  • Tests that the inner tag occurs exactly once in the generated wrapper name.
  • Adds release fragments for the root and reflex-base packages.

Confidence Score: 4/5

The code change appears sound, but the reflex-base news fragment must be renamed so the generated release note references the correct pull request before merging.

The package fragment is named 6955.bugfix.md even though this is PR 7004, causing the release record to associate the fix with the wrong pull request.

Files Needing Attention: packages/reflex-base/news/6955.bugfix.md

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/components/memo.py Overrides memo tag generation to remove the redundant self.tag segment while preserving the component hash and class-qualified prefix.
tests/units/compiler/test_memoize_plugin.py Adds focused regression coverage asserting that generated memo-wrapper tags contain the wrapped tag exactly once.
packages/reflex-base/news/6955.bugfix.md Adds the previously missing package release note, but its filename associates this PR's change with PR 6955 instead of PR 7004.
news/7004.bugfix.md Adds the root-package release note using the current pull-request number.

Reviews (3): Last reviewed commit: "chore: add reflex-base news fragment for..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/components/memo.py

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

No issues found across 2 files

Re-trigger cubic

Adds the required package news fragment so changelog validation
passes for the user-facing memo-wrapper tag fix.

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

No issues found across 3 files

Re-trigger cubic

@LeonxLJX

LeonxLJX commented Sep 1, 2026

Copy link
Copy Markdown
Author

Added the missing reflex-base news fragment (news/7004.bugfix.md) so the release workflow is satisfied.

Root cause. MemoComponent._compute_memo_tag rebuilt the tag by re-prepending the wrapped component's class-qualified prefix on top of the already-prefixed base, so the generated memo-wrapper name carried the inner tag twice. That made memoized component names unstable / collision-prone across recompiles.

Fix. Override _compute_memo_tag to preserve the class-qualified prefix + component hash exactly once, and added a compiler regression test that asserts the produced tag is no longer duplicated. Tag shape is unchanged for the common case, so existing apps keep the same memo identity.

If you'd prefer the fragment wording tuned (e.g. mention the failing case explicitly), happy to adjust.

@@ -0,0 +1 @@
Auto-memoized `@rx.memo` wrapper tags no longer duplicate the wrapped component's tag. `MemoComponent._compute_memo_tag` now skips the `self.tag` segment, which is already embedded in the dynamic subclass `__qualname__` (`MemoComponent_<tag>`), so generated wrapper names read as `Memocomponent_<tag>_<hash>` instead of `Memocomponent_<tag>_<tag>_<hash>`.

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.

P1 Release note links wrong PR

When the reflex-base changelog is materialized, the 6955.bugfix.md filename associates this change with PR 6955 instead of the current PR 7004, causing the generated release note to link to the wrong pull request.

Context Used: CLAUDE.md (source)

Knowledge Base Used: Release engineering

@LeonxLJX

LeonxLJX commented Sep 4, 2026

Copy link
Copy Markdown
Author

Bumping for visibility. Greptile flagged a P1 on the reflex-base component cache behavior — basically that under MemoComponent's wrapper-tag dedup, a stale memoized tag can survive across user-state changes if the inner component's tag depends on state that changed. Before I push a fix I want to confirm the desired behavior with maintainers: should the dedup invalidate when the inner component's state changes (current PR does this implicitly via the wrapper-tag recompute), or should we expose an explicit invalidate() API on MemoComponent? I lean toward the former to keep the API surface minimal — but want to check before iterating.

@codspeed-hq

codspeed-hq Bot commented Sep 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 32 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing LeonxLJX:fix/memo-wrapper-tag-duplication (77837d3) with main (3573364)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

names like ``Memocomponent_plain_abc123_plain_abc123_<hash>``. The inner
tag must appear exactly once in the wrapper tag.
"""
ctx, _page_ctx = _compile_single_page(lambda: Plain.create(STATE_VAR))

@FarhanAliRaza FarhanAliRaza Sep 4, 2026

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.

This test fails on this branch.

STATE_VAR is passed as a child, not a prop. Component.create wraps it in a Bare, so the tree is Plain(Bare(STATE_VAR)) and the state hooks ride on the Bare. The auto-memoize pass wraps the Bare. That wrapper is an ordinary Component, not a MemoComponent, so MemoComponent._compute_memo_tag never runs and the inner tag Plain never appears in the wrapper tag.

AssertionError: Inner tag 'Plain' should appear exactly once in wrapper tag,
found 0 times. wrapper_tag=Bare_comp_bba9dad596f8328a01eabe6aaa98e665
assert 0 == 1

The doubling this PR fixes only occurs when a @rx.memo component receives a state Var as a prop. In that case the export name on main is Memocomponent_statefulcard_14db30e6_statefulcard_14db30e6_dbfbf34c..., and with this fix it is Memocomponent_statefulcard_14db30e6_dbfbf34c....

# The inner component's tag is "Plain"; after format_state_name it becomes
# lowercase "plain" in the wrapper tag. Count occurrences of the inner tag
# segment (case-insensitive, word-bounded to avoid matching hash substrings).
inner_tag_lower = Plain.tag.lower()

@FarhanAliRaza FarhanAliRaza Sep 4, 2026

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.

Component.tag is str | None, so the type check gate fails here.

uv run pyright tests/units/compiler/test_memoize_plugin.py
test_memoize_plugin.py:2616:33 - error: "lower" is not a known attribute of "None" (reportOptionalMemberAccess)

children: The children of the component (ignored).
"""

def _compute_memo_tag(self) -> str:

@FarhanAliRaza FarhanAliRaza Sep 4, 2026

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.

This override repeats the whole body of Component._compute_memo_tag. Only one segment of the f-string differs. The imports, the strategy lookup, the hash call and the format_state_name(...).capitalize() now exist in two places, so the hashing policy can drift between them.

@FarhanAliRaza FarhanAliRaza left a comment

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.

See the inline comments.

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.

Auto-memo wrapper tags for @rx.memo components repeat the inner tag

2 participants