@@ -723,32 +723,12 @@ export const vnode_diff = (
723
723
let needsQDispatchEventPatch = false ;
724
724
const currentKey = getKey ( vCurrent ) ;
725
725
if ( ! isSameElementName || jsxKey !== currentKey ) {
726
- // scan until you find the key you want.
727
- vNewNode = retrieveChildWithKey ( elementName , jsxKey ) ;
728
- // If found remove everything before and place in side buffer.
729
- if ( vNewNode ) {
730
- vCurrent = vNewNode ;
731
- vNewNode = null ;
732
- } else {
733
- // If not found, check the side buffer
734
- vNewNode = vSideBuffer ?. get ( elementName + ':' + jsxKey ) || null ;
735
- if ( vNewNode ) {
736
- vSideBuffer ! . delete ( elementName + ':' + jsxKey ) ;
737
- // if found insert from side-buffer
738
- // Existing keyed node
739
- vnode_insertBefore ( journal , vParent as ElementVNode , vNewNode , vCurrent ) ;
740
- // We are here, so jsx is different from the vCurrent, so now we want to point to the moved node.
741
- vCurrent = vNewNode ;
742
- // We need to clean up the vNewNode, because we don't want to skip advance to next sibling (see `advance` function).
743
- vNewNode = null ;
744
- } else {
745
- // if not found it is a new item create it.
746
- needsQDispatchEventPatch = createNewElement ( jsx , elementName ) ;
747
- }
748
- }
749
- } else if ( vSideBuffer ?. has ( elementName + ':' + jsxKey ) ) {
726
+ const sideBufferKey = getSideBufferKey ( elementName , jsxKey ) ;
727
+ const createNew = ( ) => ( needsQDispatchEventPatch = createNewElement ( jsx , elementName ) ) ;
728
+ moveOrCreateKeyedNode ( elementName , jsxKey , sideBufferKey , vParent as ElementVNode , createNew ) ;
729
+ } else {
750
730
// delete the key from the side buffer if it is the same element
751
- vSideBuffer . delete ( elementName + ':' + jsxKey ) ;
731
+ deleteFromSideBuffer ( elementName , jsxKey ) ;
752
732
}
753
733
754
734
// reconcile attributes
@@ -1039,14 +1019,89 @@ export const vnode_diff = (
1039
1019
const name = vnode_isElementVNode ( vCurrent ) ? vnode_getElementName ( vCurrent ) : null ;
1040
1020
const vKey = getKey ( vCurrent ) || getComponentHash ( vCurrent , container . $getObjectById$ ) ;
1041
1021
if ( vKey != null ) {
1042
- const sideBufferKey = name ? name + ':' + vKey : vKey ;
1022
+ const sideBufferKey = getSideBufferKey ( name , vKey ) ;
1043
1023
vSideBuffer ||= new Map ( ) ;
1044
1024
vSideBuffer . set ( sideBufferKey , vCurrent ) ;
1045
1025
}
1046
1026
}
1047
1027
return vNodeWithKey ;
1048
1028
}
1049
1029
1030
+ function getSideBufferKey ( nodeName : string | null , key : string ) : string ;
1031
+ function getSideBufferKey ( nodeName : string | null , key : string | null ) : string | null ;
1032
+ function getSideBufferKey ( nodeName : string | null , key : string | null ) : string | null {
1033
+ if ( key == null ) {
1034
+ return null ;
1035
+ }
1036
+ return nodeName ? nodeName + ':' + key : key ;
1037
+ }
1038
+
1039
+ function deleteFromSideBuffer ( nodeName : string | null , key : string | null ) : boolean {
1040
+ const sbKey = getSideBufferKey ( nodeName , key ) ;
1041
+ if ( sbKey && vSideBuffer ?. has ( sbKey ) ) {
1042
+ vSideBuffer . delete ( sbKey ) ;
1043
+ return true ;
1044
+ }
1045
+ return false ;
1046
+ }
1047
+
1048
+ /**
1049
+ * Shared utility to resolve a keyed node by:
1050
+ *
1051
+ * 1. Scanning forward siblings via `retrieveChildWithKey`
1052
+ * 2. Falling back to the side buffer using the provided `sideBufferKey`
1053
+ * 3. Creating a new node via `createNew` when not found
1054
+ *
1055
+ * If a node is moved from the side buffer, it is inserted before `vCurrent` under
1056
+ * `parentForInsert`. The function updates `vCurrent`/`vNewNode` accordingly and returns the value
1057
+ * from `createNew` when a new node is created.
1058
+ */
1059
+ function moveOrCreateKeyedNode (
1060
+ nodeName : string | null ,
1061
+ lookupKey : string | null ,
1062
+ sideBufferKey : string | null ,
1063
+ parentForInsert : VNode ,
1064
+ createNew : ( ) => any ,
1065
+ addCurrentToSideBufferOnSideInsert ?: boolean
1066
+ ) : any {
1067
+ // 1) Try to find the node among upcoming siblings
1068
+ vNewNode = retrieveChildWithKey ( nodeName , lookupKey ) ;
1069
+ if ( vNewNode ) {
1070
+ vCurrent = vNewNode ;
1071
+ vNewNode = null ;
1072
+ return ;
1073
+ }
1074
+
1075
+ // 2) Try side buffer
1076
+ if ( sideBufferKey != null ) {
1077
+ const buffered = vSideBuffer ?. get ( sideBufferKey ) || null ;
1078
+ if ( buffered ) {
1079
+ vSideBuffer ! . delete ( sideBufferKey ) ;
1080
+ if ( addCurrentToSideBufferOnSideInsert && vCurrent ) {
1081
+ const currentKey =
1082
+ getKey ( vCurrent ) || getComponentHash ( vCurrent , container . $getObjectById$ ) ;
1083
+ if ( currentKey != null ) {
1084
+ const currentName = vnode_isElementVNode ( vCurrent )
1085
+ ? vnode_getElementName ( vCurrent )
1086
+ : null ;
1087
+ const currentSideKey = getSideBufferKey ( currentName , currentKey ) ;
1088
+ if ( currentSideKey != null ) {
1089
+ vSideBuffer ||= new Map ( ) ;
1090
+ vSideBuffer . set ( currentSideKey , vCurrent ) ;
1091
+ }
1092
+ }
1093
+ }
1094
+ vnode_insertBefore ( journal , parentForInsert as any , buffered , vCurrent ) ;
1095
+ vCurrent = buffered ;
1096
+ vNewNode = null ;
1097
+ return ;
1098
+ }
1099
+ }
1100
+
1101
+ // 3) Create new
1102
+ return createNew ( ) ;
1103
+ }
1104
+
1050
1105
function expectVirtual ( type : VirtualType , jsxKey : string | null ) {
1051
1106
const checkKey = type === VirtualType . Fragment ;
1052
1107
const currentKey = getKey ( vCurrent ) ;
@@ -1058,33 +1113,11 @@ export const vnode_diff = (
1058
1113
1059
1114
if ( isSameNode ) {
1060
1115
// All is good.
1061
- if ( currentKey && vSideBuffer ?. has ( currentKey ) ) {
1062
- vSideBuffer . delete ( currentKey ) ;
1063
- }
1116
+ deleteFromSideBuffer ( null , currentKey ) ;
1064
1117
return ;
1065
1118
}
1066
- if ( jsxKey !== null ) {
1067
- // Try to find the node.
1068
- vNewNode = retrieveChildWithKey ( null , jsxKey ) ;
1069
- if ( vNewNode ) {
1070
- vCurrent = vNewNode ;
1071
- vNewNode = null ;
1072
- } else {
1073
- // If not found, check the side buffer
1074
- vNewNode = vSideBuffer ?. get ( jsxKey ) || null ;
1075
- if ( vNewNode ) {
1076
- // if found insert from side-buffer
1077
- vSideBuffer ! . delete ( jsxKey ) ;
1078
- // Add current to the side buffer
1079
- if ( vCurrent && currentKey ) {
1080
- vSideBuffer ?. set ( currentKey , vCurrent ) ;
1081
- }
1082
- vnode_insertBefore ( journal , vParent as VirtualVNode , vNewNode , vCurrent ) ;
1083
- }
1084
- }
1085
- }
1086
- if ( vNewNode === null ) {
1087
- // if not found it is a new item create it.
1119
+
1120
+ const createNew = ( ) => {
1088
1121
vnode_insertBefore (
1089
1122
journal ,
1090
1123
vParent as VirtualVNode ,
@@ -1093,7 +1126,20 @@ export const vnode_diff = (
1093
1126
) ;
1094
1127
( vNewNode as VirtualVNode ) . setProp ( ELEMENT_KEY , jsxKey ) ;
1095
1128
isDev && ( vNewNode as VirtualVNode ) . setProp ( DEBUG_TYPE , type ) ;
1129
+ } ;
1130
+ // For fragments without a key, always create a new virtual node (ensures rerender semantics)
1131
+ if ( checkKey && jsxKey === null ) {
1132
+ createNew ( ) ;
1133
+ return ;
1096
1134
}
1135
+ moveOrCreateKeyedNode (
1136
+ null ,
1137
+ jsxKey ,
1138
+ getSideBufferKey ( null , jsxKey ) ,
1139
+ vParent as VirtualVNode ,
1140
+ createNew ,
1141
+ true
1142
+ ) ;
1097
1143
}
1098
1144
1099
1145
function expectComponent ( component : Function ) {
@@ -1116,32 +1162,19 @@ export const vnode_diff = (
1116
1162
const hashesAreEqual = componentHash === vNodeComponentHash ;
1117
1163
1118
1164
if ( ! lookupKeysAreEqual ) {
1119
- vNewNode = retrieveChildWithKey ( null , lookupKey ) ;
1120
- if ( vNewNode ) {
1121
- vCurrent = vNewNode ;
1122
- vNewNode = null ;
1123
- host = vCurrent as VirtualVNode ;
1124
- } else {
1125
- vNewNode = lookupKey != null ? vSideBuffer ?. get ( lookupKey ) || null : null ;
1126
- if ( vNewNode ) {
1127
- vSideBuffer ! . delete ( lookupKey ) ;
1128
- vnode_insertBefore ( journal , vParent as VirtualVNode , vNewNode , vCurrent ) ;
1129
- vCurrent = vNewNode ;
1130
- vNewNode = null ;
1131
- host = vCurrent as VirtualVNode ;
1132
- } else {
1133
- insertNewComponent ( host , componentQRL , jsxProps ) ;
1134
- shouldRender = true ;
1135
- host = vNewNode ! as VirtualVNode ;
1136
- }
1137
- }
1165
+ const createNew = ( ) => {
1166
+ insertNewComponent ( host , componentQRL , jsxProps ) ;
1167
+ shouldRender = true ;
1168
+ host = vNewNode ! as VirtualVNode ;
1169
+ } ;
1170
+ moveOrCreateKeyedNode ( null , lookupKey , lookupKey , vParent as VirtualVNode , createNew ) ;
1138
1171
} else if ( ! hashesAreEqual || ! jsxNode . key ) {
1139
1172
insertNewComponent ( host , componentQRL , jsxProps ) ;
1140
1173
host = vNewNode as VirtualVNode ;
1141
1174
shouldRender = true ;
1142
- } else if ( vSideBuffer ?. has ( lookupKey ) ) {
1175
+ } else {
1143
1176
// delete the key from the side buffer if it is the same component
1144
- vSideBuffer . delete ( lookupKey ) ;
1177
+ deleteFromSideBuffer ( null , lookupKey ) ;
1145
1178
}
1146
1179
1147
1180
if ( host ) {
@@ -1196,29 +1229,15 @@ export const vnode_diff = (
1196
1229
insertNewInlineComponent ( ) ;
1197
1230
host = vNewNode as VirtualVNode ;
1198
1231
} else if ( ! lookupKeysAreEqual ) {
1199
- // See if we already have this inline component later on.
1200
- vNewNode = retrieveChildWithKey ( null , lookupKey ) ;
1201
- if ( vNewNode ) {
1202
- vCurrent = vNewNode ;
1203
- vNewNode = null ;
1204
- host = vCurrent as VirtualVNode ;
1205
- } else {
1206
- vNewNode = vSideBuffer ?. get ( lookupKey ) || null ;
1207
- if ( vNewNode ) {
1208
- vSideBuffer ! . delete ( lookupKey ) ;
1209
- vnode_insertBefore ( journal , vParent as VirtualVNode , vNewNode , vCurrent ) ;
1210
- vCurrent = vNewNode ;
1211
- vNewNode = null ;
1212
- host = vCurrent as VirtualVNode ;
1213
- } else {
1214
- // We did not find the inline component, create it.
1215
- insertNewInlineComponent ( ) ;
1216
- host = vNewNode ! as VirtualVNode ;
1217
- }
1218
- }
1219
- } else if ( vSideBuffer ?. has ( lookupKey ) ) {
1232
+ const createNew = ( ) => {
1233
+ // We did not find the inline component, create it.
1234
+ insertNewInlineComponent ( ) ;
1235
+ host = vNewNode ! as VirtualVNode ;
1236
+ } ;
1237
+ moveOrCreateKeyedNode ( null , lookupKey , lookupKey , vParent as VirtualVNode , createNew ) ;
1238
+ } else {
1220
1239
// delete the key from the side buffer if it is the same component
1221
- vSideBuffer . delete ( lookupKey ) ;
1240
+ deleteFromSideBuffer ( null , lookupKey ) ;
1222
1241
}
1223
1242
1224
1243
if ( host ) {
0 commit comments