Add displayName to React components for DevTools visibility - #6945
Open
masenf wants to merge 5 commits into
Open
Add displayName to React components for DevTools visibility#6945masenf wants to merge 5 commits into
masenf wants to merge 5 commits into
Conversation
React DevTools labels a component from its `displayName` (or the name of the function it wraps). The compiled frontend supplied neither: memo bodies are anonymous arrow functions passed to `memo()`, which erases the name JS would otherwise infer from the assignment, and none of the generated contexts were named. The result was a tree of `Anonymous` entries under an unlabelled stack of `Context.Provider`s. Each generated memo module now assigns a `displayName` to its export: `@rx.memo` components use the decorated function's name, and auto-memoized wrappers use the Python class they wrap rather than their content-hashed tag (`Foreach`, not `Foreach_comp_70fd2c58...`). The name is carried on `MemoComponentDefinition.display_name` and JSON-escaped at render time. Every context in the generated `context.js` is named too — `ColorModeContext`, `UploadFilesContext`, `DispatchContext`, `EventLoopContext`, `ThemeContext`, and one per state (`StateContext(<full state name>)`) — and the `ClientSide` HOC returns a named component tagged with the client-only component it loads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4Psp6qZsEpbuRePuRVkYX
Every page compiles to a component named `Component`, so the DevTools tree showed the same label whichever route was mounted. `page_template` now takes the route and emits `Component.displayName = "Component(<route>)"`, threaded through both the page-context and legacy compile paths. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4Psp6qZsEpbuRePuRVkYX
Merging this PR will not alter performance
Comparing Footnotes
|
Contributor
Greptile SummaryThe PR adds readable React DevTools names to generated pages, contexts, client-only wrappers, and memoized components.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| packages/reflex-base/src/reflex_base/compiler/templates.py | Generates escaped display names for contexts, pages, client-side wrappers, and memo component exports. |
| packages/reflex-base/src/reflex_base/components/component.py | Passes NoSSR component names into generated ClientSide wrappers without changing their loading behavior. |
| packages/reflex-base/src/reflex_base/components/memo.py | Adds optional memo display-name metadata and assigns readable class names to auto-memoized wrappers. |
| reflex/compiler/compiler.py | Propagates route and memo display-name metadata into frontend template rendering. |
| reflex/compiler/utils.py | Includes memo display names in the generated component definition data. |
| tests/units/compiler/test_compiler.py | Covers generated context, page, and client-side wrapper display names. |
| tests/units/components/test_memo.py | Covers memo display-name generation, custom wrappers, and JavaScript string escaping. |
| tests/units/compiler/test_memoize_plugin.py | Verifies readable names for automatically memoized component wrappers. |
Reviews (3): Last reviewed commit: "chore: add per-package changelog fragmen..." | Re-trigger Greptile
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 12 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4Psp6qZsEpbuRePuRVkYX
React Router's `decorateComponentExportsWithProps` rewrites an exported
function *declaration* into a function *expression* wrapped in
`UNSAFE_withComponentProps`, so `export default function Component() {}`
leaves no module-scope `Component` binding. The trailing
`Component.displayName = ...` then threw `ReferenceError: Component is not
defined` as soon as a route module loaded, breaking every page.
Declare the function, name it, and export the identifier instead. React Router
then wraps the binding rather than the declaration, the assignment resolves,
and the wrapper renders `Component` as a child so the route name still shows in
the DevTools tree.
Verified against a real `react-router build` (8.3.0): the old shape's route
chunk throws `ReferenceError: Component is not defined` on evaluation, the new
one evaluates and keeps `Component.displayName`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4Psp6qZsEpbuRePuRVkYX
The changelog check runs towncrier per affected package, so reflex-base and reflex-components-plotly each need their own fragment alongside the root one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4Psp6qZsEpbuRePuRVkYX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type of change
Description
This PR adds
displayNameproperties to all compiled React components and contexts, improving visibility in React DevTools. Without explicit display names, DevTools shows generic labels likeContext.Provider,Anonymous, orComponentfor every instance in the tree, making it difficult to debug component hierarchies.Changes:
Context template (
context_template): AddeddisplayNameassignments for:ColorModeContext,UploadFilesContext,DispatchContext,EventLoopContextStateContext(python.state.name)to identify which Python state they carryClientSidewrapper: Now a named function component with display name reflecting the wrapped componentPage template (
page_template): Every page compiles to a component namedComponent, so the route is now carried in itsdisplayName(e.g.,Component(about),Component(test/[dynamic])) to distinguish pages in DevToolsMemo components (
_render_memo_component): AddeddisplayNameassignment to exported memoized components, using either:@rx.memodecorated componentsMemoComponentDefinition: Added optional
display_namefield to carry the name React DevTools should showDynamic imports: Updated
ClientSide()calls to pass the component name as a second argument, enabling the wrapper to set an appropriate display nameTheme context (
react-theme.js): AddeddisplayNameto the theme contextTest coverage
test_compiler.pycovering context display names, page display names, and theClientSidewrapper namingtest_memo.pycovering memo display names, custom wrappers, and string escapingtest_memoize_plugin.pycovering auto-memo display namesChecklist
uv run ruff check .anduv run ruff format .cleanuv run pyright reflex testspassespyi_hashes.jsonupdatedhttps://claude.ai/code/session_01W4Psp6qZsEpbuRePuRVkYX