diff --git a/packages/components/src/slot-fill/bubbles-virtually/use-slot-fills.ts b/packages/components/src/slot-fill/bubbles-virtually/use-slot-fills.ts index c597d68a567835..4f16841ff02b51 100644 --- a/packages/components/src/slot-fill/bubbles-virtually/use-slot-fills.ts +++ b/packages/components/src/slot-fill/bubbles-virtually/use-slot-fills.ts @@ -1,8 +1,8 @@ /** * WordPress dependencies */ -import { useContext } from '@wordpress/element'; -import { useObservableValue } from '@wordpress/compose'; +import { useContext, useMemo, useSyncExternalStore } from '@wordpress/element'; +import type { ObservableMap } from '@wordpress/compose'; /** * Internal dependencies @@ -10,7 +10,33 @@ import { useObservableValue } from '@wordpress/compose'; import SlotFillContext from '../context'; import type { SlotKey } from '../types'; +function useObservableValueWithSelector< K, V, S >( + map: ObservableMap< K, V >, + name: K, + selector: ( v: V | undefined ) => S +) { + const subscribe = useMemo( + () => ( listener: () => void ) => map.subscribe( name, listener ), + [ map, name ] + ); + const getValue = () => selector( map.get( name ) ); + return useSyncExternalStore( subscribe, getValue, getValue ); +} + +function getLength< T >( array: T[] | undefined ) { + return array?.length; +} + export default function useSlotFills( name: SlotKey ) { const registry = useContext( SlotFillContext ); - return useObservableValue( registry.fills, name ); + const length = useObservableValueWithSelector( + registry.fills, + name, + getLength + ); + // callers expect an opaque array with length `length`, so create that array + const fills = useMemo( () => { + return length !== undefined ? Array.from( { length } ) : undefined; + }, [ length ] ); + return fills; }