Incorrect procedure during component root element tag change
See original GitHub issueSandbox with forgo-state Sandbox without forgo-state
I think this may be what triggered of forgo/forgo-state forgojs/forgo-state#2; this bug was introduced in the same commit mentioned there, and now knowing the more specific cause I can reproduce this bug even after forgo-state@1.1.0.
When a component renders and returns a different root tag than before, the old tag gets marked for unloading but never actually gets cleaned up. So every time the component switches tags, the parentElement
’s list of unloadable nodes grows longer an longer.
The next time the parent rerenders, the node gets unmounted, which prompts forgo-state to unbind it from the state. Except the component never gets remounted, so the new element never gets bound back to the state, and it just becomes unresponsive to state changes, while the parent’s unloadable nodes list grows without bound.
Because the new tag got rendered, the UI looks like it’s correct until you try interacting with the updated element. And if you’re not using forgo-state, you might not even notice, since the component holding the new tag can correctly ask itself to rerender. The deletedNodes
/unmount
part happens even without forgo-state, but things seem like they’re working fine as long as the component doesn’t have an unmount
method.
I’m investigating how to fix this, with these goals:
- Prevent the unloadable nodes list from growing unboundedly
- Stop unmounting the component just because its tag changed
Issue Analytics
- State:
- Created a year ago
- Comments:8 (8 by maintainers)
Top GitHub Comments
PR for your review - #48
The bogus unmount happens in the sandbox when the parent rerenders before the child. Forgo is getting confused because
renderExistingElement()
is unloading nodes ontargetElement
, when it needs to be unloading ontargetElement.parentElement
. I.e., when the parent rerenders, forgo compares the parent’s component against the childrens’ components list, and inevitably decides to discard the children states because none of them match the parent. But if the child rerenders first,deletedNodes
is empty, and that code path isn’t triggered.I tried changing which element gets unloaded, which fixes the unmount, but then the parent’s
deletedNodes
starts growing again. I fixed that with an extraunloadMarkedNodes()
call inrenderComponentAndRemoveStaleNodes()
, which fixes the issues in the sandbox, but now a handful of tests about keyed components fail.I need to check those failing tests and see whether I need a different approach to tackle this bug.