From 49ccd2f1fba207b72da071eba901394e1591a45f Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Fri, 1 Dec 2023 11:41:11 -0800 Subject: [PATCH] Generalize MemoizationLeaf Component mixin (#2247) --- reflex/components/base/head.py | 4 +- reflex/components/base/head.pyi | 11 +-- reflex/components/component.py | 32 +++++++ .../components/graphing/recharts/charts.pyi | 6 +- .../components/graphing/recharts/general.py | 4 +- .../components/graphing/recharts/general.pyi | 10 +-- .../components/graphing/recharts/recharts.py | 33 +------ .../components/graphing/recharts/recharts.pyi | 87 ++----------------- reflex/components/layout/cond.py | 4 +- 9 files changed, 57 insertions(+), 134 deletions(-) diff --git a/reflex/components/base/head.py b/reflex/components/base/head.py index 0c1a1db07..c8a63e7b4 100644 --- a/reflex/components/base/head.py +++ b/reflex/components/base/head.py @@ -1,6 +1,6 @@ """The head component.""" -from reflex.components.component import Component +from reflex.components.component import Component, MemoizationLeaf class NextHeadLib(Component): @@ -9,7 +9,7 @@ class NextHeadLib(Component): library = "next/head" -class Head(NextHeadLib): +class Head(NextHeadLib, MemoizationLeaf): """Head Component.""" tag = "NextHead" diff --git a/reflex/components/base/head.pyi b/reflex/components/base/head.pyi index d5f40a4ea..dd975844e 100644 --- a/reflex/components/base/head.pyi +++ b/reflex/components/base/head.pyi @@ -7,7 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union, overload from reflex.vars import Var, BaseVar, ComputedVar from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style -from reflex.components.component import Component +from reflex.components.component import Component, MemoizationLeaf class NextHeadLib(Component): @overload @@ -88,7 +88,7 @@ class NextHeadLib(Component): """ ... -class Head(NextHeadLib): +class Head(NextHeadLib, MemoizationLeaf): @overload @classmethod def create( # type: ignore @@ -147,7 +147,7 @@ class Head(NextHeadLib): ] = None, **props ) -> "Head": - """Create the component. + """Create a new memoization leaf component. Args: *children: The children of the component. @@ -160,9 +160,6 @@ class Head(NextHeadLib): **props: The props of the component. Returns: - The component. - - Raises: - TypeError: If an invalid child is passed. + The memoization leaf """ ... diff --git a/reflex/components/component.py b/reflex/components/component.py index edeb47f75..cd3ef1a49 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -1674,3 +1674,35 @@ class StatefulComponent(BaseComponent): if stateful_component is not None: return stateful_component return component + + +class MemoizationLeaf(Component): + """A component that does not separately memoize its children. + + Any component which depends on finding the exact names of children + components within it, should be a memoization leaf so the compiler + does not replace the provided child tags with memoized tags. + + During creation, a memoization leaf will mark itself as wanting to be + memoized if any of its children return any hooks. + """ + + _memoization_mode = MemoizationMode(recursive=False) + + @classmethod + def create(cls, *children, **props) -> Component: + """Create a new memoization leaf component. + + Args: + *children: The children of the component. + **props: The props of the component. + + Returns: + The memoization leaf + """ + comp = super().create(*children, **props) + if comp.get_hooks(): + comp._memoization_mode = cls._memoization_mode.copy( + update={"disposition": MemoizationDisposition.ALWAYS} + ) + return comp diff --git a/reflex/components/graphing/recharts/charts.pyi b/reflex/components/graphing/recharts/charts.pyi index 8534a6e72..ddbbff231 100644 --- a/reflex/components/graphing/recharts/charts.pyi +++ b/reflex/components/graphing/recharts/charts.pyi @@ -770,10 +770,10 @@ class FunnelChart(RechartsCharts): ] = None, **props ) -> "FunnelChart": - """Create a Recharts chart container component (mixin). + """Create a new memoization leaf component. Args: - *children: The children components. + *children: The children of the component. data: The source data, in which each element is an object. sync_id: If any two categorical charts(rx.line_chart, rx.area_chart, rx.bar_chart, rx.composed_chart) have the same sync_id, these two charts can sync the position GraphingTooltip, and the start_index, end_index of Brush. sync_method: When sync_id is provided, allows customisation of how the charts will synchronize GraphingTooltips and brushes. Using 'index' (default setting), other charts will reuse current datum's index within the data array. In cases where data does not have the same length, this might yield unexpected results. In that case use 'value' which will try to match other charts values, or a fully custom function which will receive tick, data as argument and should return an index. 'index' | 'value' | function @@ -791,7 +791,7 @@ class FunnelChart(RechartsCharts): **props: The props of the component. Returns: - A Recharts component. + The memoization leaf """ ... diff --git a/reflex/components/graphing/recharts/general.py b/reflex/components/graphing/recharts/general.py index 9034dd1bd..ad23204d6 100644 --- a/reflex/components/graphing/recharts/general.py +++ b/reflex/components/graphing/recharts/general.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any, Dict, List, Union +from reflex.components.component import MemoizationLeaf from reflex.constants import EventTriggers from reflex.vars import Var @@ -13,11 +14,10 @@ from .recharts import ( LiteralPosition, LiteralVerticalAlign, Recharts, - RechartsMemoizationLeafMixin, ) -class ResponsiveContainer(Recharts, RechartsMemoizationLeafMixin): +class ResponsiveContainer(Recharts, MemoizationLeaf): """A base class for responsive containers in Recharts.""" tag = "ResponsiveContainer" diff --git a/reflex/components/graphing/recharts/general.pyi b/reflex/components/graphing/recharts/general.pyi index 107a3c67b..5643588b5 100644 --- a/reflex/components/graphing/recharts/general.pyi +++ b/reflex/components/graphing/recharts/general.pyi @@ -8,6 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style from typing import Any, Dict, List, Union +from reflex.components.component import MemoizationLeaf from reflex.constants import EventTriggers from reflex.vars import Var from .recharts import ( @@ -17,10 +18,9 @@ from .recharts import ( LiteralPosition, LiteralVerticalAlign, Recharts, - RechartsMemoizationLeafMixin, ) -class ResponsiveContainer(Recharts, RechartsMemoizationLeafMixin): +class ResponsiveContainer(Recharts, MemoizationLeaf): @overload @classmethod def create( # type: ignore @@ -85,10 +85,10 @@ class ResponsiveContainer(Recharts, RechartsMemoizationLeafMixin): ] = None, **props ) -> "ResponsiveContainer": - """Create a Recharts chart container component (mixin). + """Create a new memoization leaf component. Args: - *children: The children components. + *children: The children of the component. aspect: The aspect ratio of the container. The final aspect ratio of the SVG element will be (width / height) * aspect. Number width: The width of chart container. Can be a number or string height: The height of chart container. Number @@ -104,7 +104,7 @@ class ResponsiveContainer(Recharts, RechartsMemoizationLeafMixin): **props: The props of the component. Returns: - A Recharts component. + The memoization leaf """ ... diff --git a/reflex/components/graphing/recharts/recharts.py b/reflex/components/graphing/recharts/recharts.py index c6cc4de67..28c8a5c8a 100644 --- a/reflex/components/graphing/recharts/recharts.py +++ b/reflex/components/graphing/recharts/recharts.py @@ -1,8 +1,7 @@ """A component that wraps a recharts lib.""" from typing import Literal -from reflex.components.component import Component, NoSSRComponent -from reflex.constants import MemoizationDisposition, MemoizationMode +from reflex.components.component import Component, MemoizationLeaf, NoSSRComponent class Recharts(Component): @@ -11,35 +10,7 @@ class Recharts(Component): library = "recharts@2.8.0" -class RechartsMemoizationLeafMixin(Component): - """A mixin for Recharts components that must not memoize their children separately. - - This includes all chart types and ResponsiveContainer itself. - """ - - _memoization_mode = MemoizationMode(recursive=False) - - @classmethod - def create(cls, *children, **props) -> Component: - """Create a Recharts chart container component (mixin). - - Args: - *children: The children components. - **props: The props of the component. - - Returns: - A Recharts component. - """ - comp = super().create(*children, **props) - if comp.get_hooks(): - # If any of the children depend on state, then this instance needs to memoize. - comp._memoization_mode = cls._memoization_mode.copy( - update={"disposition": MemoizationDisposition.ALWAYS}, - ) - return comp - - -class RechartsCharts(NoSSRComponent, RechartsMemoizationLeafMixin): +class RechartsCharts(NoSSRComponent, MemoizationLeaf): """A component that wraps a recharts lib.""" library = "recharts@2.8.0" diff --git a/reflex/components/graphing/recharts/recharts.pyi b/reflex/components/graphing/recharts/recharts.pyi index fae559991..eb9dd8ceb 100644 --- a/reflex/components/graphing/recharts/recharts.pyi +++ b/reflex/components/graphing/recharts/recharts.pyi @@ -8,8 +8,7 @@ from reflex.vars import Var, BaseVar, ComputedVar from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style from typing import Literal -from reflex.components.component import Component, NoSSRComponent -from reflex.constants import MemoizationDisposition, MemoizationMode +from reflex.components.component import Component, MemoizationLeaf, NoSSRComponent class Recharts(Component): @overload @@ -90,83 +89,7 @@ class Recharts(Component): """ ... -class RechartsMemoizationLeafMixin(Component): - @overload - @classmethod - def create( # type: ignore - cls, - *children, - style: Optional[Style] = None, - key: Optional[Any] = None, - id: Optional[Any] = None, - class_name: Optional[Any] = None, - autofocus: Optional[bool] = None, - custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, - on_blur: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_click: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_context_menu: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_double_click: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_focus: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mount: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_down: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_enter: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_leave: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_move: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_out: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_over: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_mouse_up: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_scroll: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - on_unmount: Optional[ - Union[EventHandler, EventSpec, list, function, BaseVar] - ] = None, - **props - ) -> "RechartsMemoizationLeafMixin": - """Create a Recharts chart container component (mixin). - - Args: - *children: The children components. - style: The style of the component. - key: A unique key for the component. - id: The id for the component. - class_name: The class name for the component. - autofocus: Whether the component should take the focus once the page is loaded - custom_attrs: custom attribute - **props: The props of the component. - - Returns: - A Recharts component. - """ - ... - -class RechartsCharts(NoSSRComponent, RechartsMemoizationLeafMixin): +class RechartsCharts(NoSSRComponent, MemoizationLeaf): @overload @classmethod def create( # type: ignore @@ -225,10 +148,10 @@ class RechartsCharts(NoSSRComponent, RechartsMemoizationLeafMixin): ] = None, **props ) -> "RechartsCharts": - """Create a Recharts chart container component (mixin). + """Create a new memoization leaf component. Args: - *children: The children components. + *children: The children of the component. style: The style of the component. key: A unique key for the component. id: The id for the component. @@ -238,7 +161,7 @@ class RechartsCharts(NoSSRComponent, RechartsMemoizationLeafMixin): **props: The props of the component. Returns: - A Recharts component. + The memoization leaf """ ... diff --git a/reflex/components/layout/cond.py b/reflex/components/layout/cond.py index 467a2e692..91ef389d5 100644 --- a/reflex/components/layout/cond.py +++ b/reflex/components/layout/cond.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any, Dict, Optional, overload -from reflex.components.component import BaseComponent, Component +from reflex.components.component import BaseComponent, Component, MemoizationLeaf from reflex.components.layout.fragment import Fragment from reflex.components.tags import CondTag, Tag from reflex.constants import Dirs @@ -15,7 +15,7 @@ _IS_TRUE_IMPORT = { } -class Cond(Component): +class Cond(MemoizationLeaf): """Render one of two components based on a condition.""" # The cond to determine which component to render.