Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor collection internals in preparation for public API #6608

Merged
merged 2 commits into from
Jun 26, 2024

Conversation

devongovett
Copy link
Member

@devongovett devongovett commented Jun 24, 2024

In preparation for exposing RAC's collection implementation publicly, this refactors the internals with a safer and less error-prone API that exposes fewer implementation details. It exposes a CollectionBuilder component (name tbd??) that takes care of rendering the collection in a hidden tree and exposing the built collection.

<CollectionBuilder content={children}>
  {collection => /* render stuff here */}
</CollectionBuilder>

This ensures that the order is always correct (the portal must be before the rendered content for SSR), and avoids exposing things like the internal document and other implementation details.

The content prop can be any React children, either direct collection elements (e.g. MenuItem) or other elements (in the case of Select and ComboBox). A <Collection> rendered inside starts a collection root and registers items with the collection owned by CollectionBuilder. Many collection components accept collection children and the items prop directly as a shortcut, so these render content={<Collection {...props} />}.

This will move to a separate package (out of RAC) so it can be used by hook users next. Wanted to do this refactor first so it's easier to review.

focusMode?: 'row' | 'cell'
focusMode?: 'row' | 'cell',
/** @private - do not use unless you know what you're doing. */
UNSAFE_selectionState?: MultipleSelectionState
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't love this, but we need to avoid a circular dependency in RAC Table. We need the selection state in order to determine whether to render checkboxes based on the selectionBehavior etc (via useTableOptions), but we can't build the full TableState until we have the collection. This prop lets us split this up to call useMultipleSelectionState directly, and pass this state down into useTableState. It is unsafe because of the rules of hooks below, so this is not meant to be a public API. Happy to change if someone has a better idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense for useMultipleSelectionState to have this and reconcile the difference? then if we run into this issue in other places it's not just in useGridState?

Also, we could not call it conditionally below, it just sets up state and then if nothing interacts with it, it won't do anything. So should be harmless to call it unconditionally and only use the result if props.UNSAFE_selectionState isn't passed in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah maybe. It bothers me to do extra unnecessary work and throw it away lol

return (
<>
<Hidden>
<CollectionDocumentContext.Provider value={document}>
Copy link
Member Author

@devongovett devongovett Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are technically not required for collections that render collection children directly (like Menu), it's only needed for cases like Select and ComboBox that have intermediary elements. It shouldn't hurt anything but if there were performance issues with this we could theoretically optimize it (e.g. check if props.children is a <Collection> element).

@rspbot
Copy link

rspbot commented Jun 24, 2024

@rspbot
Copy link

rspbot commented Jun 24, 2024

## API Changes

unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any', access: 'private' }
unknown top level export { type: 'any', access: 'private' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'identifier', name: 'Column' }
unknown top level export { type: 'identifier', name: 'Column' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown type { type: 'link' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }
unknown top level export { type: 'any' }

Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks better. I like the look of it in our own components. Especially nice that it allowed us to get rid of some extraneous interfaces as well

Copy link
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, RAC collection components still seem to work as expected. Just one comment to clarify that I understand what is being exposed and why. We should discuss some names for CollectionBuilder haha (CollectionInitializer? Dunno, having a hard time not using builder in the name)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So just for book keeping, the extra Collection stuff we are planning on exposing from a new package is as follows:

  • CollectionBuilder (with a new name to differentiate it from the existing CollectionBuilder stuff from old collections)
  • createLeaf/BranchComponent (for creating collection elements)
  • CollectionRendererContext (to access various info from Virtualizer in your branch/leaf components)
  • DefaultCollectionRenderer (for CollectionRoot and CollectionBranch)

with those (and the already exposed Collection), hooks users can use new collections for their components

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that sounds right. Not sure what to do about BaseCollection which is used to subclass in TableCollection too.

@devongovett devongovett merged commit 7dd7b48 into main Jun 26, 2024
28 checks passed
@devongovett devongovett deleted the collection-builder branch June 26, 2024 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants