Skip to content

Commit c3236fe

Browse files
committed
add 'key' to VDOM spec
all makes event handler targets deterministic based on keys and child indices
1 parent d04faf9 commit c3236fe

File tree

11 files changed

+207
-233
lines changed

11 files changed

+207
-233
lines changed

docs/source/core-concepts.rst

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,18 @@ have to re-render the layout and see what changed:
7777
from idom.core.layout import LayoutEvent
7878

7979

80-
event_handler_id = "on-click"
81-
82-
8380
@idom.component
8481
def ClickCount():
8582
count, set_count = idom.hooks.use_state(0)
86-
87-
@idom.event(target_id=event_handler_id) # <-- trick to hard code event handler ID
88-
def on_click(event):
89-
set_count(count + 1)
90-
9183
return idom.html.button(
92-
{"onClick": on_click},
84+
{"onClick": lambda event: set_count(count + 1)},
9385
[f"Click count: {count}"],
9486
)
9587

96-
97-
async with idom.Layout(ClickCount()) as layout:
88+
async with idom.Layout(ClickCount(key="something")) as layout:
9889
patch_1 = await layout.render()
9990

100-
fake_event = LayoutEvent(event_handler_id, [{}])
91+
fake_event = LayoutEvent("something.onClick", [{}])
10192
await layout.dispatch(fake_event)
10293
patch_2 = await layout.render()
10394

src/idom/core/component.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1+
from __future__ import annotations
2+
13
import abc
24
import inspect
35
from functools import wraps
4-
from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple, Union
5-
6+
from typing import Any, Callable, Dict, Tuple, Union
67

7-
if TYPE_CHECKING: # pragma: no cover
8-
from .vdom import VdomDict # noqa
8+
from .vdom import VdomDict
99

1010

1111
ComponentConstructor = Callable[..., "AbstractComponent"]
12-
ComponentRenderFunction = Callable[..., Union["AbstractComponent", "VdomDict"]]
12+
ComponentRenderFunction = Callable[..., Union["AbstractComponent", VdomDict]]
1313

1414

1515
def component(function: ComponentRenderFunction) -> Callable[..., "Component"]:
@@ -21,42 +21,47 @@ def component(function: ComponentRenderFunction) -> Callable[..., "Component"]:
2121
"""
2222

2323
@wraps(function)
24-
def constructor(*args: Any, **kwargs: Any) -> Component:
25-
return Component(function, args, kwargs)
24+
def constructor(*args: Any, key: str = "", **kwargs: Any) -> Component:
25+
return Component(function, key, args, kwargs)
2626

2727
return constructor
2828

2929

3030
class AbstractComponent(abc.ABC):
3131

32-
__slots__ = [] if hasattr(abc.ABC, "__weakref__") else ["__weakref__"]
32+
__slots__ = ["key"]
33+
if not hasattr(abc.ABC, "__weakref__"):
34+
__slots__.append("__weakref__") # pragma: no cover
35+
36+
key: str
3337

3438
@abc.abstractmethod
35-
def render(self) -> "VdomDict":
39+
def render(self) -> VdomDict:
3640
"""Render the component's :ref:`VDOM <VDOM Mimetype>` model."""
3741

3842

3943
class Component(AbstractComponent):
4044
"""An object for rending component models."""
4145

42-
__slots__ = (
43-
"_function",
44-
"_args",
45-
"_kwargs",
46-
)
46+
__slots__ = "_function", "_args", "_kwargs"
4747

4848
def __init__(
4949
self,
5050
function: ComponentRenderFunction,
51+
key: str,
5152
args: Tuple[Any, ...],
5253
kwargs: Dict[str, Any],
5354
) -> None:
55+
self.key = key
5456
self._function = function
5557
self._args = args
5658
self._kwargs = kwargs
5759

58-
def render(self) -> Any:
59-
return self._function(*self._args, **self._kwargs)
60+
def render(self) -> VdomDict:
61+
model = self._function(*self._args, **self._kwargs)
62+
if isinstance(model, AbstractComponent):
63+
model = {"tagName": "div", "children": [model]}
64+
return model
6065

6166
def __repr__(self) -> str:
6267
sig = inspect.signature(self._function)

src/idom/core/events.py

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,15 @@
1212
)
1313

1414
from anyio import create_task_group
15-
from mypy_extensions import TypedDict
1615

1716

1817
EventsMapping = Union[Dict[str, Union["Callable[..., Any]", "EventHandler"]], "Events"]
1918

2019

21-
class EventTarget(TypedDict):
22-
target: str
23-
preventDefault: bool # noqa
24-
stopPropagation: bool # noqa
25-
26-
2720
def event(
2821
function: Optional[Callable[..., Any]] = None,
2922
stop_propagation: bool = False,
3023
prevent_default: bool = False,
31-
target_id: Optional[str] = None,
3224
) -> Union["EventHandler", Callable[[Callable[..., Any]], "EventHandler"]]:
3325
"""Create an event handler function with extra functionality.
3426
@@ -49,12 +41,8 @@ def event(
4941
Block the event from propagating further up the DOM.
5042
prevent_default:
5143
Stops the default actional associate with the event from taking place.
52-
target_id:
53-
A unique ID used to locate this handler in the resulting VDOM. This is
54-
automatically generated by default and is typically not set manually
55-
except in testing.
5644
"""
57-
handler = EventHandler(stop_propagation, prevent_default, target_id=target_id)
45+
handler = EventHandler(stop_propagation, prevent_default)
5846
if function is not None:
5947
handler.add(function)
6048
return handler
@@ -142,8 +130,6 @@ def __repr__(self) -> str: # pragma: no cover
142130
class EventHandler:
143131
"""An object which defines an event handler.
144132
145-
Get a serialized reference to the handler via :meth:`Handler.serialize`.
146-
147133
The event handler object acts like a coroutine when called.
148134
149135
Parameters:
@@ -159,19 +145,16 @@ class EventHandler:
159145
"__weakref__",
160146
"_coro_handlers",
161147
"_func_handlers",
162-
"target_id",
163148
"prevent_default",
164-
"stop_propogation",
149+
"stop_propagation",
165150
)
166151

167152
def __init__(
168153
self,
169154
stop_propagation: bool = False,
170155
prevent_default: bool = False,
171-
target_id: Optional[str] = None,
172156
) -> None:
173-
self.target_id = target_id or str(id(self))
174-
self.stop_propogation = stop_propagation
157+
self.stop_propagation = stop_propagation
175158
self.prevent_default = prevent_default
176159
self._coro_handlers: List[Callable[..., Coroutine[Any, Any, Any]]] = []
177160
self._func_handlers: List[Callable[..., Any]] = []
@@ -216,6 +199,3 @@ def __contains__(self, function: Any) -> bool:
216199
return function in self._coro_handlers
217200
else:
218201
return function in self._func_handlers
219-
220-
def __repr__(self) -> str: # pragma: no cover
221-
return f"{type(self).__name__}({self.serialize()})"

0 commit comments

Comments
 (0)