Skip to content

Commit 60ac361

Browse files
authored
Dedupe custom codes emitted by rx.memo components (#4793)
* Dedupe custom codes emitted by rx.memo components * Update variable names * Add test case for multiple memo components with custom code
1 parent 003d598 commit 60ac361

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

reflex/.templates/jinja/web/pages/custom_component.js.jinja2

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
{% extends "web/pages/base_page.js.jinja2" %}
22
{% from "web/pages/macros.js.jinja2" import renderHooks %}
3-
{% block export %}
4-
{% for component in components %}
53

6-
{% for custom_code in component.custom_code %}
4+
{% block declaration %}
5+
{% for custom_code in custom_codes %}
76
{{custom_code}}
87
{% endfor %}
8+
{% endblock %}
9+
10+
{% block export %}
11+
{% for component in components %}
912

1013
export const {{component.name}} = memo(({ {{-component.props|join(", ")-}} }) => {
1114
{{ renderHooks(component.hooks) }}

reflex/compiler/compiler.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,19 @@ def _compile_components(
247247
for comp_import in comp_render["dynamic_imports"]
248248
}
249249

250+
custom_codes = {
251+
comp_custom_code: None
252+
for comp_render in component_renders
253+
for comp_custom_code in comp_render.get("custom_code", [])
254+
}
255+
250256
# Compile the components page.
251257
return (
252258
templates.COMPONENTS.render(
253259
imports=utils.compile_imports(imports),
254260
components=component_renders,
255261
dynamic_imports=dynamic_imports,
262+
custom_codes=custom_codes,
256263
),
257264
imports,
258265
)

tests/integration/test_memo.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""Integration tests for rx.memo components."""
2+
3+
from typing import Generator
4+
5+
import pytest
6+
from selenium.webdriver.common.by import By
7+
8+
from reflex.testing import AppHarness
9+
10+
11+
def MemoApp():
12+
"""Reflex app with memo components."""
13+
import reflex as rx
14+
15+
class FooComponent(rx.Fragment):
16+
def add_custom_code(self) -> list[str]:
17+
return [
18+
"const foo = 'bar'",
19+
]
20+
21+
@rx.memo
22+
def foo_component(t: str):
23+
return FooComponent.create(t, rx.Var("foo"))
24+
25+
@rx.memo
26+
def foo_component2(t: str):
27+
return FooComponent.create(t, rx.Var("foo"))
28+
29+
def index() -> rx.Component:
30+
return rx.vstack(
31+
foo_component(t="foo"), foo_component2(t="bar"), id="memo-custom-code"
32+
)
33+
34+
app = rx.App()
35+
app.add_page(index)
36+
37+
38+
@pytest.fixture()
39+
def memo_app(tmp_path) -> Generator[AppHarness, None, None]:
40+
"""Start MemoApp app at tmp_path via AppHarness.
41+
42+
Args:
43+
tmp_path: pytest tmp_path fixture
44+
45+
Yields:
46+
running AppHarness instance
47+
"""
48+
with AppHarness.create(
49+
root=tmp_path,
50+
app_source=MemoApp,
51+
) as harness:
52+
yield harness
53+
54+
55+
@pytest.mark.asyncio
56+
async def test_memo_app(memo_app: AppHarness):
57+
"""Render various memo'd components and assert on the output.
58+
59+
Args:
60+
memo_app: harness for MemoApp app
61+
"""
62+
assert memo_app.app_instance is not None, "app is not running"
63+
driver = memo_app.frontend()
64+
65+
# check that the output matches
66+
memo_custom_code_stack = driver.find_element(By.ID, "memo-custom-code")
67+
assert memo_custom_code_stack.text == "foobarbarbar"

0 commit comments

Comments
 (0)