diff --git a/change/@fluentui-react-virtualizer-31fd546c-91d0-46c0-854a-9eb30822acb2.json b/change/@fluentui-react-virtualizer-31fd546c-91d0-46c0-854a-9eb30822acb2.json new file mode 100644 index 0000000000000..ae502d7ef1818 --- /dev/null +++ b/change/@fluentui-react-virtualizer-31fd546c-91d0-46c0-854a-9eb30822acb2.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Feat: Add imperative ref access to both virtualizer length and current index", + "packageName": "@fluentui/react-virtualizer", + "email": "mifraser@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-virtualizer/etc/react-virtualizer.api.md b/packages/react-components/react-virtualizer/etc/react-virtualizer.api.md index abbcc63f6a8e2..68bdb8fb6eac0 100644 --- a/packages/react-components/react-virtualizer/etc/react-virtualizer.api.md +++ b/packages/react-components/react-virtualizer/etc/react-virtualizer.api.md @@ -33,6 +33,8 @@ export interface ResizeCallbackWithRef { // @public (undocumented) export type ScrollToInterface = { scrollTo: (index: number, behavior?: ScrollBehavior, callback?: (index: number) => void) => void; + virtualizerLength: RefObject; + currentIndex: RefObject | undefined; }; // @public (undocumented) @@ -133,6 +135,7 @@ export type VirtualizerDataRef = { progressiveSizes: RefObject; nodeSizes: RefObject; setFlaggedIndex: (index: number | null) => void; + currentIndex: RefObject; }; // @public (undocumented) diff --git a/packages/react-components/react-virtualizer/src/components/Virtualizer/Virtualizer.types.ts b/packages/react-components/react-virtualizer/src/components/Virtualizer/Virtualizer.types.ts index 35c9ee94d0ce5..ea08e3ef27589 100644 --- a/packages/react-components/react-virtualizer/src/components/Virtualizer/Virtualizer.types.ts +++ b/packages/react-components/react-virtualizer/src/components/Virtualizer/Virtualizer.types.ts @@ -69,6 +69,7 @@ export type VirtualizerDataRef = { progressiveSizes: RefObject; nodeSizes: RefObject; setFlaggedIndex: (index: number | null) => void; + currentIndex: RefObject; }; export type VirtualizerConfigProps = { diff --git a/packages/react-components/react-virtualizer/src/components/Virtualizer/useVirtualizer.ts b/packages/react-components/react-virtualizer/src/components/Virtualizer/useVirtualizer.ts index c624eca75bd47..14084735438f6 100644 --- a/packages/react-components/react-virtualizer/src/components/Virtualizer/useVirtualizer.ts +++ b/packages/react-components/react-virtualizer/src/components/Virtualizer/useVirtualizer.ts @@ -28,6 +28,11 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta /* The context is optional, it's useful for injecting additional index logic, or performing uniform state updates*/ const _virtualizerContext = useVirtualizerContextState_unstable(virtualizerContext); + // We use this ref as a constant source to access the virtualizer's state imperatively + const actualIndexRef = useRef(_virtualizerContext.contextIndex); + if (actualIndexRef.current !== _virtualizerContext.contextIndex) { + actualIndexRef.current = _virtualizerContext.contextIndex; + } const flaggedIndex = useRef(null); const actualIndex = _virtualizerContext.contextIndex; @@ -113,6 +118,11 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta // Local updates updateChildRows(index); updateCurrentItemSizes(index); + + // Set before 'setActualIndex' call + // If it changes before render, or injected via context, re-render will update ref. + actualIndexRef.current = index; + // State setters setActualIndex(index); }; @@ -412,6 +422,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta progressiveSizes: childProgressiveSizes, nodeSizes: childSizes, setFlaggedIndex: (index: number | null) => (flaggedIndex.current = index), + currentIndex: actualIndexRef, }; }, [childProgressiveSizes, childSizes], diff --git a/packages/react-components/react-virtualizer/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts b/packages/react-components/react-virtualizer/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts index 9cfdd9629da76..4caccd8dcb608 100644 --- a/packages/react-components/react-virtualizer/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts +++ b/packages/react-components/react-virtualizer/src/components/VirtualizerScrollView/useVirtualizerScrollView.ts @@ -14,6 +14,11 @@ export function useVirtualizerScrollView_unstable(props: VirtualizerScrollViewPr direction: props.axis ?? 'vertical', }); + // Store the virtualizer length as a ref for imperative ref access + const virtualizerLengthRef = React.useRef(virtualizerLength); + if (virtualizerLengthRef.current !== virtualizerLength) { + virtualizerLengthRef.current = virtualizerLength; + } const scrollViewRef = useMergedRefs(React.useRef(null), scrollRef) as React.RefObject; const imperativeVirtualizerRef = React.useRef(null); const scrollCallbackRef = React.useRef void)>(null); @@ -35,6 +40,8 @@ export function useVirtualizerScrollView_unstable(props: VirtualizerScrollViewPr behavior, }); }, + currentIndex: imperativeVirtualizerRef.current?.currentIndex, + virtualizerLength: virtualizerLengthRef, }; }, [axis, scrollViewRef, itemSize, numItems, reversed], diff --git a/packages/react-components/react-virtualizer/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.ts b/packages/react-components/react-virtualizer/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.ts index f4498755f4e5f..63f6b7c7698d8 100644 --- a/packages/react-components/react-virtualizer/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.ts +++ b/packages/react-components/react-virtualizer/src/components/VirtualizerScrollViewDynamic/useVirtualizerScrollViewDynamic.ts @@ -24,6 +24,11 @@ export function useVirtualizerScrollViewDynamic_unstable( numItems: props.numItems, }); + // Store the virtualizer length as a ref for imperative ref access + const virtualizerLengthRef = React.useRef(virtualizerLength); + if (virtualizerLengthRef.current !== virtualizerLength) { + virtualizerLengthRef.current = virtualizerLength; + } const scrollViewRef = useMergedRefs(React.useRef(null), scrollRef) as React.RefObject; const scrollCallbackRef = React.useRef void)>(null); @@ -54,6 +59,8 @@ export function useVirtualizerScrollViewDynamic_unstable( }); } }, + currentIndex: _imperativeVirtualizerRef.current?.currentIndex, + virtualizerLength: virtualizerLengthRef, }; }, [axis, scrollViewRef, reversed, _imperativeVirtualizerRef], diff --git a/packages/react-components/react-virtualizer/src/utilities/ImperativeScrolling/imperativeScrolling.types.ts b/packages/react-components/react-virtualizer/src/utilities/ImperativeScrolling/imperativeScrolling.types.ts index cfb151d020131..28716641fa3f2 100644 --- a/packages/react-components/react-virtualizer/src/utilities/ImperativeScrolling/imperativeScrolling.types.ts +++ b/packages/react-components/react-virtualizer/src/utilities/ImperativeScrolling/imperativeScrolling.types.ts @@ -22,4 +22,6 @@ export type ScrollToItemDynamicParams = { export type ScrollToInterface = { scrollTo: (index: number, behavior?: ScrollBehavior, callback?: (index: number) => void) => void; + virtualizerLength: RefObject; + currentIndex: RefObject | undefined; };