From ffb24ceeee0b87527b5744065ee50c5598ae5288 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 19 Jun 2024 07:01:46 -0700 Subject: [PATCH] Improve ClientState compatibility with rx.input (#3490) * Ignore type checking for generic aliases in _var_name_unwrapped * Allow ClientStateVar.set_value to be used with text inputs * client_state: If var_name is not provided, create a random name * client_state: partition the arg value to get `_e0` from `_e0.target.value` --- reflex/components/chakra/forms/input.py | 2 +- reflex/components/chakra/forms/textarea.py | 2 +- .../components/radix/themes/components/text_area.py | 2 +- .../components/radix/themes/components/text_field.py | 2 +- reflex/experimental/client_state.py | 11 ++++++++--- reflex/vars.py | 12 +++++------- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/reflex/components/chakra/forms/input.py b/reflex/components/chakra/forms/input.py index d7a77a78d..c162a776f 100644 --- a/reflex/components/chakra/forms/input.py +++ b/reflex/components/chakra/forms/input.py @@ -92,7 +92,7 @@ class Input(ChakraComponent): Returns: The component. """ - if props.get("value") is not None and props.get("on_change"): + if props.get("value") is not None and props.get("on_change") is not None: # create a debounced input if the user requests full control to avoid typing jank return DebounceInput.create(super().create(*children, **props)) return super().create(*children, **props) diff --git a/reflex/components/chakra/forms/textarea.py b/reflex/components/chakra/forms/textarea.py index e7a3ce758..a88158feb 100644 --- a/reflex/components/chakra/forms/textarea.py +++ b/reflex/components/chakra/forms/textarea.py @@ -73,7 +73,7 @@ class TextArea(ChakraComponent): Returns: The component. """ - if props.get("value") is not None and props.get("on_change"): + if props.get("value") is not None and props.get("on_change") is not None: # create a debounced input if the user requests full control to avoid typing jank return DebounceInput.create(super().create(*children, **props)) return super().create(*children, **props) diff --git a/reflex/components/radix/themes/components/text_area.py b/reflex/components/radix/themes/components/text_area.py index a0c409350..a1c672b60 100644 --- a/reflex/components/radix/themes/components/text_area.py +++ b/reflex/components/radix/themes/components/text_area.py @@ -107,7 +107,7 @@ class TextArea(RadixThemesComponent, elements.Textarea): Returns: The component. """ - if props.get("value") is not None and props.get("on_change"): + if props.get("value") is not None and props.get("on_change") is not None: # create a debounced input if the user requests full control to avoid typing jank return DebounceInput.create(super().create(*children, **props)) return super().create(*children, **props) diff --git a/reflex/components/radix/themes/components/text_field.py b/reflex/components/radix/themes/components/text_field.py index 03a53e1de..3e9a3915b 100644 --- a/reflex/components/radix/themes/components/text_field.py +++ b/reflex/components/radix/themes/components/text_field.py @@ -100,7 +100,7 @@ class TextFieldRoot(elements.Div, RadixThemesComponent): The component. """ component = super().create(*children, **props) - if props.get("value") is not None and props.get("on_change"): + if props.get("value") is not None and props.get("on_change") is not None: # create a debounced input if the user requests full control to avoid typing jank return DebounceInput.create(component) return component diff --git a/reflex/experimental/client_state.py b/reflex/experimental/client_state.py index e4d6c258e..3033b94dd 100644 --- a/reflex/experimental/client_state.py +++ b/reflex/experimental/client_state.py @@ -9,7 +9,7 @@ from typing import Any, Callable, Optional, Type, Union from reflex import constants from reflex.event import EventChain, EventHandler, EventSpec, call_script from reflex.utils.imports import ImportVar -from reflex.vars import Var, VarData +from reflex.vars import Var, VarData, get_unique_variable_name NoValue = object() @@ -75,7 +75,10 @@ class ClientStateVar(Var): @classmethod def create( - cls, var_name: str, default: Any = NoValue, global_ref: bool = True + cls, + var_name: str | None = None, + default: Any = NoValue, + global_ref: bool = True, ) -> "ClientStateVar": """Create a local_state Var that can be accessed and updated on the client. @@ -102,6 +105,8 @@ class ClientStateVar(Var): Returns: ClientStateVar """ + if var_name is None: + var_name = get_unique_variable_name() assert isinstance(var_name, str), "var_name must be a string." if default is NoValue: default_var = Var.create_safe("", _var_is_local=False, _var_is_string=False) @@ -189,7 +194,7 @@ class ClientStateVar(Var): # This is a hack to make it work like an EventSpec taking an arg value = Var.create_safe(value, _var_is_string=isinstance(value, str)) if not value._var_is_string and value._var_full_name.startswith("_"): - arg = value._var_name_unwrapped + arg = value._var_name_unwrapped.partition(".")[0] else: arg = "" setter = f"({arg}) => {setter}({value._var_name_unwrapped})" diff --git a/reflex/vars.py b/reflex/vars.py index 5fd80f0a0..11fb76c96 100644 --- a/reflex/vars.py +++ b/reflex/vars.py @@ -1792,18 +1792,16 @@ class Var: """ from reflex.style import Style - type_ = ( - get_origin(self._var_type) - if types.is_generic_alias(self._var_type) - else self._var_type - ) + generic_alias = types.is_generic_alias(self._var_type) + + type_ = get_origin(self._var_type) if generic_alias else self._var_type wrapped_var = str(self) return ( wrapped_var if not self._var_state - and types._issubclass(type_, dict) - or types._issubclass(type_, Style) + and not generic_alias + and (types._issubclass(type_, dict) or types._issubclass(type_, Style)) else wrapped_var.strip("{}") )