@@ -100,7 +100,7 @@ def __enter__(self: _Self) -> _Self:
100
100
def __exit__ (self , * exc : Any ) -> None :
101
101
root_csid = self ._root_life_cycle_state_id
102
102
root_model_state = self ._model_states_by_life_cycle_state_id [root_csid ]
103
- self ._unmount_model_states ([root_model_state ])
103
+ self ._deep_unmount_model_states ([root_model_state ])
104
104
105
105
# delete attributes here to avoid access after exiting context manager
106
106
del self ._event_handlers
@@ -166,7 +166,7 @@ def _create_layout_update(self, old_state: _ModelState) -> LayoutUpdate:
166
166
167
167
# hook effects must run after the update is complete
168
168
for model_state in _iter_model_state_children (new_state ):
169
- if hasattr ( model_state , "life_cycle_state" ) :
169
+ if model_state . is_component_state :
170
170
model_state .life_cycle_state .hook .component_did_render ()
171
171
172
172
old_model : Optional [VdomJson ]
@@ -271,10 +271,10 @@ def _render_model_attributes(
271
271
272
272
model_event_handlers = new_state .model .current ["eventHandlers" ] = {}
273
273
for event , handler in handlers_by_event .items ():
274
- target = old_state .targets_by_event . get (
275
- event ,
276
- uuid4 (). hex if handler . target is None else handler . target ,
277
- )
274
+ if event in old_state .targets_by_event :
275
+ target = old_state . targets_by_event [ event ]
276
+ else :
277
+ target = uuid4 (). hex if handler . target is None else handler . target
278
278
new_state .targets_by_event [event ] = target
279
279
self ._event_handlers [target ] = handler
280
280
model_event_handlers [event ] = {
@@ -320,7 +320,7 @@ def _render_model_children(
320
320
self ._render_model_children_without_old_state (new_state , raw_children )
321
321
return None
322
322
elif not raw_children :
323
- self ._unmount_model_states (list (old_state .children_by_key .values ()))
323
+ self ._deep_unmount_model_states (list (old_state .children_by_key .values ()))
324
324
return None
325
325
326
326
child_type_key_tuples = list (_process_child_type_and_key (raw_children ))
@@ -335,12 +335,13 @@ def _render_model_children(
335
335
336
336
old_keys = set (old_state .children_by_key ).difference (new_keys )
337
337
if old_keys :
338
- self ._unmount_model_states (
338
+ self ._deep_unmount_model_states (
339
339
[old_state .children_by_key [key ] for key in old_keys ]
340
340
)
341
341
342
342
new_children = new_state .model .current ["children" ] = []
343
343
for index , (child , child_type , key ) in enumerate (child_type_key_tuples ):
344
+ old_child_state = old_state .children_by_key .get (key )
344
345
if child_type is _DICT_TYPE :
345
346
old_child_state = old_state .children_by_key .get (key )
346
347
if old_child_state is None :
@@ -350,6 +351,8 @@ def _render_model_children(
350
351
key ,
351
352
)
352
353
else :
354
+ if old_child_state .is_component_state :
355
+ self ._shallow_unmount_model_state (old_child_state )
353
356
new_child_state = _update_element_model_state (
354
357
old_child_state ,
355
358
new_state ,
@@ -374,9 +377,13 @@ def _render_model_children(
374
377
new_state ,
375
378
index ,
376
379
child ,
380
+ self ._rendering_queue .put ,
377
381
)
378
382
self ._render_component (old_child_state , new_child_state , child )
379
383
else :
384
+ old_child_state = old_state .children_by_key .get (key )
385
+ if old_child_state is not None :
386
+ self ._deep_unmount_model_states ([old_child_state ])
380
387
new_children .append (child )
381
388
382
389
def _render_model_children_without_old_state (
@@ -399,20 +406,21 @@ def _render_model_children_without_old_state(
399
406
else :
400
407
new_children .append (child )
401
408
402
- def _unmount_model_states (self , old_states : List [_ModelState ]) -> None :
409
+ def _deep_unmount_model_states (self , old_states : List [_ModelState ]) -> None :
403
410
to_unmount = old_states [::- 1 ] # unmount in reversed order of rendering
404
411
while to_unmount :
405
412
model_state = to_unmount .pop ()
413
+ self ._shallow_unmount_model_state (model_state )
414
+ to_unmount .extend (model_state .children_by_key .values ())
406
415
407
- for target in model_state .targets_by_event .values ():
408
- del self ._event_handlers [target ]
409
-
410
- if hasattr (model_state , "life_cycle_state" ):
411
- life_cycle_state = model_state .life_cycle_state
412
- del self ._model_states_by_life_cycle_state_id [life_cycle_state .id ]
413
- life_cycle_state .hook .component_will_unmount ()
416
+ def _shallow_unmount_model_state (self , old_state : _ModelState ) -> None :
417
+ for target in old_state .targets_by_event .values ():
418
+ del self ._event_handlers [target ]
414
419
415
- to_unmount .extend (model_state .children_by_key .values ())
420
+ if old_state .is_component_state :
421
+ life_cycle_state = old_state .life_cycle_state
422
+ del self ._model_states_by_life_cycle_state_id [life_cycle_state .id ]
423
+ life_cycle_state .hook .component_will_unmount ()
416
424
417
425
def __repr__ (self ) -> str :
418
426
return f"{ type (self ).__name__ } ({ self .root } )"
@@ -459,7 +467,6 @@ def _make_component_model_state(
459
467
460
468
461
469
def _copy_component_model_state (old_model_state : _ModelState ) -> _ModelState :
462
-
463
470
# use try/except here because not having a parent is rare (only the root state)
464
471
try :
465
472
parent : Optional [_ModelState ] = old_model_state .parent
@@ -483,15 +490,8 @@ def _update_component_model_state(
483
490
new_parent : _ModelState ,
484
491
new_index : int ,
485
492
new_component : ComponentType ,
493
+ schedule_render : Callable [[_LifeCycleStateId ], None ],
486
494
) -> _ModelState :
487
- try :
488
- old_life_cycle_state = old_model_state .life_cycle_state
489
- except AttributeError :
490
- raise ValueError (
491
- f"Failed to render layout at { old_model_state .patch_path !r} with key "
492
- f"{ old_model_state .key !r} - prior element with this key wasn't a component"
493
- )
494
-
495
495
return _ModelState (
496
496
parent = new_parent ,
497
497
index = new_index ,
@@ -500,7 +500,11 @@ def _update_component_model_state(
500
500
patch_path = old_model_state .patch_path ,
501
501
children_by_key = {},
502
502
targets_by_event = {},
503
- life_cycle_state = _update_life_cycle_state (old_life_cycle_state , new_component ),
503
+ life_cycle_state = (
504
+ _update_life_cycle_state (old_model_state .life_cycle_state , new_component )
505
+ if old_model_state .is_component_state
506
+ else _make_life_cycle_state (new_component , schedule_render )
507
+ ),
504
508
)
505
509
506
510
@@ -525,12 +529,6 @@ def _update_element_model_state(
525
529
new_parent : _ModelState ,
526
530
new_index : int ,
527
531
) -> _ModelState :
528
- if hasattr (old_model_state , "life_cycle_state" ):
529
- raise ValueError (
530
- f"Failed to render layout at { old_model_state .patch_path !r} with key "
531
- f"{ old_model_state .key !r} - prior element with this key was a component"
532
- )
533
-
534
532
return _ModelState (
535
533
parent = new_parent ,
536
534
index = new_index ,
@@ -597,6 +595,10 @@ def __init__(
597
595
self .life_cycle_state = life_cycle_state
598
596
"""The state for the element's component (if it has one)"""
599
597
598
+ @property
599
+ def is_component_state (self ) -> bool :
600
+ return hasattr (self , "life_cycle_state" )
601
+
600
602
@property
601
603
def parent (self ) -> _ModelState :
602
604
parent = self ._parent_ref ()
0 commit comments