18
18
Set ,
19
19
Tuple ,
20
20
TypeVar ,
21
+ cast ,
21
22
)
22
23
from uuid import uuid4
23
24
from weakref import ref as weakref
31
32
32
33
from ._event_proxy import _wrap_in_warning_event_proxies
33
34
from .hooks import LifeCycleHook
34
- from .proto import ComponentType , EventHandlerDict , VdomJson
35
+ from .proto import ComponentType , EventHandlerDict , VdomDict , VdomJson
35
36
from .vdom import validate_vdom_json
36
37
37
38
@@ -199,7 +200,15 @@ def _render_component(
199
200
raw_model = component .render ()
200
201
finally :
201
202
life_cycle_hook .unset_current ()
202
- self ._render_model (old_state , new_state , raw_model )
203
+
204
+ # wrap the model in a fragment (i.e. tagName="") to ensure components have
205
+ # a separate node in the model state tree. This could be removed if this
206
+ # components are given a node in the tree some other way
207
+ wrapper_model : VdomDict = {"tagName" : "" }
208
+ if raw_model is not None :
209
+ wrapper_model ["children" ] = [raw_model ]
210
+
211
+ self ._render_model (old_state , new_state , wrapper_model )
203
212
except Exception as error :
204
213
logger .exception (f"Failed to render { component } " )
205
214
new_state .model .current = {
@@ -233,15 +242,6 @@ def _render_model(
233
242
new_state .key = new_state .model .current ["key" ] = raw_model ["key" ]
234
243
if "importSource" in raw_model :
235
244
new_state .model .current ["importSource" ] = raw_model ["importSource" ]
236
-
237
- if old_state is not None and old_state .key != new_state .key :
238
- self ._unmount_model_states ([old_state ])
239
- if new_state .is_component_state :
240
- self ._model_states_by_life_cycle_state_id [
241
- new_state .life_cycle_state .id
242
- ] = new_state
243
- old_state = None
244
-
245
245
self ._render_model_attributes (old_state , new_state , raw_model )
246
246
self ._render_model_children (old_state , new_state , raw_model .get ("children" , []))
247
247
@@ -371,6 +371,7 @@ def _render_model_children(
371
371
new_children .append (new_child_state .model .current )
372
372
new_state .children_by_key [key ] = new_child_state
373
373
elif child_type is _COMPONENT_TYPE :
374
+ child = cast (ComponentType , child )
374
375
old_child_state = old_state .children_by_key .get (key )
375
376
if old_child_state is None :
376
377
new_child_state = _make_component_model_state (
@@ -381,8 +382,7 @@ def _render_model_children(
381
382
self ._rendering_queue .put ,
382
383
)
383
384
elif old_child_state .is_component_state and (
384
- old_child_state .life_cycle_state .component .definition_id
385
- != child .definition_id
385
+ old_child_state .life_cycle_state .component .type != child .type
386
386
):
387
387
self ._unmount_model_states ([old_child_state ])
388
388
old_child_state = None
@@ -411,10 +411,18 @@ def _render_model_children(
411
411
def _render_model_children_without_old_state (
412
412
self , new_state : _ModelState , raw_children : List [Any ]
413
413
) -> None :
414
+ child_type_key_tuples = list (_process_child_type_and_key (raw_children ))
415
+
416
+ new_keys = {item [2 ] for item in child_type_key_tuples }
417
+ if len (new_keys ) != len (raw_children ):
418
+ key_counter = Counter (item [2 ] for item in child_type_key_tuples )
419
+ duplicate_keys = [key for key , count in key_counter .items () if count > 1 ]
420
+ raise ValueError (
421
+ f"Duplicate keys { duplicate_keys } at { new_state .patch_path or '/' !r} "
422
+ )
423
+
414
424
new_children = new_state .model .current ["children" ] = []
415
- for index , (child , child_type , key ) in enumerate (
416
- _process_child_type_and_key (raw_children )
417
- ):
425
+ for index , (child , child_type , key ) in enumerate (child_type_key_tuples ):
418
426
if child_type is _DICT_TYPE :
419
427
child_state = _make_element_model_state (new_state , index , key )
420
428
self ._render_model (None , child_state , child )
0 commit comments