Skip to content

Commit 54ecdb3

Browse files
committed
chore: remove vsiblings
1 parent 9a27bb7 commit 54ecdb3

File tree

6 files changed

+63
-94
lines changed

6 files changed

+63
-94
lines changed

packages/qwik/src/core/client/vnode-diff.ts

Lines changed: 13 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,6 @@ export const vnode_diff = (
133133
/// and is not connected to the tree.
134134
let vNewNode: VNode | null = null;
135135

136-
/// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
137-
/// In such a case this array will contain the elements after the current location.
138-
/// The array even indices will contains keys and odd indices the vNode.
139-
let vSiblings: Array<string | null | VNode> | null = null; // See: `SiblingsArray`
140-
let vSiblingsIdx = -1;
141-
142136
/// Current set of JSX children.
143137
let jsxChildren: JSXChildren[] = null!;
144138
// Current JSX child.
@@ -278,16 +272,9 @@ export const vnode_diff = (
278272
* order and can't rely on `vnode_getNextSibling` and instead we need to go by `vSiblings`.
279273
*/
280274
function peekNextSibling() {
281-
if (vSiblings !== null) {
282-
// We came across a key, and we moved nodes around. This means we can no longer use
283-
// `vnode_getNextSibling` to look at next node and instead we have to go by `vSiblings`.
284-
const idx = vSiblingsIdx + SiblingsArray.NextVNode;
285-
return idx < vSiblings.length ? (vSiblings[idx] as any) : null;
286-
} else {
287-
// If we don't have a `vNewNode`, than that means we just reconciled the current node.
288-
// So advance it.
289-
return vCurrent ? vnode_getNextSibling(vCurrent) : null;
290-
}
275+
// If we don't have a `vNewNode`, than that means we just reconciled the current node.
276+
// So advance it.
277+
return vCurrent ? vnode_getNextSibling(vCurrent) : null;
291278
}
292279

293280
/**
@@ -299,9 +286,6 @@ export const vnode_diff = (
299286
*/
300287
function advanceToNextSibling() {
301288
vCurrent = peekNextSibling();
302-
if (vSiblings !== null) {
303-
vSiblingsIdx += SiblingsArray.Size; // advance;
304-
}
305289
}
306290

307291
/**
@@ -331,8 +315,6 @@ export const vnode_diff = (
331315
stackPush(children, descendVNode);
332316
if (descendVNode) {
333317
assertDefined(vCurrent || vNewNode, 'Expecting vCurrent to be defined.');
334-
vSiblings = null;
335-
vSiblingsIdx = -1;
336318
vParent = vNewNode || vCurrent!;
337319
vCurrent = vnode_getFirstChild(vParent);
338320
vNewNode = null;
@@ -343,8 +325,6 @@ export const vnode_diff = (
343325
function ascend() {
344326
const descendVNode = stack.pop(); // boolean: descendVNode
345327
if (descendVNode) {
346-
vSiblingsIdx = stack.pop();
347-
vSiblings = stack.pop();
348328
vNewNode = stack.pop();
349329
vCurrent = stack.pop();
350330
vParent = stack.pop();
@@ -359,7 +339,7 @@ export const vnode_diff = (
359339
function stackPush(children: JSXChildren, descendVNode: boolean) {
360340
stack.push(jsxChildren, jsxIdx, jsxCount, jsxValue);
361341
if (descendVNode) {
362-
stack.push(vParent, vCurrent, vNewNode, vSiblings, vSiblingsIdx);
342+
stack.push(vParent, vCurrent, vNewNode);
363343
}
364344
stack.push(descendVNode);
365345
if (Array.isArray(children)) {
@@ -384,9 +364,6 @@ export const vnode_diff = (
384364
function getInsertBefore() {
385365
if (vNewNode) {
386366
return vCurrent;
387-
} else if (vSiblings !== null) {
388-
const nextIdx = vSiblingsIdx + SiblingsArray.NextVNode;
389-
return nextIdx < vSiblings.length ? (vSiblings[nextIdx] as VNode) : null;
390367
} else {
391368
return peekNextSibling();
392369
}
@@ -751,10 +728,6 @@ export const vnode_diff = (
751728
vCurrent = vNewNode;
752729
// We need to clean up the vNewNode, because we don't want to skip advance to next sibling (see `advance` function).
753730
vNewNode = null;
754-
// We need also to go back to the previous sibling, because we assigned previous sibling to the vCurrent.
755-
if (vSiblings !== null) {
756-
vSiblingsIdx -= SiblingsArray.Size;
757-
}
758731
}
759732
}
760733
// reconcile attributes
@@ -953,63 +926,21 @@ export const vnode_diff = (
953926
}
954927
}
955928

956-
/**
957-
* Retrieve the child with the given key.
958-
*
959-
* By retrieving the child with the given key we are effectively removing it from the list of
960-
* future elements. This means that we can't just use `vnode_getNextSibling` to find the next
961-
* instead we have to keep track of the elements we have already seen.
962-
*
963-
* We call this materializing the elements.
964-
*
965-
* `vSiblingsIdx`:
966-
*
967-
* - -1: Not materialized
968-
* - Positive number - the index of the next element in the `vSiblings` array.
969-
*
970-
* By retrieving the child with the given key we are effectively removing it from the list (hence
971-
* we need to splice the `vSiblings` array).
972-
*
973-
* @param nodeName
974-
* @param key
975-
* @returns Array where: (see: `SiblingsArray`)
976-
*
977-
* - Idx%3 == 0 nodeName
978-
* - Idx%3 == 1 key
979-
* - Idx%3 == 2 vNode
980-
*/
929+
/** Retrieve the child with the given key. */
981930
function retrieveChildWithKey(
982931
nodeName: string | null,
983932
key: string | null
984933
): ElementVNode | VirtualVNode | null {
985934
let vNodeWithKey: ElementVNode | VirtualVNode | null = null;
986-
if (vSiblingsIdx === -1) {
987-
// it is not materialized; so materialize it.
988-
vSiblings = [];
989-
vSiblingsIdx = 0;
990-
let vNode = vCurrent;
991-
while (vNode) {
992-
const name = vnode_isElementVNode(vNode) ? vnode_getElementName(vNode) : null;
993-
const vKey = getKey(vNode) || getComponentHash(vNode, container.$getObjectById$);
994-
if (vNodeWithKey === null && vKey == key && name == nodeName) {
995-
vNodeWithKey = vNode as ElementVNode | VirtualVNode;
996-
} else {
997-
// we only add the elements which we did not find yet.
998-
vSiblings.push(name, vKey, vNode);
999-
}
1000-
vNode = vnode_getNextSibling(vNode);
1001-
}
1002-
} else {
1003-
for (let idx = vSiblingsIdx; idx < vSiblings!.length; idx += SiblingsArray.Size) {
1004-
const name = vSiblings![idx + SiblingsArray.Name];
1005-
const vKey = vSiblings![idx + SiblingsArray.Key];
1006-
if (vKey === key && name === nodeName) {
1007-
vNodeWithKey = vSiblings![idx + SiblingsArray.VNode] as any;
1008-
// remove the node from the siblings array
1009-
vSiblings?.splice(idx, SiblingsArray.Size);
1010-
break;
1011-
}
935+
let vNode = vCurrent;
936+
while (vNode) {
937+
const name = vnode_isElementVNode(vNode) ? vnode_getElementName(vNode) : null;
938+
const vKey = getKey(vNode) || getComponentHash(vNode, container.$getObjectById$);
939+
if (vKey == key && name == nodeName) {
940+
vNodeWithKey = vNode as ElementVNode | VirtualVNode;
941+
break;
1012942
}
943+
vNode = vnode_getNextSibling(vNode);
1013944
}
1014945
return vNodeWithKey;
1015946
}
@@ -1446,10 +1377,3 @@ function markVNodeAsDeleted(vCursor: VNode) {
14461377
*/
14471378
const HANDLER_PREFIX = ':';
14481379
let count = 0;
1449-
const enum SiblingsArray {
1450-
Name = 0,
1451-
Key = 1,
1452-
VNode = 2,
1453-
Size = 3,
1454-
NextVNode = Size + VNode,
1455-
}

packages/qwik/src/core/tests/component.spec.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,52 @@ describe.each([
823823
);
824824
});
825825

826+
it('should preserve the same elements', async () => {
827+
const Cmp = component$(() => {
828+
const keys = useSignal(['A', 'B', 'C']);
829+
830+
return (
831+
<div onClick$={() => (keys.value = ['B', 'C', 'A'])}>
832+
{keys.value.map((key) => (
833+
<span key={key} id={key}>
834+
{key}
835+
</span>
836+
))}
837+
</div>
838+
);
839+
});
840+
841+
const { vNode, document } = await render(<Cmp />, { debug });
842+
expect(vNode).toMatchVDOM(
843+
<Component>
844+
<div>
845+
<span id="A">A</span>
846+
<span id="B">B</span>
847+
<span id="C">C</span>
848+
</div>
849+
</Component>
850+
);
851+
const a1 = document.getElementById('A');
852+
const b1 = document.getElementById('B');
853+
const c1 = document.getElementById('C');
854+
await trigger(document.body, 'div', 'click');
855+
expect(vNode).toMatchVDOM(
856+
<Component>
857+
<div>
858+
<span id="B">B</span>
859+
<span id="C">C</span>
860+
<span id="A">A</span>
861+
</div>
862+
</Component>
863+
);
864+
const a2 = document.getElementById('A');
865+
const b2 = document.getElementById('B');
866+
const c2 = document.getElementById('C');
867+
expect(a1).toBe(a2);
868+
expect(b1).toBe(b2);
869+
expect(c1).toBe(c2);
870+
});
871+
826872
it('should render correctly component only with text node and node sibling', async () => {
827873
const Child = component$(() => {
828874
return <>0</>;

starters/apps/e2e/src/components/attributes/attributes.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const Attributes = component$(() => {
1818
>
1919
Rerender
2020
</button>
21+
<span id="render-count">{render.value}</span>
2122
<AttributesChild v={render.value} key={render.value} />
2223
<ProgressParent />
2324
</>

starters/apps/e2e/src/components/context/context.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const ContextRoot = component$(() => {
2828
<button id="btn-rerender" onClick$={() => count.value++}>
2929
Client Rerender
3030
</button>
31+
<span id="render-count">{count.value}</span>
3132
<ContextApp key={count.value} />
3233
</div>
3334
);

starters/e2e/e2e.attributes.e2e.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,8 @@ test.describe("attributes", () => {
325325
test.describe("client rerender", () => {
326326
test.beforeEach(async ({ page }) => {
327327
const toggleRender = page.locator("#force-rerender");
328-
const v = Number(await toggleRender.getAttribute("data-v"));
329328
await toggleRender.click();
330-
await expect(page.locator("#renderCount")).toHaveText(`Render ${v}`);
331-
// Without this the tests still fail?
332-
await page.waitForTimeout(50);
329+
await expect(page.locator("#render-count")).toHaveText("1");
333330
});
334331
tests();
335332
});

starters/e2e/e2e.context.e2e.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ test.describe("context", () => {
189189
test.beforeEach(async ({ page }) => {
190190
const rerender = page.locator("#btn-rerender");
191191
await rerender.click();
192-
await page.waitForTimeout(100);
192+
await expect(page.locator("#render-count")).toHaveText("1");
193193
});
194194
tests(false);
195195
});

0 commit comments

Comments
 (0)