|
@@ -23,6 +23,8 @@ from typing import (
|
|
|
Union,
|
|
|
)
|
|
|
|
|
|
+from typing_extensions import deprecated
|
|
|
+
|
|
|
import reflex.state
|
|
|
from reflex.base import Base
|
|
|
from reflex.compiler.templates import STATEFUL_COMPONENT
|
|
@@ -43,17 +45,13 @@ from reflex.constants.state import FRONTEND_EVENT_STATE
|
|
|
from reflex.event import (
|
|
|
EventCallback,
|
|
|
EventChain,
|
|
|
- EventChainVar,
|
|
|
EventHandler,
|
|
|
EventSpec,
|
|
|
EventVar,
|
|
|
- call_event_fn,
|
|
|
- call_event_handler,
|
|
|
- get_handler_args,
|
|
|
no_args_event_spec,
|
|
|
)
|
|
|
from reflex.style import Style, format_as_emotion
|
|
|
-from reflex.utils import format, imports, types
|
|
|
+from reflex.utils import console, format, imports, types
|
|
|
from reflex.utils.imports import (
|
|
|
ImmutableParsedImportDict,
|
|
|
ImportDict,
|
|
@@ -104,7 +102,7 @@ class BaseComponent(Base, ABC):
|
|
|
"""
|
|
|
|
|
|
@abstractmethod
|
|
|
- def _get_all_hooks_internal(self) -> dict[str, None]:
|
|
|
+ def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
|
|
|
"""Get the reflex internal hooks for the component and its children.
|
|
|
|
|
|
Returns:
|
|
@@ -112,7 +110,7 @@ class BaseComponent(Base, ABC):
|
|
|
"""
|
|
|
|
|
|
@abstractmethod
|
|
|
- def _get_all_hooks(self) -> dict[str, None]:
|
|
|
+ def _get_all_hooks(self) -> dict[str, VarData | None]:
|
|
|
"""Get the React hooks for this component.
|
|
|
|
|
|
Returns:
|
|
@@ -493,8 +491,7 @@ class Component(BaseComponent, ABC):
|
|
|
)
|
|
|
# Check if the key is an event trigger.
|
|
|
if key in component_specific_triggers:
|
|
|
- # Temporarily disable full control for event triggers.
|
|
|
- kwargs["event_triggers"][key] = self._create_event_chain(
|
|
|
+ kwargs["event_triggers"][key] = EventChain.create(
|
|
|
value=value, # type: ignore
|
|
|
args_spec=component_specific_triggers[key],
|
|
|
key=key,
|
|
@@ -548,6 +545,7 @@ class Component(BaseComponent, ABC):
|
|
|
# Construct the component.
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
+ @deprecated("Use rx.EventChain.create instead.")
|
|
|
def _create_event_chain(
|
|
|
self,
|
|
|
args_spec: types.ArgsSpec | Sequence[types.ArgsSpec],
|
|
@@ -569,82 +567,18 @@ class Component(BaseComponent, ABC):
|
|
|
|
|
|
Returns:
|
|
|
The event chain.
|
|
|
-
|
|
|
- Raises:
|
|
|
- ValueError: If the value is not a valid event chain.
|
|
|
- """
|
|
|
- # If it's an event chain var, return it.
|
|
|
- if isinstance(value, Var):
|
|
|
- if isinstance(value, EventChainVar):
|
|
|
- return value
|
|
|
- elif isinstance(value, EventVar):
|
|
|
- value = [value]
|
|
|
- elif issubclass(value._var_type, (EventChain, EventSpec)):
|
|
|
- return self._create_event_chain(args_spec, value.guess_type(), key=key)
|
|
|
- else:
|
|
|
- raise ValueError(
|
|
|
- f"Invalid event chain: {value!s} of type {value._var_type}"
|
|
|
- )
|
|
|
- elif isinstance(value, EventChain):
|
|
|
- # Trust that the caller knows what they're doing passing an EventChain directly
|
|
|
- return value
|
|
|
-
|
|
|
- # If the input is a single event handler, wrap it in a list.
|
|
|
- if isinstance(value, (EventHandler, EventSpec)):
|
|
|
- value = [value]
|
|
|
-
|
|
|
- # If the input is a list of event handlers, create an event chain.
|
|
|
- if isinstance(value, List):
|
|
|
- events: List[Union[EventSpec, EventVar]] = []
|
|
|
- for v in value:
|
|
|
- if isinstance(v, (EventHandler, EventSpec)):
|
|
|
- # Call the event handler to get the event.
|
|
|
- events.append(call_event_handler(v, args_spec, key=key))
|
|
|
- elif isinstance(v, Callable):
|
|
|
- # Call the lambda to get the event chain.
|
|
|
- result = call_event_fn(v, args_spec, key=key)
|
|
|
- if isinstance(result, Var):
|
|
|
- raise ValueError(
|
|
|
- f"Invalid event chain: {v}. Cannot use a Var-returning "
|
|
|
- "lambda inside an EventChain list."
|
|
|
- )
|
|
|
- events.extend(result)
|
|
|
- elif isinstance(v, EventVar):
|
|
|
- events.append(v)
|
|
|
- else:
|
|
|
- raise ValueError(f"Invalid event: {v}")
|
|
|
-
|
|
|
- # If the input is a callable, create an event chain.
|
|
|
- elif isinstance(value, Callable):
|
|
|
- result = call_event_fn(value, args_spec, key=key)
|
|
|
- if isinstance(result, Var):
|
|
|
- # Recursively call this function if the lambda returned an EventChain Var.
|
|
|
- return self._create_event_chain(args_spec, result, key=key)
|
|
|
- events = [*result]
|
|
|
-
|
|
|
- # Otherwise, raise an error.
|
|
|
- else:
|
|
|
- raise ValueError(f"Invalid event chain: {value}")
|
|
|
-
|
|
|
- # Add args to the event specs if necessary.
|
|
|
- events = [
|
|
|
- (e.with_args(get_handler_args(e)) if isinstance(e, EventSpec) else e)
|
|
|
- for e in events
|
|
|
- ]
|
|
|
-
|
|
|
- # Return the event chain.
|
|
|
- if isinstance(args_spec, Var):
|
|
|
- return EventChain(
|
|
|
- events=events,
|
|
|
- args_spec=None,
|
|
|
- event_actions={},
|
|
|
- )
|
|
|
- else:
|
|
|
- return EventChain(
|
|
|
- events=events,
|
|
|
- args_spec=args_spec,
|
|
|
- event_actions={},
|
|
|
- )
|
|
|
+ """
|
|
|
+ console.deprecate(
|
|
|
+ "Component._create_event_chain",
|
|
|
+ "Use rx.EventChain.create instead.",
|
|
|
+ deprecation_version="0.6.8",
|
|
|
+ removal_version="0.7.0",
|
|
|
+ )
|
|
|
+ return EventChain.create(
|
|
|
+ value=value, # type: ignore
|
|
|
+ args_spec=args_spec,
|
|
|
+ key=key,
|
|
|
+ )
|
|
|
|
|
|
def get_event_triggers(
|
|
|
self,
|
|
@@ -1086,18 +1020,22 @@ class Component(BaseComponent, ABC):
|
|
|
event_args.append(spec)
|
|
|
yield event_trigger, event_args
|
|
|
|
|
|
- def _get_vars(self, include_children: bool = False) -> list[Var]:
|
|
|
+ def _get_vars(
|
|
|
+ self, include_children: bool = False, ignore_ids: set[int] | None = None
|
|
|
+ ) -> Iterator[Var]:
|
|
|
"""Walk all Vars used in this component.
|
|
|
|
|
|
Args:
|
|
|
include_children: Whether to include Vars from children.
|
|
|
+ ignore_ids: The ids to ignore.
|
|
|
|
|
|
- Returns:
|
|
|
+ Yields:
|
|
|
Each var referenced by the component (props, styles, event handlers).
|
|
|
"""
|
|
|
- vars = getattr(self, "__vars", None)
|
|
|
+ ignore_ids = ignore_ids or set()
|
|
|
+ vars: List[Var] | None = getattr(self, "__vars", None)
|
|
|
if vars is not None:
|
|
|
- return vars
|
|
|
+ yield from vars
|
|
|
vars = self.__vars = []
|
|
|
# Get Vars associated with event trigger arguments.
|
|
|
for _, event_vars in self._get_vars_from_event_triggers(self.event_triggers):
|
|
@@ -1141,12 +1079,15 @@ class Component(BaseComponent, ABC):
|
|
|
# Get Vars associated with children.
|
|
|
if include_children:
|
|
|
for child in self.children:
|
|
|
- if not isinstance(child, Component):
|
|
|
+ if not isinstance(child, Component) or id(child) in ignore_ids:
|
|
|
continue
|
|
|
- child_vars = child._get_vars(include_children=include_children)
|
|
|
+ ignore_ids.add(id(child))
|
|
|
+ child_vars = child._get_vars(
|
|
|
+ include_children=include_children, ignore_ids=ignore_ids
|
|
|
+ )
|
|
|
vars.extend(child_vars)
|
|
|
|
|
|
- return vars
|
|
|
+ yield from vars
|
|
|
|
|
|
def _event_trigger_values_use_state(self) -> bool:
|
|
|
"""Check if the values of a component's event trigger use state.
|
|
@@ -1338,7 +1279,7 @@ class Component(BaseComponent, ABC):
|
|
|
"""
|
|
|
_imports = {}
|
|
|
|
|
|
- if self._get_ref_hook():
|
|
|
+ if self._get_ref_hook() is not None:
|
|
|
# Handle hooks needed for attaching react refs to DOM nodes.
|
|
|
_imports.setdefault("react", set()).add(ImportVar(tag="useRef"))
|
|
|
_imports.setdefault(f"$/{Dirs.STATE_PATH}", set()).add(
|
|
@@ -1454,7 +1395,7 @@ class Component(BaseComponent, ABC):
|
|
|
}}
|
|
|
}}, []);"""
|
|
|
|
|
|
- def _get_ref_hook(self) -> str | None:
|
|
|
+ def _get_ref_hook(self) -> Var | None:
|
|
|
"""Generate the ref hook for the component.
|
|
|
|
|
|
Returns:
|
|
@@ -1462,11 +1403,12 @@ class Component(BaseComponent, ABC):
|
|
|
"""
|
|
|
ref = self.get_ref()
|
|
|
if ref is not None:
|
|
|
- return (
|
|
|
- f"const {ref} = useRef(null); {Var(_js_expr=ref)._as_ref()!s} = {ref};"
|
|
|
+ return Var(
|
|
|
+ f"const {ref} = useRef(null); {Var(_js_expr=ref)._as_ref()!s} = {ref};",
|
|
|
+ _var_data=VarData(position=Hooks.HookPosition.INTERNAL),
|
|
|
)
|
|
|
|
|
|
- def _get_vars_hooks(self) -> dict[str, None]:
|
|
|
+ def _get_vars_hooks(self) -> dict[str, VarData | None]:
|
|
|
"""Get the hooks required by vars referenced in this component.
|
|
|
|
|
|
Returns:
|
|
@@ -1479,27 +1421,38 @@ class Component(BaseComponent, ABC):
|
|
|
vars_hooks.update(
|
|
|
var_data.hooks
|
|
|
if isinstance(var_data.hooks, dict)
|
|
|
- else {k: None for k in var_data.hooks}
|
|
|
+ else {
|
|
|
+ k: VarData(position=Hooks.HookPosition.INTERNAL)
|
|
|
+ for k in var_data.hooks
|
|
|
+ }
|
|
|
)
|
|
|
return vars_hooks
|
|
|
|
|
|
- def _get_events_hooks(self) -> dict[str, None]:
|
|
|
+ def _get_events_hooks(self) -> dict[str, VarData | None]:
|
|
|
"""Get the hooks required by events referenced in this component.
|
|
|
|
|
|
Returns:
|
|
|
The hooks for the events.
|
|
|
"""
|
|
|
- return {Hooks.EVENTS: None} if self.event_triggers else {}
|
|
|
+ return (
|
|
|
+ {Hooks.EVENTS: VarData(position=Hooks.HookPosition.INTERNAL)}
|
|
|
+ if self.event_triggers
|
|
|
+ else {}
|
|
|
+ )
|
|
|
|
|
|
- def _get_special_hooks(self) -> dict[str, None]:
|
|
|
+ def _get_special_hooks(self) -> dict[str, VarData | None]:
|
|
|
"""Get the hooks required by special actions referenced in this component.
|
|
|
|
|
|
Returns:
|
|
|
The hooks for special actions.
|
|
|
"""
|
|
|
- return {Hooks.AUTOFOCUS: None} if self.autofocus else {}
|
|
|
+ return (
|
|
|
+ {Hooks.AUTOFOCUS: VarData(position=Hooks.HookPosition.INTERNAL)}
|
|
|
+ if self.autofocus
|
|
|
+ else {}
|
|
|
+ )
|
|
|
|
|
|
- def _get_hooks_internal(self) -> dict[str, None]:
|
|
|
+ def _get_hooks_internal(self) -> dict[str, VarData | None]:
|
|
|
"""Get the React hooks for this component managed by the framework.
|
|
|
|
|
|
Downstream components should NOT override this method to avoid breaking
|
|
@@ -1510,7 +1463,7 @@ class Component(BaseComponent, ABC):
|
|
|
"""
|
|
|
return {
|
|
|
**{
|
|
|
- hook: None
|
|
|
+ str(hook): VarData(position=Hooks.HookPosition.INTERNAL)
|
|
|
for hook in [self._get_ref_hook(), self._get_mount_lifecycle_hook()]
|
|
|
if hook is not None
|
|
|
},
|
|
@@ -1559,7 +1512,7 @@ class Component(BaseComponent, ABC):
|
|
|
"""
|
|
|
return
|
|
|
|
|
|
- def _get_all_hooks_internal(self) -> dict[str, None]:
|
|
|
+ def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
|
|
|
"""Get the reflex internal hooks for the component and its children.
|
|
|
|
|
|
Returns:
|
|
@@ -1574,7 +1527,7 @@ class Component(BaseComponent, ABC):
|
|
|
|
|
|
return code
|
|
|
|
|
|
- def _get_all_hooks(self) -> dict[str, None]:
|
|
|
+ def _get_all_hooks(self) -> dict[str, VarData | None]:
|
|
|
"""Get the React hooks for this component and its children.
|
|
|
|
|
|
Returns:
|
|
@@ -1582,6 +1535,9 @@ class Component(BaseComponent, ABC):
|
|
|
"""
|
|
|
code = {}
|
|
|
|
|
|
+ # Add the internal hooks for this component.
|
|
|
+ code.update(self._get_hooks_internal())
|
|
|
+
|
|
|
# Add the hook code for this component.
|
|
|
hooks = self._get_hooks()
|
|
|
if hooks is not None:
|
|
@@ -1737,7 +1693,7 @@ class CustomComponent(Component):
|
|
|
|
|
|
# Handle event chains.
|
|
|
if types._issubclass(type_, EventChain):
|
|
|
- value = self._create_event_chain(
|
|
|
+ value = EventChain.create(
|
|
|
value=value,
|
|
|
args_spec=event_triggers_in_component_declaration.get(
|
|
|
key, no_args_event_spec
|
|
@@ -1862,19 +1818,25 @@ class CustomComponent(Component):
|
|
|
for name, prop in self.props.items()
|
|
|
]
|
|
|
|
|
|
- def _get_vars(self, include_children: bool = False) -> list[Var]:
|
|
|
+ def _get_vars(
|
|
|
+ self, include_children: bool = False, ignore_ids: set[int] | None = None
|
|
|
+ ) -> Iterator[Var]:
|
|
|
"""Walk all Vars used in this component.
|
|
|
|
|
|
Args:
|
|
|
include_children: Whether to include Vars from children.
|
|
|
+ ignore_ids: The ids to ignore.
|
|
|
|
|
|
- Returns:
|
|
|
+ Yields:
|
|
|
Each var referenced by the component (props, styles, event handlers).
|
|
|
"""
|
|
|
- return (
|
|
|
- super()._get_vars(include_children=include_children)
|
|
|
- + [prop for prop in self.props.values() if isinstance(prop, Var)]
|
|
|
- + self.get_component(self)._get_vars(include_children=include_children)
|
|
|
+ ignore_ids = ignore_ids or set()
|
|
|
+ yield from super()._get_vars(
|
|
|
+ include_children=include_children, ignore_ids=ignore_ids
|
|
|
+ )
|
|
|
+ yield from filter(lambda prop: isinstance(prop, Var), self.props.values())
|
|
|
+ yield from self.get_component(self)._get_vars(
|
|
|
+ include_children=include_children, ignore_ids=ignore_ids
|
|
|
)
|
|
|
|
|
|
@lru_cache(maxsize=None) # noqa
|
|
@@ -2277,7 +2239,7 @@ class StatefulComponent(BaseComponent):
|
|
|
)
|
|
|
return trigger_memo
|
|
|
|
|
|
- def _get_all_hooks_internal(self) -> dict[str, None]:
|
|
|
+ def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
|
|
|
"""Get the reflex internal hooks for the component and its children.
|
|
|
|
|
|
Returns:
|
|
@@ -2285,7 +2247,7 @@ class StatefulComponent(BaseComponent):
|
|
|
"""
|
|
|
return {}
|
|
|
|
|
|
- def _get_all_hooks(self) -> dict[str, None]:
|
|
|
+ def _get_all_hooks(self) -> dict[str, VarData | None]:
|
|
|
"""Get the React hooks for this component.
|
|
|
|
|
|
Returns:
|
|
@@ -2403,7 +2365,7 @@ class MemoizationLeaf(Component):
|
|
|
The memoization leaf
|
|
|
"""
|
|
|
comp = super().create(*children, **props)
|
|
|
- if comp._get_all_hooks() or comp._get_all_hooks_internal():
|
|
|
+ if comp._get_all_hooks():
|
|
|
comp._memoization_mode = cls._memoization_mode.copy(
|
|
|
update={"disposition": MemoizationDisposition.ALWAYS}
|
|
|
)
|