@@ -262,6 +262,7 @@ export class ElementNode<T> extends BaseNode<T> {
262262 nodeType = 8 ; // COMMENT_NODE (we'd use ELEMENT_NODE but React DevTools will fail to get its dimensions)
263263 node : NodeValue < T > ;
264264 private _index : number = 0 ;
265+ private hasSetProps = false ;
265266
266267 constructor ( type : string , ownerDocument : Document < T , any > ) {
267268 super ( ownerDocument ) ;
@@ -297,21 +298,20 @@ export class ElementNode<T> extends BaseNode<T> {
297298 node . lastChildKey = this . lastChild ?. node . key ?? null ;
298299 }
299300
300- // Special property that React passes through as an object rather than a string via setAttribute.
301- // See below for details.
302- set multiple ( obj : any ) {
301+ setProps ( obj : any , rendered ?: ReactNode ) {
303302 let node = this . ownerDocument . getMutableNode ( this ) ;
304- let { rendered , value, textValue, id, ...props } = obj ;
303+ let { value, textValue, id, ...props } = obj ;
305304 node . props = props ;
306305 node . rendered = rendered ;
307306 node . value = value ;
308307 node . textValue = textValue || ( typeof rendered === 'string' ? rendered : '' ) || obj [ 'aria-label' ] || '' ;
309308 if ( id != null && id !== node . key ) {
310- if ( this . parentNode ) {
309+ if ( this . hasSetProps ) {
311310 throw new Error ( 'Cannot change the id of an item' ) ;
312311 }
313312 node . key = id ;
314313 }
314+ this . hasSetProps = true ;
315315 }
316316
317317 get style ( ) {
@@ -672,11 +672,13 @@ export function useCollection<T extends object, C extends BaseCollection<T>>(pro
672672}
673673
674674/** Renders a DOM element (e.g. separator or header) shallowly when inside a collection. */
675- export function useShallowRender < T extends Element > ( Element : string , props : React . HTMLAttributes < T > , ref : React . RefObject < T > ) : ReactElement | null {
675+ export function useShallowRender < T extends Element > ( Element : string , props : React . HTMLAttributes < T > , ref : React . Ref < T > ) : ReactElement | null {
676676 let isShallow = useContext ( ShallowRenderContext ) ;
677+ props = useMemo ( ( ) => ( { ...props , ref} ) , [ props , ref ] ) ;
678+ ref = useCollectionItemRef ( props , props . children ) ;
677679 if ( isShallow ) {
678680 // @ts -ignore
679- return < Element multiple = { { ... props , ref, rendered : props . children } } /> ;
681+ return < Element ref = { ref } /> ;
680682 }
681683
682684 return null ;
@@ -738,6 +740,13 @@ export interface ItemRenderProps {
738740 isDropTarget ?: boolean
739741}
740742
743+ export function useCollectionItemRef ( props : any , rendered ?: ReactNode ) {
744+ // Return a callback ref that sets the props object on the fake DOM node.
745+ return useCallback ( ( element ) => {
746+ element ?. setProps ( props , rendered ) ;
747+ } , [ props , rendered ] ) ;
748+ }
749+
741750export interface ItemProps < T = object > extends Omit < SharedItemProps < T > , 'children' > , RenderProps < ItemRenderProps > {
742751 /** The unique id of the item. */
743752 id ?: Key ,
@@ -746,13 +755,8 @@ export interface ItemProps<T = object> extends Omit<SharedItemProps<T>, 'childre
746755}
747756
748757export function Item < T extends object > ( props : ItemProps < T > ) : JSX . Element {
749- // HACK: the `multiple` prop is special in that React will pass it through as a property rather
750- // than converting to a string and using setAttribute. This allows our custom fake DOM to receive
751- // the props as an object. Once React supports custom elements, we can switch to that instead.
752- // https://github.com/facebook/react/issues/11347
753- // https://github.com/facebook/react/blob/82c64e1a49239158c0daa7f0d603d2ad2ee667a9/packages/react-dom/src/shared/DOMProperty.js#L386
754758 // @ts -ignore
755- return < item multiple = { { ... props , rendered : props . children } } /> ;
759+ return < item ref = { useCollectionItemRef ( props , props . children ) } /> ;
756760}
757761
758762export interface SectionProps < T > extends Omit < SharedSectionProps < T > , 'children' | 'title' > , DOMProps {
@@ -768,7 +772,7 @@ export function Section<T extends object>(props: SectionProps<T>): JSX.Element {
768772 let children = useCollectionChildren ( props ) ;
769773
770774 // @ts -ignore
771- return < section multiple = { { ... props , rendered : props . title } } > { children } </ section > ;
775+ return < section ref = { useCollectionItemRef ( props ) } > { children } </ section > ;
772776}
773777
774778export const CollectionContext = createContext < CachedChildrenOptions < unknown > | null > ( null ) ;
0 commit comments