Skip to content

Commit d682407

Browse files
authored
fix append to stale children ref (#807)
1 parent a1ebebd commit d682407

File tree

3 files changed

+54
-9
lines changed

3 files changed

+54
-9
lines changed

Diff for: docs/source/about/changelog.rst

+5-3
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ more info, see the :ref:`Contributor Guide <Creating a Changelog Entry>`.
2323
Unreleased
2424
----------
2525

26-
No changes.
26+
**Fixed**
2727

28+
- :issue:`806` - child models after a component fail to render
2829

29-
v0.40.0
30-
-------
30+
31+
v0.40.0 (yanked)
32+
----------------
3133
:octicon:`milestone` *released on 2022-08-13*
3234

3335
**Fixed**

Diff for: src/idom/core/layout.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ def _render_model_children(
361361
[old_state.children_by_key[key] for key in old_keys]
362362
)
363363

364-
new_children = new_state.model.current["children"] = []
364+
new_state.model.current["children"] = []
365365
for index, (child, child_type, key) in enumerate(child_type_key_tuples):
366366
old_child_state = old_state.children_by_key.get(key)
367367
if child_type is _DICT_TYPE:
@@ -388,7 +388,7 @@ def _render_model_children(
388388
index,
389389
)
390390
self._render_model(exit_stack, old_child_state, new_child_state, child)
391-
new_children.append(new_child_state.model.current)
391+
new_state.append_child(new_child_state.model.current)
392392
new_state.children_by_key[key] = new_child_state
393393
elif child_type is _COMPONENT_TYPE:
394394
child = cast(ComponentType, child)
@@ -428,7 +428,7 @@ def _render_model_children(
428428
old_child_state = old_state.children_by_key.get(key)
429429
if old_child_state is not None:
430430
self._unmount_model_states([old_child_state])
431-
new_children.append(child)
431+
new_state.append_child(child)
432432

433433
def _render_model_children_without_old_state(
434434
self,
@@ -446,20 +446,20 @@ def _render_model_children_without_old_state(
446446
f"Duplicate keys {duplicate_keys} at {new_state.patch_path or '/'!r}"
447447
)
448448

449-
new_children = new_state.model.current["children"] = []
449+
new_state.model.current["children"] = []
450450
for index, (child, child_type, key) in enumerate(child_type_key_tuples):
451451
if child_type is _DICT_TYPE:
452452
child_state = _make_element_model_state(new_state, index, key)
453453
self._render_model(exit_stack, None, child_state, child)
454-
new_children.append(child_state.model.current)
454+
new_state.append_child(child_state.model.current)
455455
new_state.children_by_key[key] = child_state
456456
elif child_type is _COMPONENT_TYPE:
457457
child_state = _make_component_model_state(
458458
new_state, index, key, child, self._rendering_queue.put
459459
)
460460
self._render_component(exit_stack, None, child_state, child)
461461
else:
462-
new_children.append(child)
462+
new_state.append_child(child)
463463

464464
def _unmount_model_states(self, old_states: List[_ModelState]) -> None:
465465
to_unmount = old_states[::-1] # unmount in reversed order of rendering
@@ -661,6 +661,9 @@ def parent(self) -> _ModelState:
661661
assert parent is not None, "detached model state"
662662
return parent
663663

664+
def append_child(self, child: Any) -> None:
665+
self.model.current["children"].append(child)
666+
664667
def __repr__(self) -> str: # pragma: no cover
665668
return f"ModelState({ {s: getattr(self, s, None) for s in self.__slots__} })"
666669

Diff for: tests/test_core/test_layout.py

+40
Original file line numberDiff line numberDiff line change
@@ -1207,3 +1207,43 @@ def bad_should_render(new):
12071207
await layout.render()
12081208
root_hook.latest.schedule_render()
12091209
await layout.render()
1210+
1211+
1212+
async def test_does_render_children_after_component():
1213+
"""Regression test for bug where layout was appending children to a stale ref
1214+
1215+
The stale reference was created when a component got rendered. Thus, everything
1216+
after the component failed to display.
1217+
"""
1218+
1219+
@idom.component
1220+
def Parent():
1221+
return html.div(
1222+
html.p("first"),
1223+
Child(),
1224+
html.p("third"),
1225+
)
1226+
1227+
@idom.component
1228+
def Child():
1229+
return html.p("second")
1230+
1231+
async with idom.Layout(Parent()) as layout:
1232+
update = await layout.render()
1233+
print(update.new)
1234+
assert update.new == {
1235+
"tagName": "",
1236+
"children": [
1237+
{
1238+
"tagName": "div",
1239+
"children": [
1240+
{"tagName": "p", "children": ["first"]},
1241+
{
1242+
"tagName": "",
1243+
"children": [{"tagName": "p", "children": ["second"]}],
1244+
},
1245+
{"tagName": "p", "children": ["third"]},
1246+
],
1247+
}
1248+
],
1249+
}

0 commit comments

Comments
 (0)