Skip to content

Commit

Permalink
[reflex-web tweaks] Do not memoize children of InputGroup (#2230)
Browse files Browse the repository at this point in the history
  • Loading branch information
masenf authored Nov 30, 2023
1 parent a86bcb3 commit a2d5bbc
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 17 deletions.
25 changes: 18 additions & 7 deletions integration/test_form_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def FormSubmitName():

class FormState(rx.State):
form_data: dict = {}
val: str = "foo"
options: list[str] = ["option1", "option2"]

def form_submit(self, form_data: dict):
self.form_data = form_data
Expand All @@ -96,15 +98,24 @@ def index():
rx.switch(name="bool_input4"),
rx.slider(name="slider_input"),
rx.range_slider(name="range_input"),
rx.radio_group(["option1", "option2"], name="radio_input"),
rx.select(["option1", "option2"], name="select_input"),
rx.radio_group(FormState.options, name="radio_input"),
rx.select(FormState.options, name="select_input"),
rx.text_area(name="text_area_input"),
rx.input(
name="debounce_input",
debounce_timeout=0,
on_change=rx.console_log,
rx.input_group(
rx.input_left_element(rx.icon(tag="chevron_right")),
rx.input(
name="debounce_input",
debounce_timeout=0,
on_change=rx.console_log,
),
rx.input_right_element(rx.icon(tag="chevron_left")),
),
rx.button_group(
rx.button("Submit", type_="submit"),
rx.icon_button(FormState.val, icon=rx.icon(tag="add")),
variant="outline",
is_attached=True,
),
rx.button("Submit", type_="submit"),
),
on_submit=FormState.form_submit,
custom_attrs={"action": "/invalid"},
Expand Down
25 changes: 17 additions & 8 deletions reflex/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@
from reflex.base import Base
from reflex.compiler.templates import STATEFUL_COMPONENT
from reflex.components.tags import Tag
from reflex.constants import Dirs, EventTriggers, Hooks, Imports, PageNames
from reflex.constants import (
Dirs,
EventTriggers,
Hooks,
Imports,
MemoizationMode,
PageNames,
)
from reflex.event import (
EventChain,
EventHandler,
Expand Down Expand Up @@ -150,6 +157,9 @@ class Component(BaseComponent, ABC):
# custom attribute
custom_attrs: Dict[str, Union[Var, str]] = {}

# When to memoize this component and its children.
_memoization_mode: MemoizationMode = MemoizationMode()

@classmethod
def __init_subclass__(cls, **kwargs):
"""Set default properties.
Expand Down Expand Up @@ -1643,14 +1653,13 @@ def compile_from(cls, component: BaseComponent) -> BaseComponent:
Returns:
The memoized component tree.
"""
from reflex.components.layout.foreach import Foreach

# Foreach must be memoized as a single component to retain index Var context.
if not isinstance(component, Foreach):
component.children = [
cls.compile_from(child) for child in component.children
]
if isinstance(component, Component):
if component._memoization_mode.recursive:
# Recursively memoize stateful children (default).
component.children = [
cls.compile_from(child) for child in component.children
]
# Memoize this component if it depends on state.
stateful_component = cls.create(component)
if stateful_component is not None:
return stateful_component
Expand Down
4 changes: 3 additions & 1 deletion reflex/components/forms/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
LiteralButtonSize,
LiteralInputVariant,
)
from reflex.constants import EventTriggers
from reflex.constants import EventTriggers, MemoizationMode
from reflex.utils import imports
from reflex.vars import Var

Expand Down Expand Up @@ -107,6 +107,8 @@ class InputGroup(ChakraComponent):

tag = "InputGroup"

_memoization_mode = MemoizationMode(recursive=False)


class InputLeftAddon(ChakraComponent):
"""The InputLeftAddon component is a component that is used to add an addon to the left of an input."""
Expand Down
2 changes: 1 addition & 1 deletion reflex/components/forms/input.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ from reflex.components.libs.chakra import (
LiteralButtonSize,
LiteralInputVariant,
)
from reflex.constants import EventTriggers
from reflex.constants import EventTriggers, MemoizationMode
from reflex.utils import imports
from reflex.vars import Var

Expand Down
3 changes: 3 additions & 0 deletions reflex/components/layout/foreach.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
from reflex.components.component import Component
from reflex.components.layout.fragment import Fragment
from reflex.components.tags import IterTag
from reflex.constants import MemoizationMode
from reflex.vars import Var


class Foreach(Component):
"""A component that takes in an iterable and a render function and renders a list of components."""

_memoization_mode = MemoizationMode(recursive=False)

# The iterable to create components from.
iterable: Var[Iterable]

Expand Down
4 changes: 4 additions & 0 deletions reflex/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
Ext,
Hooks,
Imports,
MemoizationDisposition,
MemoizationMode,
PageNames,
)
from .config import (
Expand Down Expand Up @@ -75,6 +77,8 @@
IS_WINDOWS,
LOCAL_STORAGE,
LogLevel,
MemoizationDisposition,
MemoizationMode,
Next,
Node,
NOCOMPILE_FILE,
Expand Down
20 changes: 20 additions & 0 deletions reflex/constants/compiler.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Compiler variables."""
import enum
from enum import Enum
from types import SimpleNamespace

from reflex.base import Base
from reflex.constants import Dirs
from reflex.utils.imports import ImportVar

Expand Down Expand Up @@ -104,3 +106,21 @@ class Hooks(SimpleNamespace):
"""Common sets of hook declarations."""

EVENTS = f"const [{CompileVars.ADD_EVENTS}, {CompileVars.CONNECT_ERROR}] = useContext(EventLoopContext);"


class MemoizationDisposition(enum.Enum):
"""The conditions under which a component should be memoized."""

# If the component uses state or events, it should be memoized.
STATEFUL = "stateful"
# TODO: add more modes, like always and never


class MemoizationMode(Base):
"""The mode for memoizing a Component."""

# The conditions under which the component should be memoized.
disposition: MemoizationDisposition = MemoizationDisposition.STATEFUL

# Whether children of this component should be memoized first.
recursive: bool = True
1 change: 1 addition & 0 deletions scripts/pyi_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"is_default",
"special_props",
"_invalid_children",
"_memoization_mode",
"_valid_children",
]

Expand Down

0 comments on commit a2d5bbc

Please sign in to comment.