Skip to content

Commit

Permalink
bugfix: lib/dom - reconciler now correctly handles 'mapRemainingChild…
Browse files Browse the repository at this point in the history
…ren' phase when dealing with array children
  • Loading branch information
LankyMoose committed Jul 21, 2024
1 parent 5dea8f3 commit 601b116
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
17 changes: 17 additions & 0 deletions packages/lib/src/reconciler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,23 @@ function updateFromMap(
}
}

if (Array.isArray(newChild)) {
const oldChild = existingChildren.get(index)
if (oldChild) {
oldChild.effectTag = EffectTag.UPDATE
oldChild.props.children = newChild
return oldChild
} else {
const n = createElement(elementTypes.fragment, {
children: newChild,
})
n.parent = parent
n.effectTag = EffectTag.PLACEMENT
n.index = index
return n
}
}

return null
}

Expand Down
46 changes: 46 additions & 0 deletions packages/lib/src/tests/unit/reconciler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,52 @@ import { EffectTag } from "../../constants.js"
import { shuffle } from "./utils.js"

describe("reconciler", () => {
it("correctly handles correctly handles 'mapRemainingChildren' phase when dealing with array children", (t) => {
ctx.current = new kaioken.AppContext(() => null)
const mockRequestDeleteFn = t.mock.fn<(node: Kaioken.VNode) => void>(
() => {}
)
ctx.current.requestDelete = mockRequestDeleteFn
const items = "abcdefghijklmnopqrstuvwxyz".split("")
const node = kaioken.createElement("div")
node.child = reconcileChildren(node, null, [
false,
items.map((i) => kaioken.createElement("div", { key: i }, i)),
])!

const mockCommit = () => {
let stack: Kaioken.VNode[] = [node.child!]
while (stack.length) {
const n = stack.pop()!
if (n.child) stack.push(n.child)
if (n.sibling) stack.push(n.sibling)
n.prev = { ...n, props: { ...n.props }, prev: undefined }
}
}

const mockReconcile = () => {
node.child = reconcileChildren(node, node.child!, [
false,
items.map((i) => kaioken.createElement("div", { key: i }, i)),
])!
}

mockCommit()
shuffle(items)
mockReconcile()
mockCommit()
shuffle(items)
mockReconcile()
mockCommit()
shuffle(items)
mockReconcile()
// should not have any delete calls
assert.strictEqual(
mockRequestDeleteFn.mock.calls.length,
0,
`delete was called but should not have`
)
})
it("correctly handles reordered Array children with keys", (t) => {
ctx.current = new kaioken.AppContext(() => null)
const mockRequestDeleteFn = t.mock.fn<(node: Kaioken.VNode) => void>(
Expand Down

0 comments on commit 601b116

Please sign in to comment.