@legendapp/list 3.3.1 → 3.3.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 3.3.2
2
+
3
+ - Perf: Recycled rows update their current item without changing the container context, so recycling hooks avoid extra rerenders during scroll.
4
+ - Perf: Internal signal hooks recreate subscriptions less often
5
+
1
6
  ## 3.3.1
2
7
 
3
8
  - Perf: Animated `scrollToIndex` and `scrollToOffset` calls now mount the destination rows before native scrolling starts, so long programmatic jumps are less likely to show blank space at the target.
package/animated.d.ts CHANGED
@@ -8,7 +8,7 @@ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
8
8
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
9
9
  }
10
10
 
11
- type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
11
+ type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemInfo${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
12
12
  type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
13
13
  type ListenerTypeValueMap = {
14
14
  activeStickyIndex: number;
@@ -48,6 +48,8 @@ type ListenerTypeValueMap = {
48
48
  [K in ListenerType as K extends `containerItemKey${number}` ? K : never]: string;
49
49
  } & {
50
50
  [K in ListenerType as K extends `containerItemData${number}` ? K : never]: any;
51
+ } & {
52
+ [K in ListenerType as K extends `containerItemInfo${number}` ? K : never]: ContainerItemInfo;
51
53
  } & {
52
54
  [K in ListenerType as K extends `containerPosition${number}` ? K : never]: number;
53
55
  } & {
@@ -57,6 +59,11 @@ type ListenerTypeValueMap = {
57
59
  } & {
58
60
  [K in ListenerType as K extends `containerSticky${number}` ? K : never]: boolean;
59
61
  };
62
+ interface ContainerItemInfo {
63
+ index: number;
64
+ itemKey: string;
65
+ value: any;
66
+ }
60
67
 
61
68
  interface Insets {
62
69
  top: number;
@@ -9,7 +9,7 @@ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
9
9
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
10
10
  }
11
11
 
12
- type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
12
+ type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemInfo${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
13
13
  type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
14
14
  type ListenerTypeValueMap = {
15
15
  activeStickyIndex: number;
@@ -49,6 +49,8 @@ type ListenerTypeValueMap = {
49
49
  [K in ListenerType as K extends `containerItemKey${number}` ? K : never]: string;
50
50
  } & {
51
51
  [K in ListenerType as K extends `containerItemData${number}` ? K : never]: any;
52
+ } & {
53
+ [K in ListenerType as K extends `containerItemInfo${number}` ? K : never]: ContainerItemInfo;
52
54
  } & {
53
55
  [K in ListenerType as K extends `containerPosition${number}` ? K : never]: number;
54
56
  } & {
@@ -58,6 +60,11 @@ type ListenerTypeValueMap = {
58
60
  } & {
59
61
  [K in ListenerType as K extends `containerSticky${number}` ? K : never]: boolean;
60
62
  };
63
+ interface ContainerItemInfo {
64
+ index: number;
65
+ itemKey: string;
66
+ value: any;
67
+ }
61
68
 
62
69
  interface Insets {
63
70
  top: number;
package/keyboard.d.ts CHANGED
@@ -10,7 +10,7 @@ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
10
10
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
11
11
  }
12
12
 
13
- type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
13
+ type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemInfo${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
14
14
  type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
15
15
  type ListenerTypeValueMap = {
16
16
  activeStickyIndex: number;
@@ -50,6 +50,8 @@ type ListenerTypeValueMap = {
50
50
  [K in ListenerType as K extends `containerItemKey${number}` ? K : never]: string;
51
51
  } & {
52
52
  [K in ListenerType as K extends `containerItemData${number}` ? K : never]: any;
53
+ } & {
54
+ [K in ListenerType as K extends `containerItemInfo${number}` ? K : never]: ContainerItemInfo;
53
55
  } & {
54
56
  [K in ListenerType as K extends `containerPosition${number}` ? K : never]: number;
55
57
  } & {
@@ -59,6 +61,11 @@ type ListenerTypeValueMap = {
59
61
  } & {
60
62
  [K in ListenerType as K extends `containerSticky${number}` ? K : never]: boolean;
61
63
  };
64
+ interface ContainerItemInfo {
65
+ index: number;
66
+ itemKey: string;
67
+ value: any;
68
+ }
62
69
 
63
70
  interface Insets {
64
71
  top: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "3.3.1",
3
+ "version": "3.3.2",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,
package/react-native.d.ts CHANGED
@@ -8,7 +8,7 @@ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
8
8
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
9
9
  }
10
10
 
11
- type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
11
+ type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemInfo${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
12
12
  type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
13
13
  type ListenerTypeValueMap = {
14
14
  activeStickyIndex: number;
@@ -48,6 +48,8 @@ type ListenerTypeValueMap = {
48
48
  [K in ListenerType as K extends `containerItemKey${number}` ? K : never]: string;
49
49
  } & {
50
50
  [K in ListenerType as K extends `containerItemData${number}` ? K : never]: any;
51
+ } & {
52
+ [K in ListenerType as K extends `containerItemInfo${number}` ? K : never]: ContainerItemInfo;
51
53
  } & {
52
54
  [K in ListenerType as K extends `containerPosition${number}` ? K : never]: number;
53
55
  } & {
@@ -57,6 +59,11 @@ type ListenerTypeValueMap = {
57
59
  } & {
58
60
  [K in ListenerType as K extends `containerSticky${number}` ? K : never]: boolean;
59
61
  };
62
+ interface ContainerItemInfo {
63
+ index: number;
64
+ itemKey: string;
65
+ value: any;
66
+ }
60
67
 
61
68
  interface Insets {
62
69
  top: number;
package/react-native.js CHANGED
@@ -138,6 +138,7 @@ var createAnimatedValue = (value) => new ReactNative.Animated.Value(value);
138
138
 
139
139
  // src/state/state.tsx
140
140
  var ContextState = React2__namespace.createContext(null);
141
+ var SIGNAL_NAMES_SEPARATOR = "\0";
141
142
  var contextNum = 0;
142
143
  function StateProvider({ children }) {
143
144
  const [value] = React2__namespace.useState(() => ({
@@ -209,6 +210,12 @@ function createSelectorFunctionsArr(ctx, signalNames) {
209
210
  }
210
211
  };
211
212
  }
213
+ function getSignalNamesKey(signalNames) {
214
+ return signalNames.length === 1 ? signalNames[0] : signalNames.join(SIGNAL_NAMES_SEPARATOR);
215
+ }
216
+ function getSignalNamesFromKey(signalNamesKey) {
217
+ return signalNamesKey.split(SIGNAL_NAMES_SEPARATOR);
218
+ }
212
219
  function listen$(ctx, signalName, cb) {
213
220
  const { listeners } = ctx;
214
221
  let setListeners = listeners.get(signalName);
@@ -256,7 +263,11 @@ function notifyPosition$(ctx, key, value) {
256
263
  }
257
264
  function useArr$(signalNames) {
258
265
  const ctx = React2__namespace.useContext(ContextState);
259
- const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
266
+ const signalNamesKey = getSignalNamesKey(signalNames);
267
+ const { subscribe, get } = React2__namespace.useMemo(
268
+ () => createSelectorFunctionsArr(ctx, getSignalNamesFromKey(signalNamesKey)),
269
+ [ctx, signalNamesKey]
270
+ );
260
271
  const value = shim.useSyncExternalStore(subscribe, get, get);
261
272
  return value;
262
273
  }
@@ -608,9 +619,22 @@ function findContainerId(ctx, key) {
608
619
 
609
620
  // src/state/ContextContainer.ts
610
621
  var ContextContainer = React2.createContext(null);
622
+ var NO_CONTAINER_ID = -1;
611
623
  function useContextContainer() {
612
624
  return React2.useContext(ContextContainer);
613
625
  }
626
+ function useContainerItemInfo(containerContext) {
627
+ var _a3;
628
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
629
+ const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
630
+ return containerContext ? itemInfo : void 0;
631
+ }
632
+ function useContainerItemKey(containerContext) {
633
+ var _a3;
634
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
635
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
636
+ return containerContext ? itemKey : void 0;
637
+ }
614
638
  function useAdaptiveRender() {
615
639
  const [mode] = useArr$(["adaptiveRender"]);
616
640
  return mode;
@@ -681,40 +705,35 @@ function useViewabilityAmount(callback) {
681
705
  }
682
706
  function useRecyclingEffect(effect) {
683
707
  const containerContext = useContextContainer();
684
- const prevValues = React2.useRef({
685
- prevIndex: void 0,
686
- prevItem: void 0
687
- });
708
+ const itemInfo = useContainerItemInfo(containerContext);
709
+ const prevInfo = React2.useRef(void 0);
688
710
  React2.useEffect(() => {
689
- if (!containerContext) {
711
+ if (!itemInfo) {
690
712
  return;
691
713
  }
692
- const { index, value } = containerContext;
693
714
  let ret;
694
- if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
715
+ if (prevInfo.current) {
695
716
  ret = effect({
696
- index,
697
- item: value,
698
- prevIndex: prevValues.current.prevIndex,
699
- prevItem: prevValues.current.prevItem
717
+ index: itemInfo.index,
718
+ item: itemInfo.value,
719
+ prevIndex: prevInfo.current.index,
720
+ prevItem: prevInfo.current.value
700
721
  });
701
722
  }
702
- prevValues.current = {
703
- prevIndex: index,
704
- prevItem: value
705
- };
723
+ prevInfo.current = itemInfo;
706
724
  return ret;
707
- }, [effect, containerContext]);
725
+ }, [effect, itemInfo]);
708
726
  }
709
727
  function useRecyclingState(valueOrFun) {
710
- var _a3, _b;
728
+ var _a3;
711
729
  const containerContext = useContextContainer();
712
- const computeValue = (ctx) => {
730
+ const itemInfo = useContainerItemInfo(containerContext);
731
+ const computeValue = (info) => {
713
732
  if (isFunction(valueOrFun)) {
714
733
  const initializer = valueOrFun;
715
- return ctx ? initializer({
716
- index: ctx.index,
717
- item: ctx.value,
734
+ return info ? initializer({
735
+ index: info.index,
736
+ item: info.value,
718
737
  prevIndex: void 0,
719
738
  prevItem: void 0
720
739
  }) : initializer();
@@ -722,13 +741,12 @@ function useRecyclingState(valueOrFun) {
722
741
  return valueOrFun;
723
742
  };
724
743
  const [stateValue, setStateValue] = React2.useState(() => {
725
- return computeValue(containerContext);
744
+ return computeValue(itemInfo);
726
745
  });
727
- const prevItemKeyRef = React2.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
728
- const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
729
- if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
730
- prevItemKeyRef.current = currentItemKey;
731
- setStateValue(computeValue(containerContext));
746
+ const prevItemKeyRef = React2.useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
747
+ if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
748
+ prevItemKeyRef.current = itemInfo.itemKey;
749
+ setStateValue(computeValue(itemInfo));
732
750
  }
733
751
  const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
734
752
  const setState = React2.useCallback(
@@ -747,12 +765,10 @@ function useRecyclingState(valueOrFun) {
747
765
  }
748
766
  function useIsLastItem() {
749
767
  const containerContext = useContextContainer();
768
+ const itemKey = useContainerItemKey(containerContext);
750
769
  const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
751
- if (containerContext) {
752
- const { itemKey } = containerContext;
753
- if (!isNullOrUndefined(itemKey)) {
754
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
755
- }
770
+ if (containerContext && !isNullOrUndefined(itemKey)) {
771
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
756
772
  }
757
773
  return false;
758
774
  });
@@ -766,12 +782,7 @@ var noop = () => {
766
782
  };
767
783
  function useSyncLayout() {
768
784
  const containerContext = useContextContainer();
769
- if (IsNewArchitecture && containerContext) {
770
- const { triggerLayout: syncLayout } = containerContext;
771
- return syncLayout;
772
- } else {
773
- return noop;
774
- }
785
+ return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
775
786
  }
776
787
 
777
788
  // src/components/Separator.tsx
@@ -3407,22 +3418,28 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3407
3418
  set$(ctx, `containerSpan${containerIndex}`, span);
3408
3419
  }
3409
3420
  }
3421
+ const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3410
3422
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3423
+ let itemInfoValue = prevData;
3424
+ let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3425
+ const updateData = () => {
3426
+ set$(ctx, `containerItemData${containerIndex}`, item);
3427
+ itemInfoValue = item;
3428
+ didChangeItemInfo = true;
3429
+ didRefreshData = true;
3430
+ };
3411
3431
  if (prevData !== item) {
3412
3432
  const pendingDataComparison = ((_e = state.pendingDataComparison) == null ? void 0 : _e.previousData) === state.previousData && ((_f = state.pendingDataComparison) == null ? void 0 : _f.nextData) === data ? state.pendingDataComparison : void 0;
3413
3433
  const cachedComparison = (_g = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _g : 0;
3414
3434
  if (cachedComparison === 2) {
3415
- set$(ctx, `containerItemData${containerIndex}`, item);
3416
- didRefreshData = true;
3435
+ updateData();
3417
3436
  } else if (cachedComparison !== 1) {
3418
3437
  const nextItemKey = (_h = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _h : itemKey;
3419
3438
  const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3420
3439
  if (prevData === void 0 || !keyExtractor || prevKey !== nextItemKey) {
3421
- set$(ctx, `containerItemData${containerIndex}`, item);
3422
- didRefreshData = true;
3440
+ updateData();
3423
3441
  } else if (!itemsAreEqual) {
3424
- set$(ctx, `containerItemData${containerIndex}`, item);
3425
- didRefreshData = true;
3442
+ updateData();
3426
3443
  } else {
3427
3444
  const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3428
3445
  if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
@@ -3438,12 +3455,18 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3438
3455
  state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3439
3456
  }
3440
3457
  if (!isEqual) {
3441
- set$(ctx, `containerItemData${containerIndex}`, item);
3442
- didRefreshData = true;
3458
+ updateData();
3443
3459
  }
3444
3460
  }
3445
3461
  }
3446
3462
  }
3463
+ if (didChangeItemInfo) {
3464
+ set$(ctx, `containerItemInfo${containerIndex}`, {
3465
+ index: itemIndex,
3466
+ itemKey,
3467
+ value: itemInfoValue
3468
+ });
3469
+ }
3447
3470
  return { didChangePosition, didRefreshData };
3448
3471
  }
3449
3472
 
@@ -4689,6 +4712,11 @@ function calculateItemsInView(ctx, params = {}) {
4689
4712
  }
4690
4713
  set$(ctx, `containerItemKey${containerIndex}`, id);
4691
4714
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4715
+ set$(ctx, `containerItemInfo${containerIndex}`, {
4716
+ index: i,
4717
+ itemKey: id,
4718
+ value: data[i]
4719
+ });
4692
4720
  if (allocation.itemType !== void 0) {
4693
4721
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4694
4722
  }
@@ -4755,6 +4783,7 @@ function calculateItemsInView(ctx, params = {}) {
4755
4783
  }
4756
4784
  set$(ctx, `containerItemKey${i}`, void 0);
4757
4785
  set$(ctx, `containerItemData${i}`, void 0);
4786
+ set$(ctx, `containerItemInfo${i}`, void 0);
4758
4787
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4759
4788
  set$(ctx, `containerColumn${i}`, -1);
4760
4789
  set$(ctx, `containerSpan${i}`, 1);
@@ -5324,12 +5353,9 @@ var Container = typedMemo(function Container2({
5324
5353
  ctx.viewRefs.set(id, ref);
5325
5354
  return {
5326
5355
  containerId: id,
5327
- index,
5328
- itemKey,
5329
- triggerLayout,
5330
- value: data
5356
+ triggerLayout
5331
5357
  };
5332
- }, [id, itemKey, index, data, triggerLayout]);
5358
+ }, [id, triggerLayout]);
5333
5359
  React2.useLayoutEffect(() => {
5334
5360
  ctx.containerLayoutTriggers.set(id, triggerLayout);
5335
5361
  return () => {
package/react-native.mjs CHANGED
@@ -117,6 +117,7 @@ var createAnimatedValue = (value) => new Animated.Value(value);
117
117
 
118
118
  // src/state/state.tsx
119
119
  var ContextState = React2.createContext(null);
120
+ var SIGNAL_NAMES_SEPARATOR = "\0";
120
121
  var contextNum = 0;
121
122
  function StateProvider({ children }) {
122
123
  const [value] = React2.useState(() => ({
@@ -188,6 +189,12 @@ function createSelectorFunctionsArr(ctx, signalNames) {
188
189
  }
189
190
  };
190
191
  }
192
+ function getSignalNamesKey(signalNames) {
193
+ return signalNames.length === 1 ? signalNames[0] : signalNames.join(SIGNAL_NAMES_SEPARATOR);
194
+ }
195
+ function getSignalNamesFromKey(signalNamesKey) {
196
+ return signalNamesKey.split(SIGNAL_NAMES_SEPARATOR);
197
+ }
191
198
  function listen$(ctx, signalName, cb) {
192
199
  const { listeners } = ctx;
193
200
  let setListeners = listeners.get(signalName);
@@ -235,7 +242,11 @@ function notifyPosition$(ctx, key, value) {
235
242
  }
236
243
  function useArr$(signalNames) {
237
244
  const ctx = React2.useContext(ContextState);
238
- const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
245
+ const signalNamesKey = getSignalNamesKey(signalNames);
246
+ const { subscribe, get } = React2.useMemo(
247
+ () => createSelectorFunctionsArr(ctx, getSignalNamesFromKey(signalNamesKey)),
248
+ [ctx, signalNamesKey]
249
+ );
239
250
  const value = useSyncExternalStore(subscribe, get, get);
240
251
  return value;
241
252
  }
@@ -587,9 +598,22 @@ function findContainerId(ctx, key) {
587
598
 
588
599
  // src/state/ContextContainer.ts
589
600
  var ContextContainer = createContext(null);
601
+ var NO_CONTAINER_ID = -1;
590
602
  function useContextContainer() {
591
603
  return useContext(ContextContainer);
592
604
  }
605
+ function useContainerItemInfo(containerContext) {
606
+ var _a3;
607
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
608
+ const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
609
+ return containerContext ? itemInfo : void 0;
610
+ }
611
+ function useContainerItemKey(containerContext) {
612
+ var _a3;
613
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
614
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
615
+ return containerContext ? itemKey : void 0;
616
+ }
593
617
  function useAdaptiveRender() {
594
618
  const [mode] = useArr$(["adaptiveRender"]);
595
619
  return mode;
@@ -660,40 +684,35 @@ function useViewabilityAmount(callback) {
660
684
  }
661
685
  function useRecyclingEffect(effect) {
662
686
  const containerContext = useContextContainer();
663
- const prevValues = useRef({
664
- prevIndex: void 0,
665
- prevItem: void 0
666
- });
687
+ const itemInfo = useContainerItemInfo(containerContext);
688
+ const prevInfo = useRef(void 0);
667
689
  useEffect(() => {
668
- if (!containerContext) {
690
+ if (!itemInfo) {
669
691
  return;
670
692
  }
671
- const { index, value } = containerContext;
672
693
  let ret;
673
- if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
694
+ if (prevInfo.current) {
674
695
  ret = effect({
675
- index,
676
- item: value,
677
- prevIndex: prevValues.current.prevIndex,
678
- prevItem: prevValues.current.prevItem
696
+ index: itemInfo.index,
697
+ item: itemInfo.value,
698
+ prevIndex: prevInfo.current.index,
699
+ prevItem: prevInfo.current.value
679
700
  });
680
701
  }
681
- prevValues.current = {
682
- prevIndex: index,
683
- prevItem: value
684
- };
702
+ prevInfo.current = itemInfo;
685
703
  return ret;
686
- }, [effect, containerContext]);
704
+ }, [effect, itemInfo]);
687
705
  }
688
706
  function useRecyclingState(valueOrFun) {
689
- var _a3, _b;
707
+ var _a3;
690
708
  const containerContext = useContextContainer();
691
- const computeValue = (ctx) => {
709
+ const itemInfo = useContainerItemInfo(containerContext);
710
+ const computeValue = (info) => {
692
711
  if (isFunction(valueOrFun)) {
693
712
  const initializer = valueOrFun;
694
- return ctx ? initializer({
695
- index: ctx.index,
696
- item: ctx.value,
713
+ return info ? initializer({
714
+ index: info.index,
715
+ item: info.value,
697
716
  prevIndex: void 0,
698
717
  prevItem: void 0
699
718
  }) : initializer();
@@ -701,13 +720,12 @@ function useRecyclingState(valueOrFun) {
701
720
  return valueOrFun;
702
721
  };
703
722
  const [stateValue, setStateValue] = useState(() => {
704
- return computeValue(containerContext);
723
+ return computeValue(itemInfo);
705
724
  });
706
- const prevItemKeyRef = useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
707
- const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
708
- if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
709
- prevItemKeyRef.current = currentItemKey;
710
- setStateValue(computeValue(containerContext));
725
+ const prevItemKeyRef = useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
726
+ if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
727
+ prevItemKeyRef.current = itemInfo.itemKey;
728
+ setStateValue(computeValue(itemInfo));
711
729
  }
712
730
  const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
713
731
  const setState = useCallback(
@@ -726,12 +744,10 @@ function useRecyclingState(valueOrFun) {
726
744
  }
727
745
  function useIsLastItem() {
728
746
  const containerContext = useContextContainer();
747
+ const itemKey = useContainerItemKey(containerContext);
729
748
  const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
730
- if (containerContext) {
731
- const { itemKey } = containerContext;
732
- if (!isNullOrUndefined(itemKey)) {
733
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
734
- }
749
+ if (containerContext && !isNullOrUndefined(itemKey)) {
750
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
735
751
  }
736
752
  return false;
737
753
  });
@@ -745,12 +761,7 @@ var noop = () => {
745
761
  };
746
762
  function useSyncLayout() {
747
763
  const containerContext = useContextContainer();
748
- if (IsNewArchitecture && containerContext) {
749
- const { triggerLayout: syncLayout } = containerContext;
750
- return syncLayout;
751
- } else {
752
- return noop;
753
- }
764
+ return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
754
765
  }
755
766
 
756
767
  // src/components/Separator.tsx
@@ -3386,22 +3397,28 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3386
3397
  set$(ctx, `containerSpan${containerIndex}`, span);
3387
3398
  }
3388
3399
  }
3400
+ const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3389
3401
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3402
+ let itemInfoValue = prevData;
3403
+ let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3404
+ const updateData = () => {
3405
+ set$(ctx, `containerItemData${containerIndex}`, item);
3406
+ itemInfoValue = item;
3407
+ didChangeItemInfo = true;
3408
+ didRefreshData = true;
3409
+ };
3390
3410
  if (prevData !== item) {
3391
3411
  const pendingDataComparison = ((_e = state.pendingDataComparison) == null ? void 0 : _e.previousData) === state.previousData && ((_f = state.pendingDataComparison) == null ? void 0 : _f.nextData) === data ? state.pendingDataComparison : void 0;
3392
3412
  const cachedComparison = (_g = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _g : 0;
3393
3413
  if (cachedComparison === 2) {
3394
- set$(ctx, `containerItemData${containerIndex}`, item);
3395
- didRefreshData = true;
3414
+ updateData();
3396
3415
  } else if (cachedComparison !== 1) {
3397
3416
  const nextItemKey = (_h = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _h : itemKey;
3398
3417
  const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3399
3418
  if (prevData === void 0 || !keyExtractor || prevKey !== nextItemKey) {
3400
- set$(ctx, `containerItemData${containerIndex}`, item);
3401
- didRefreshData = true;
3419
+ updateData();
3402
3420
  } else if (!itemsAreEqual) {
3403
- set$(ctx, `containerItemData${containerIndex}`, item);
3404
- didRefreshData = true;
3421
+ updateData();
3405
3422
  } else {
3406
3423
  const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3407
3424
  if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
@@ -3417,12 +3434,18 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3417
3434
  state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3418
3435
  }
3419
3436
  if (!isEqual) {
3420
- set$(ctx, `containerItemData${containerIndex}`, item);
3421
- didRefreshData = true;
3437
+ updateData();
3422
3438
  }
3423
3439
  }
3424
3440
  }
3425
3441
  }
3442
+ if (didChangeItemInfo) {
3443
+ set$(ctx, `containerItemInfo${containerIndex}`, {
3444
+ index: itemIndex,
3445
+ itemKey,
3446
+ value: itemInfoValue
3447
+ });
3448
+ }
3426
3449
  return { didChangePosition, didRefreshData };
3427
3450
  }
3428
3451
 
@@ -4668,6 +4691,11 @@ function calculateItemsInView(ctx, params = {}) {
4668
4691
  }
4669
4692
  set$(ctx, `containerItemKey${containerIndex}`, id);
4670
4693
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4694
+ set$(ctx, `containerItemInfo${containerIndex}`, {
4695
+ index: i,
4696
+ itemKey: id,
4697
+ value: data[i]
4698
+ });
4671
4699
  if (allocation.itemType !== void 0) {
4672
4700
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4673
4701
  }
@@ -4734,6 +4762,7 @@ function calculateItemsInView(ctx, params = {}) {
4734
4762
  }
4735
4763
  set$(ctx, `containerItemKey${i}`, void 0);
4736
4764
  set$(ctx, `containerItemData${i}`, void 0);
4765
+ set$(ctx, `containerItemInfo${i}`, void 0);
4737
4766
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4738
4767
  set$(ctx, `containerColumn${i}`, -1);
4739
4768
  set$(ctx, `containerSpan${i}`, 1);
@@ -5303,12 +5332,9 @@ var Container = typedMemo(function Container2({
5303
5332
  ctx.viewRefs.set(id, ref);
5304
5333
  return {
5305
5334
  containerId: id,
5306
- index,
5307
- itemKey,
5308
- triggerLayout,
5309
- value: data
5335
+ triggerLayout
5310
5336
  };
5311
- }, [id, itemKey, index, data, triggerLayout]);
5337
+ }, [id, triggerLayout]);
5312
5338
  useLayoutEffect(() => {
5313
5339
  ctx.containerLayoutTriggers.set(id, triggerLayout);
5314
5340
  return () => {