@legendapp/list 2.1.0-beta.6 → 2.1.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.mts CHANGED
@@ -125,6 +125,12 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
125
125
  index: number;
126
126
  viewOffset?: number | undefined;
127
127
  };
128
+ /**
129
+ * When true, the list initializes scrolled to the last item.
130
+ * Overrides `initialScrollIndex` and `initialScrollOffset` when data is available.
131
+ * @default false
132
+ */
133
+ initialScrollAtEnd?: boolean;
128
134
  /**
129
135
  * Component to render between items, receiving the leading item as prop.
130
136
  */
@@ -358,6 +364,10 @@ interface InternalState {
358
364
  queuedInitialLayout?: boolean | undefined;
359
365
  queuedCalculateItemsInView: number | undefined;
360
366
  dataChangeNeedsScrollUpdate: boolean;
367
+ previousData?: readonly unknown[];
368
+ didColumnsChange?: boolean;
369
+ didDataChange?: boolean;
370
+ isFirst?: boolean;
361
371
  lastBatchingAction: number;
362
372
  ignoreScrollFromMVCP?: {
363
373
  lt?: number;
package/index.d.ts CHANGED
@@ -125,6 +125,12 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
125
125
  index: number;
126
126
  viewOffset?: number | undefined;
127
127
  };
128
+ /**
129
+ * When true, the list initializes scrolled to the last item.
130
+ * Overrides `initialScrollIndex` and `initialScrollOffset` when data is available.
131
+ * @default false
132
+ */
133
+ initialScrollAtEnd?: boolean;
128
134
  /**
129
135
  * Component to render between items, receiving the leading item as prop.
130
136
  */
@@ -358,6 +364,10 @@ interface InternalState {
358
364
  queuedInitialLayout?: boolean | undefined;
359
365
  queuedCalculateItemsInView: number | undefined;
360
366
  dataChangeNeedsScrollUpdate: boolean;
367
+ previousData?: readonly unknown[];
368
+ didColumnsChange?: boolean;
369
+ didDataChange?: boolean;
370
+ isFirst?: boolean;
361
371
  lastBatchingAction: number;
362
372
  ignoreScrollFromMVCP?: {
363
373
  lt?: number;
package/index.js CHANGED
@@ -1196,6 +1196,26 @@ function calculateOffsetForIndex(ctx, state, index) {
1196
1196
  return position;
1197
1197
  }
1198
1198
 
1199
+ // src/core/checkActualChange.ts
1200
+ function checkActualChange(state, dataProp, previousData) {
1201
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
1202
+ return true;
1203
+ }
1204
+ const {
1205
+ idCache,
1206
+ props: { keyExtractor }
1207
+ } = state;
1208
+ for (let i = 0; i < dataProp.length; i++) {
1209
+ if (dataProp[i] !== previousData[i]) {
1210
+ return true;
1211
+ }
1212
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
1213
+ return true;
1214
+ }
1215
+ }
1216
+ return false;
1217
+ }
1218
+
1199
1219
  // src/utils/setPaddingTop.ts
1200
1220
  function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1201
1221
  if (stylePaddingTop !== void 0) {
@@ -1530,7 +1550,11 @@ function scrollTo(ctx, state, params) {
1530
1550
  refScroller,
1531
1551
  props: { horizontal }
1532
1552
  } = state;
1533
- const offset = calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1553
+ let offset = calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1554
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1555
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
1556
+ offset = Math.min(offset, maxOffset);
1557
+ }
1534
1558
  state.scrollHistory.length = 0;
1535
1559
  if (!noScrollingTo) {
1536
1560
  set$(ctx, "scrollingTo", scrollTarget);
@@ -2706,25 +2730,23 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2706
2730
  }
2707
2731
 
2708
2732
  // src/core/checkResetContainers.ts
2709
- function checkResetContainers(ctx, state, isFirst, dataProp) {
2710
- if (state) {
2711
- if (!isFirst && state.props.data !== dataProp) {
2712
- updateAveragesOnDataChange(state, state.props.data, dataProp);
2713
- }
2714
- const { maintainScrollAtEnd } = state.props;
2715
- if (!isFirst) {
2716
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2717
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2718
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2719
- if (!didMaintainScrollAtEnd && dataProp.length > state.props.data.length) {
2720
- state.isEndReached = false;
2721
- }
2722
- if (!didMaintainScrollAtEnd) {
2723
- checkAtTop(state);
2724
- checkAtBottom(ctx, state);
2725
- }
2726
- }
2733
+ function checkResetContainers(ctx, state, dataProp) {
2734
+ const { previousData } = state;
2735
+ if (previousData) {
2736
+ updateAveragesOnDataChange(state, previousData, dataProp);
2727
2737
  }
2738
+ const { maintainScrollAtEnd } = state.props;
2739
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2740
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2741
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2742
+ if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2743
+ state.isEndReached = false;
2744
+ }
2745
+ if (!didMaintainScrollAtEnd) {
2746
+ checkAtTop(state);
2747
+ checkAtBottom(ctx, state);
2748
+ }
2749
+ delete state.previousData;
2728
2750
  }
2729
2751
 
2730
2752
  // src/core/doInitialAllocateContainers.ts
@@ -3150,6 +3172,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3150
3172
  getItemType,
3151
3173
  horizontal,
3152
3174
  initialContainerPoolRatio = 2,
3175
+ initialScrollAtEnd = false,
3153
3176
  initialScrollIndex: initialScrollIndexProp,
3154
3177
  initialScrollOffset: initialScrollOffsetProp,
3155
3178
  itemsAreEqual,
@@ -3189,7 +3212,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3189
3212
  ...rest
3190
3213
  } = props;
3191
3214
  const [renderNum, setRenderNum] = React3.useState(0);
3192
- const initialScrollProp = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3215
+ const initialScrollProp = initialScrollAtEnd ? { index: dataProp.length - 1, viewOffset: 0 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3193
3216
  const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
3194
3217
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3195
3218
  const style = { ...StyleSheet.flatten(styleProp) };
@@ -3213,6 +3236,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3213
3236
  containerItemKeys: /* @__PURE__ */ new Set(),
3214
3237
  containerItemTypes: /* @__PURE__ */ new Map(),
3215
3238
  dataChangeNeedsScrollUpdate: false,
3239
+ didColumnsChange: false,
3240
+ didDataChange: false,
3216
3241
  enableScrollForNextCalculateItemsInView: true,
3217
3242
  endBuffered: -1,
3218
3243
  endNoBuffer: -1,
@@ -3222,10 +3247,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3222
3247
  idsInView: [],
3223
3248
  indexByKey: /* @__PURE__ */ new Map(),
3224
3249
  initialScroll: initialScrollProp,
3225
- isOptimizingItemPositions: false,
3226
3250
  isAtEnd: false,
3227
3251
  isAtStart: false,
3228
3252
  isEndReached: false,
3253
+ isFirst: true,
3254
+ isOptimizingItemPositions: false,
3229
3255
  isStartReached: false,
3230
3256
  lastBatchingAction: Date.now(),
3231
3257
  lastLayout: void 0,
@@ -3264,10 +3290,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3264
3290
  refState.current = ctx.internalState;
3265
3291
  }
3266
3292
  const state = refState.current;
3267
- const isFirst = !state.props.renderItem;
3268
- const didDataChange = state.props.data !== dataProp;
3269
- if (didDataChange) {
3293
+ const isFirstLocal = state.isFirst;
3294
+ state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3295
+ const didDataChangeLocal = state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3296
+ if (didDataChangeLocal) {
3270
3297
  state.dataChangeNeedsScrollUpdate = true;
3298
+ state.didDataChange = true;
3299
+ state.previousData = state.props.data;
3271
3300
  }
3272
3301
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3273
3302
  state.props = {
@@ -3326,7 +3355,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3326
3355
  requestAdjust(ctx, state, paddingDiff);
3327
3356
  }
3328
3357
  };
3329
- if (isFirst) {
3358
+ if (isFirstLocal) {
3330
3359
  initializeStateVars();
3331
3360
  updateItemPositions(
3332
3361
  ctx,
@@ -3344,22 +3373,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3344
3373
  initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
3345
3374
  }
3346
3375
  refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
3347
- if (initialContentOffset2 > 0) {
3348
- scrollTo(ctx, state, {
3349
- animated: false,
3350
- index,
3351
- isInitialScroll: true,
3352
- offset: initialContentOffset2,
3353
- viewPosition: index === dataProp.length - 1 ? 1 : 0
3354
- });
3355
- }
3356
3376
  return initialContentOffset2;
3357
3377
  }
3358
3378
  return 0;
3359
3379
  }, [renderNum]);
3360
- if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
3380
+ if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3361
3381
  refState.current.lastBatchingAction = Date.now();
3362
- if (!keyExtractorProp && !isFirst && didDataChange) {
3382
+ if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3363
3383
  IS_DEV && warnDevOnce(
3364
3384
  "keyExtractor",
3365
3385
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
@@ -3382,22 +3402,41 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3382
3402
  }
3383
3403
  }
3384
3404
  }, []);
3405
+ const doInitialScroll = React3.useCallback(() => {
3406
+ const initialScroll = state.initialScroll;
3407
+ if (initialScroll) {
3408
+ scrollTo(ctx, state, { animated: false, offset: initialContentOffset, ...state.initialScroll || {} });
3409
+ }
3410
+ }, [initialContentOffset, state.initialScroll]);
3411
+ const onLayoutChange = React3.useCallback((layout) => {
3412
+ doInitialScroll();
3413
+ handleLayout(ctx, state, layout, setCanRender);
3414
+ }, []);
3415
+ const { onLayout } = useOnLayoutSync({
3416
+ onLayoutChange,
3417
+ onLayoutProp,
3418
+ ref: refScroller
3419
+ // the type of ScrollView doesn't include measure?
3420
+ });
3385
3421
  React3.useLayoutEffect(() => {
3386
3422
  if (snapToIndices) {
3387
3423
  updateSnapToOffsets(ctx, state);
3388
3424
  }
3389
3425
  }, [snapToIndices]);
3390
3426
  React3.useLayoutEffect(() => {
3391
- const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainers(ctx, state);
3392
- if (!didAllocateContainers) {
3393
- checkResetContainers(
3394
- ctx,
3395
- state,
3396
- /*isFirst*/
3397
- isFirst,
3398
- dataProp
3399
- );
3427
+ const {
3428
+ didColumnsChange,
3429
+ didDataChange,
3430
+ isFirst,
3431
+ props: { data }
3432
+ } = state;
3433
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3434
+ if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3435
+ checkResetContainers(ctx, state, data);
3400
3436
  }
3437
+ state.didColumnsChange = false;
3438
+ state.didDataChange = false;
3439
+ state.isFirst = false;
3401
3440
  }, [dataProp, numColumnsProp]);
3402
3441
  React3.useLayoutEffect(() => {
3403
3442
  set$(ctx, "extraData", extraData);
@@ -3417,15 +3456,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3417
3456
  state.viewabilityConfigCallbackPairs = viewability;
3418
3457
  state.enableScrollForNextCalculateItemsInView = !viewability;
3419
3458
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3420
- const onLayoutChange = React3.useCallback((layout) => {
3421
- handleLayout(ctx, state, layout, setCanRender);
3422
- }, []);
3423
- const { onLayout } = useOnLayoutSync({
3424
- onLayoutChange,
3425
- onLayoutProp,
3426
- ref: refScroller
3427
- // the type of ScrollView doesn't include measure?
3428
- });
3429
3459
  React3.useImperativeHandle(forwardedRef, () => {
3430
3460
  const scrollIndexIntoView = (options) => {
3431
3461
  const state2 = refState.current;
@@ -3509,12 +3539,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3509
3539
  };
3510
3540
  }, []);
3511
3541
  {
3512
- React3.useEffect(() => {
3513
- const { initialScroll } = refState.current;
3514
- if (initialContentOffset) {
3515
- scrollTo(ctx, state, { animated: false, offset: initialContentOffset, ...initialScroll || {} });
3516
- }
3517
- }, []);
3542
+ React3.useEffect(doInitialScroll, []);
3518
3543
  }
3519
3544
  const fns = React3.useMemo(
3520
3545
  () => ({
package/index.mjs CHANGED
@@ -1175,6 +1175,26 @@ function calculateOffsetForIndex(ctx, state, index) {
1175
1175
  return position;
1176
1176
  }
1177
1177
 
1178
+ // src/core/checkActualChange.ts
1179
+ function checkActualChange(state, dataProp, previousData) {
1180
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
1181
+ return true;
1182
+ }
1183
+ const {
1184
+ idCache,
1185
+ props: { keyExtractor }
1186
+ } = state;
1187
+ for (let i = 0; i < dataProp.length; i++) {
1188
+ if (dataProp[i] !== previousData[i]) {
1189
+ return true;
1190
+ }
1191
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
1192
+ return true;
1193
+ }
1194
+ }
1195
+ return false;
1196
+ }
1197
+
1178
1198
  // src/utils/setPaddingTop.ts
1179
1199
  function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1180
1200
  if (stylePaddingTop !== void 0) {
@@ -1509,7 +1529,11 @@ function scrollTo(ctx, state, params) {
1509
1529
  refScroller,
1510
1530
  props: { horizontal }
1511
1531
  } = state;
1512
- const offset = calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1532
+ let offset = calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1533
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1534
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
1535
+ offset = Math.min(offset, maxOffset);
1536
+ }
1513
1537
  state.scrollHistory.length = 0;
1514
1538
  if (!noScrollingTo) {
1515
1539
  set$(ctx, "scrollingTo", scrollTarget);
@@ -2685,25 +2709,23 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2685
2709
  }
2686
2710
 
2687
2711
  // src/core/checkResetContainers.ts
2688
- function checkResetContainers(ctx, state, isFirst, dataProp) {
2689
- if (state) {
2690
- if (!isFirst && state.props.data !== dataProp) {
2691
- updateAveragesOnDataChange(state, state.props.data, dataProp);
2692
- }
2693
- const { maintainScrollAtEnd } = state.props;
2694
- if (!isFirst) {
2695
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2696
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2697
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2698
- if (!didMaintainScrollAtEnd && dataProp.length > state.props.data.length) {
2699
- state.isEndReached = false;
2700
- }
2701
- if (!didMaintainScrollAtEnd) {
2702
- checkAtTop(state);
2703
- checkAtBottom(ctx, state);
2704
- }
2705
- }
2712
+ function checkResetContainers(ctx, state, dataProp) {
2713
+ const { previousData } = state;
2714
+ if (previousData) {
2715
+ updateAveragesOnDataChange(state, previousData, dataProp);
2706
2716
  }
2717
+ const { maintainScrollAtEnd } = state.props;
2718
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2719
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2720
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2721
+ if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2722
+ state.isEndReached = false;
2723
+ }
2724
+ if (!didMaintainScrollAtEnd) {
2725
+ checkAtTop(state);
2726
+ checkAtBottom(ctx, state);
2727
+ }
2728
+ delete state.previousData;
2707
2729
  }
2708
2730
 
2709
2731
  // src/core/doInitialAllocateContainers.ts
@@ -3129,6 +3151,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3129
3151
  getItemType,
3130
3152
  horizontal,
3131
3153
  initialContainerPoolRatio = 2,
3154
+ initialScrollAtEnd = false,
3132
3155
  initialScrollIndex: initialScrollIndexProp,
3133
3156
  initialScrollOffset: initialScrollOffsetProp,
3134
3157
  itemsAreEqual,
@@ -3168,7 +3191,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3168
3191
  ...rest
3169
3192
  } = props;
3170
3193
  const [renderNum, setRenderNum] = useState(0);
3171
- const initialScrollProp = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3194
+ const initialScrollProp = initialScrollAtEnd ? { index: dataProp.length - 1, viewOffset: 0 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3172
3195
  const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
3173
3196
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3174
3197
  const style = { ...StyleSheet.flatten(styleProp) };
@@ -3192,6 +3215,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3192
3215
  containerItemKeys: /* @__PURE__ */ new Set(),
3193
3216
  containerItemTypes: /* @__PURE__ */ new Map(),
3194
3217
  dataChangeNeedsScrollUpdate: false,
3218
+ didColumnsChange: false,
3219
+ didDataChange: false,
3195
3220
  enableScrollForNextCalculateItemsInView: true,
3196
3221
  endBuffered: -1,
3197
3222
  endNoBuffer: -1,
@@ -3201,10 +3226,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3201
3226
  idsInView: [],
3202
3227
  indexByKey: /* @__PURE__ */ new Map(),
3203
3228
  initialScroll: initialScrollProp,
3204
- isOptimizingItemPositions: false,
3205
3229
  isAtEnd: false,
3206
3230
  isAtStart: false,
3207
3231
  isEndReached: false,
3232
+ isFirst: true,
3233
+ isOptimizingItemPositions: false,
3208
3234
  isStartReached: false,
3209
3235
  lastBatchingAction: Date.now(),
3210
3236
  lastLayout: void 0,
@@ -3243,10 +3269,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3243
3269
  refState.current = ctx.internalState;
3244
3270
  }
3245
3271
  const state = refState.current;
3246
- const isFirst = !state.props.renderItem;
3247
- const didDataChange = state.props.data !== dataProp;
3248
- if (didDataChange) {
3272
+ const isFirstLocal = state.isFirst;
3273
+ state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3274
+ const didDataChangeLocal = state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3275
+ if (didDataChangeLocal) {
3249
3276
  state.dataChangeNeedsScrollUpdate = true;
3277
+ state.didDataChange = true;
3278
+ state.previousData = state.props.data;
3250
3279
  }
3251
3280
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3252
3281
  state.props = {
@@ -3305,7 +3334,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3305
3334
  requestAdjust(ctx, state, paddingDiff);
3306
3335
  }
3307
3336
  };
3308
- if (isFirst) {
3337
+ if (isFirstLocal) {
3309
3338
  initializeStateVars();
3310
3339
  updateItemPositions(
3311
3340
  ctx,
@@ -3323,22 +3352,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3323
3352
  initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
3324
3353
  }
3325
3354
  refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
3326
- if (initialContentOffset2 > 0) {
3327
- scrollTo(ctx, state, {
3328
- animated: false,
3329
- index,
3330
- isInitialScroll: true,
3331
- offset: initialContentOffset2,
3332
- viewPosition: index === dataProp.length - 1 ? 1 : 0
3333
- });
3334
- }
3335
3355
  return initialContentOffset2;
3336
3356
  }
3337
3357
  return 0;
3338
3358
  }, [renderNum]);
3339
- if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
3359
+ if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3340
3360
  refState.current.lastBatchingAction = Date.now();
3341
- if (!keyExtractorProp && !isFirst && didDataChange) {
3361
+ if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3342
3362
  IS_DEV && warnDevOnce(
3343
3363
  "keyExtractor",
3344
3364
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
@@ -3361,22 +3381,41 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3361
3381
  }
3362
3382
  }
3363
3383
  }, []);
3384
+ const doInitialScroll = useCallback(() => {
3385
+ const initialScroll = state.initialScroll;
3386
+ if (initialScroll) {
3387
+ scrollTo(ctx, state, { animated: false, offset: initialContentOffset, ...state.initialScroll || {} });
3388
+ }
3389
+ }, [initialContentOffset, state.initialScroll]);
3390
+ const onLayoutChange = useCallback((layout) => {
3391
+ doInitialScroll();
3392
+ handleLayout(ctx, state, layout, setCanRender);
3393
+ }, []);
3394
+ const { onLayout } = useOnLayoutSync({
3395
+ onLayoutChange,
3396
+ onLayoutProp,
3397
+ ref: refScroller
3398
+ // the type of ScrollView doesn't include measure?
3399
+ });
3364
3400
  useLayoutEffect(() => {
3365
3401
  if (snapToIndices) {
3366
3402
  updateSnapToOffsets(ctx, state);
3367
3403
  }
3368
3404
  }, [snapToIndices]);
3369
3405
  useLayoutEffect(() => {
3370
- const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainers(ctx, state);
3371
- if (!didAllocateContainers) {
3372
- checkResetContainers(
3373
- ctx,
3374
- state,
3375
- /*isFirst*/
3376
- isFirst,
3377
- dataProp
3378
- );
3406
+ const {
3407
+ didColumnsChange,
3408
+ didDataChange,
3409
+ isFirst,
3410
+ props: { data }
3411
+ } = state;
3412
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3413
+ if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3414
+ checkResetContainers(ctx, state, data);
3379
3415
  }
3416
+ state.didColumnsChange = false;
3417
+ state.didDataChange = false;
3418
+ state.isFirst = false;
3380
3419
  }, [dataProp, numColumnsProp]);
3381
3420
  useLayoutEffect(() => {
3382
3421
  set$(ctx, "extraData", extraData);
@@ -3396,15 +3435,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3396
3435
  state.viewabilityConfigCallbackPairs = viewability;
3397
3436
  state.enableScrollForNextCalculateItemsInView = !viewability;
3398
3437
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3399
- const onLayoutChange = useCallback((layout) => {
3400
- handleLayout(ctx, state, layout, setCanRender);
3401
- }, []);
3402
- const { onLayout } = useOnLayoutSync({
3403
- onLayoutChange,
3404
- onLayoutProp,
3405
- ref: refScroller
3406
- // the type of ScrollView doesn't include measure?
3407
- });
3408
3438
  useImperativeHandle(forwardedRef, () => {
3409
3439
  const scrollIndexIntoView = (options) => {
3410
3440
  const state2 = refState.current;
@@ -3488,12 +3518,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3488
3518
  };
3489
3519
  }, []);
3490
3520
  {
3491
- useEffect(() => {
3492
- const { initialScroll } = refState.current;
3493
- if (initialContentOffset) {
3494
- scrollTo(ctx, state, { animated: false, offset: initialContentOffset, ...initialScroll || {} });
3495
- }
3496
- }, []);
3521
+ useEffect(doInitialScroll, []);
3497
3522
  }
3498
3523
  const fns = useMemo(
3499
3524
  () => ({