Skip to content

Commit fa31110

Browse files
committed
fix: diffing virtual fragment with null key
1 parent 13fec50 commit fa31110

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

Diff for: .changeset/heavy-kids-wave.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
fix: rendering markdown file with Qwik component

Diff for: packages/qwik/src/core/client/vnode-diff.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ export const vnode_diff = (
984984
}
985985

986986
function expectVirtual(type: VirtualType, jsxKey: string | null) {
987-
if (vCurrent && vnode_isVirtualVNode(vCurrent) && getKey(vCurrent) === jsxKey) {
987+
if (vCurrent && vnode_isVirtualVNode(vCurrent) && getKey(vCurrent) === jsxKey && !!jsxKey) {
988988
// All is good.
989989
return;
990990
} else if (jsxKey !== null) {

Diff for: packages/qwik/src/core/tests/component.spec.tsx

+53-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Fragment as Signal,
99
SkipRender,
1010
Slot,
11+
_jsxSorted,
1112
component$,
1213
h,
1314
jsx,
@@ -28,7 +29,8 @@ import { QError } from '../shared/error/error';
2829
import { ErrorProvider } from '../../testing/rendering.unit-util';
2930
import * as qError from '../shared/error/error';
3031
import { QContainerValue } from '../shared/types';
31-
import { QContainerAttr } from '../shared/utils/markers';
32+
import { OnRenderProp, QContainerAttr } from '../shared/utils/markers';
33+
import { vnode_getParent, vnode_getProp, vnode_locate } from '../client/vnode';
3234

3335
const debug = false; //true;
3436
Error.stackTraceLimit = 100;
@@ -2040,6 +2042,56 @@ describe.each([
20402042
);
20412043
});
20422044

2045+
it('should remove component with null key when it is compared with fragment with null key', async () => {
2046+
const InnerCmp = component$(() => {
2047+
return <div>InnerCmp</div>;
2048+
});
2049+
2050+
const Cmp = component$(() => {
2051+
const toggle = useSignal(true);
2052+
2053+
return (
2054+
<>
2055+
<button onClick$={() => (toggle.value = !toggle.value)}></button>
2056+
{toggle.value ? (
2057+
<InnerCmp key={null} />
2058+
) : (
2059+
<Fragment key={null}>
2060+
<h1>Test</h1>
2061+
</Fragment>
2062+
)}
2063+
</>
2064+
);
2065+
});
2066+
2067+
const { vNode, document, container } = await render(<Cmp />, { debug });
2068+
expect(vNode).toMatchVDOM(
2069+
<Component ssr-required>
2070+
<Fragment ssr-required>
2071+
<button></button>
2072+
<Component ssr-required>
2073+
<div>InnerCmp</div>
2074+
</Component>
2075+
</Fragment>
2076+
</Component>
2077+
);
2078+
await trigger(document.body, 'button', 'click');
2079+
2080+
expect(vNode).toMatchVDOM(
2081+
<Component ssr-required>
2082+
<Fragment ssr-required>
2083+
<button></button>
2084+
<Fragment ssr-required>
2085+
<h1>Test</h1>
2086+
</Fragment>
2087+
</Fragment>
2088+
</Component>
2089+
);
2090+
const h1Element = vnode_locate(container.rootVNode, document.querySelector('h1')!);
2091+
2092+
expect(vnode_getProp(vnode_getParent(h1Element)!, OnRenderProp, null)).toBeNull();
2093+
});
2094+
20432095
describe('regression', () => {
20442096
it('#3643', async () => {
20452097
const Issue3643 = component$(() => {

0 commit comments

Comments
 (0)