I've noticed some unexpected behavior with keyed children, particularly <input>.
Removing a sibling before the currently focused input causes it to lose focus. Removing a sibling after the input is okay.
For example, removing the <span> or one of the <input> in this mock dynamic form:
<div>
{ state.visible ? <span key={"theSpan"}>I might disappear</span> : null }
{ state.inputs.map(inputName => (
<input
key={name}
oninput={ (event) => action.mightRemoveAnInputOrTheSpan(event.target.value)}
/>
)
}
</div>
An element.insertBefore call is being made for every keyed child beyond the removed one, which is likely when focus is lost. In this case, it is also an unecessary perf hit, since a single removeElement operation could have been performed instead.
if (oldKey === newKey) {
patch(element, keyedNode[0], keyedNode[1], children[k], isSvg)
i++
} else if (keyedNode[0]) {
patch(
element,
element.insertBefore(keyedNode[0], oldElements[i]), // <--- THIS
keyedNode[1],
children[k],
isSvg
)
@jorgebucaran Is this an issue that will be resolved with the diffing re-write you've talked about? It seems like this could be solved by determining which keyed items are being added & removed before doing any DOM operations?
Reproduction (with instructions): https://codepen.io/SkaterDad/pen/jzLbBX
Similar demo in React which seems fine: https://codesandbox.io/s/z68rm906jx
Hmm... should probably change this issue title, since the behavior is more general.
Any rearrangement of a keyed list triggers the unneeded element.insertBefore, which probably explains the benchmark performance on "swap rows" and "remove row".
@SkaterDad Is this an issue that will be resolved with the diffing re-write you've talked about? It seems like this could be solved by determining which keyed items are being added & removed before doing any DOM operations?
It will/should 馃, but if you find a temporary solution, I'd be happy to merge it in the meantime.
I am just surprised this issue is not #666.
Most helpful comment
I am just surprised this issue is not #666.