[reflex-web tweaks] Do not memoize children of InputGroup (#2230)

This commit is contained in:
Masen Furer 2023-11-30 09:48:28 -08:00 committed by GitHub
parent a86bcb3c72
commit a2d5bbc133
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 17 deletions

View File

@ -71,6 +71,8 @@ def FormSubmitName():
class FormState(rx.State): class FormState(rx.State):
form_data: dict = {} form_data: dict = {}
val: str = "foo"
options: list[str] = ["option1", "option2"]
def form_submit(self, form_data: dict): def form_submit(self, form_data: dict):
self.form_data = form_data self.form_data = form_data
@ -96,15 +98,24 @@ def FormSubmitName():
rx.switch(name="bool_input4"), rx.switch(name="bool_input4"),
rx.slider(name="slider_input"), rx.slider(name="slider_input"),
rx.range_slider(name="range_input"), rx.range_slider(name="range_input"),
rx.radio_group(["option1", "option2"], name="radio_input"), rx.radio_group(FormState.options, name="radio_input"),
rx.select(["option1", "option2"], name="select_input"), rx.select(FormState.options, name="select_input"),
rx.text_area(name="text_area_input"), rx.text_area(name="text_area_input"),
rx.input_group(
rx.input_left_element(rx.icon(tag="chevron_right")),
rx.input( rx.input(
name="debounce_input", name="debounce_input",
debounce_timeout=0, debounce_timeout=0,
on_change=rx.console_log, on_change=rx.console_log,
), ),
rx.input_right_element(rx.icon(tag="chevron_left")),
),
rx.button_group(
rx.button("Submit", type_="submit"), rx.button("Submit", type_="submit"),
rx.icon_button(FormState.val, icon=rx.icon(tag="add")),
variant="outline",
is_attached=True,
),
), ),
on_submit=FormState.form_submit, on_submit=FormState.form_submit,
custom_attrs={"action": "/invalid"}, custom_attrs={"action": "/invalid"},

View File

@ -23,7 +23,14 @@ from typing import (
from reflex.base import Base from reflex.base import Base
from reflex.compiler.templates import STATEFUL_COMPONENT from reflex.compiler.templates import STATEFUL_COMPONENT
from reflex.components.tags import Tag 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 ( from reflex.event import (
EventChain, EventChain,
EventHandler, EventHandler,
@ -150,6 +157,9 @@ class Component(BaseComponent, ABC):
# custom attribute # custom attribute
custom_attrs: Dict[str, Union[Var, str]] = {} custom_attrs: Dict[str, Union[Var, str]] = {}
# When to memoize this component and its children.
_memoization_mode: MemoizationMode = MemoizationMode()
@classmethod @classmethod
def __init_subclass__(cls, **kwargs): def __init_subclass__(cls, **kwargs):
"""Set default properties. """Set default properties.
@ -1643,14 +1653,13 @@ class StatefulComponent(BaseComponent):
Returns: Returns:
The memoized component tree. The memoized component tree.
""" """
from reflex.components.layout.foreach import Foreach if isinstance(component, Component):
if component._memoization_mode.recursive:
# Foreach must be memoized as a single component to retain index Var context. # Recursively memoize stateful children (default).
if not isinstance(component, Foreach):
component.children = [ component.children = [
cls.compile_from(child) for child in component.children cls.compile_from(child) for child in component.children
] ]
if isinstance(component, Component): # Memoize this component if it depends on state.
stateful_component = cls.create(component) stateful_component = cls.create(component)
if stateful_component is not None: if stateful_component is not None:
return stateful_component return stateful_component

View File

@ -9,7 +9,7 @@ from reflex.components.libs.chakra import (
LiteralButtonSize, LiteralButtonSize,
LiteralInputVariant, LiteralInputVariant,
) )
from reflex.constants import EventTriggers from reflex.constants import EventTriggers, MemoizationMode
from reflex.utils import imports from reflex.utils import imports
from reflex.vars import Var from reflex.vars import Var
@ -107,6 +107,8 @@ class InputGroup(ChakraComponent):
tag = "InputGroup" tag = "InputGroup"
_memoization_mode = MemoizationMode(recursive=False)
class InputLeftAddon(ChakraComponent): class InputLeftAddon(ChakraComponent):
"""The InputLeftAddon component is a component that is used to add an addon to the left of an input.""" """The InputLeftAddon component is a component that is used to add an addon to the left of an input."""

View File

@ -15,7 +15,7 @@ from reflex.components.libs.chakra import (
LiteralButtonSize, LiteralButtonSize,
LiteralInputVariant, LiteralInputVariant,
) )
from reflex.constants import EventTriggers from reflex.constants import EventTriggers, MemoizationMode
from reflex.utils import imports from reflex.utils import imports
from reflex.vars import Var from reflex.vars import Var

View File

@ -8,12 +8,15 @@ from typing import Any, Callable, Iterable
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.layout.fragment import Fragment from reflex.components.layout.fragment import Fragment
from reflex.components.tags import IterTag from reflex.components.tags import IterTag
from reflex.constants import MemoizationMode
from reflex.vars import Var from reflex.vars import Var
class Foreach(Component): class Foreach(Component):
"""A component that takes in an iterable and a render function and renders a list of components.""" """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. # The iterable to create components from.
iterable: Var[Iterable] iterable: Var[Iterable]

View File

@ -25,6 +25,8 @@ from .compiler import (
Ext, Ext,
Hooks, Hooks,
Imports, Imports,
MemoizationDisposition,
MemoizationMode,
PageNames, PageNames,
) )
from .config import ( from .config import (
@ -75,6 +77,8 @@ __ALL__ = [
IS_WINDOWS, IS_WINDOWS,
LOCAL_STORAGE, LOCAL_STORAGE,
LogLevel, LogLevel,
MemoizationDisposition,
MemoizationMode,
Next, Next,
Node, Node,
NOCOMPILE_FILE, NOCOMPILE_FILE,

View File

@ -1,7 +1,9 @@
"""Compiler variables.""" """Compiler variables."""
import enum
from enum import Enum from enum import Enum
from types import SimpleNamespace from types import SimpleNamespace
from reflex.base import Base
from reflex.constants import Dirs from reflex.constants import Dirs
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
@ -104,3 +106,21 @@ class Hooks(SimpleNamespace):
"""Common sets of hook declarations.""" """Common sets of hook declarations."""
EVENTS = f"const [{CompileVars.ADD_EVENTS}, {CompileVars.CONNECT_ERROR}] = useContext(EventLoopContext);" 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

View File

@ -44,6 +44,7 @@ EXCLUDED_PROPS = [
"is_default", "is_default",
"special_props", "special_props",
"_invalid_children", "_invalid_children",
"_memoization_mode",
"_valid_children", "_valid_children",
] ]