Skip to content

Commit 772b008

Browse files
authored
fix(suspense): handle edge case in patching list nodes within Suspense (#13306)
close #13305
1 parent cf5a5e0 commit 772b008

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,114 @@ describe('renderer: optimized mode', () => {
861861
expect(inner(root)).toBe('<div><div>true</div></div>')
862862
})
863863

864+
// #13305
865+
test('patch Suspense nested in list nodes in optimized mode', async () => {
866+
const deps: Promise<any>[] = []
867+
868+
const Item = {
869+
props: {
870+
someId: { type: Number, required: true },
871+
},
872+
async setup(props: any) {
873+
const p = new Promise(resolve => setTimeout(resolve, 1))
874+
deps.push(p)
875+
876+
await p
877+
return () => (
878+
openBlock(),
879+
createElementBlock('li', null, [
880+
createElementVNode(
881+
'p',
882+
null,
883+
String(props.someId),
884+
PatchFlags.TEXT,
885+
),
886+
])
887+
)
888+
},
889+
}
890+
891+
const list = ref([1, 2, 3])
892+
const App = {
893+
setup() {
894+
return () => (
895+
openBlock(),
896+
createElementBlock(
897+
Fragment,
898+
null,
899+
[
900+
createElementVNode(
901+
'p',
902+
null,
903+
JSON.stringify(list.value),
904+
PatchFlags.TEXT,
905+
),
906+
createElementVNode('ol', null, [
907+
(openBlock(),
908+
createBlock(SuspenseImpl, null, {
909+
fallback: withCtx(() => [
910+
createElementVNode('li', null, 'Loading…'),
911+
]),
912+
default: withCtx(() => [
913+
(openBlock(true),
914+
createElementBlock(
915+
Fragment,
916+
null,
917+
renderList(list.value, id => {
918+
return (
919+
openBlock(),
920+
createBlock(
921+
Item,
922+
{
923+
key: id,
924+
'some-id': id,
925+
},
926+
null,
927+
PatchFlags.PROPS,
928+
['some-id'],
929+
)
930+
)
931+
}),
932+
PatchFlags.KEYED_FRAGMENT,
933+
)),
934+
]),
935+
_: 1 /* STABLE */,
936+
})),
937+
]),
938+
],
939+
PatchFlags.STABLE_FRAGMENT,
940+
)
941+
)
942+
},
943+
}
944+
945+
const app = createApp(App)
946+
app.mount(root)
947+
expect(inner(root)).toBe(`<p>[1,2,3]</p>` + `<ol><li>Loading…</li></ol>`)
948+
949+
await Promise.all(deps)
950+
await nextTick()
951+
expect(inner(root)).toBe(
952+
`<p>[1,2,3]</p>` +
953+
`<ol>` +
954+
`<li><p>1</p></li>` +
955+
`<li><p>2</p></li>` +
956+
`<li><p>3</p></li>` +
957+
`</ol>`,
958+
)
959+
960+
list.value = [3, 1, 2]
961+
await nextTick()
962+
expect(inner(root)).toBe(
963+
`<p>[3,1,2]</p>` +
964+
`<ol>` +
965+
`<li><p>3</p></li>` +
966+
`<li><p>1</p></li>` +
967+
`<li><p>2</p></li>` +
968+
`</ol>`,
969+
)
970+
})
971+
864972
// #4183
865973
test('should not take unmount children fast path /w Suspense', async () => {
866974
const show = ref(true)

packages/runtime-core/src/renderer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,8 @@ function baseCreateRenderer(
961961
// which also requires the correct parent container
962962
!isSameVNodeType(oldVNode, newVNode) ||
963963
// - In the case of a component, it could contain anything.
964-
oldVNode.shapeFlag & (ShapeFlags.COMPONENT | ShapeFlags.TELEPORT))
964+
oldVNode.shapeFlag &
965+
(ShapeFlags.COMPONENT | ShapeFlags.TELEPORT | ShapeFlags.SUSPENSE))
965966
? hostParentNode(oldVNode.el)!
966967
: // In other cases, the parent container is not actually used so we
967968
// just pass the block element here to avoid a DOM parentNode call.

0 commit comments

Comments
 (0)