[TreeView] Do not re-render every Tree Item when the Rich Tree View re-renders (introduce selectors)
Part of #14200
Work
Internal changes
- [x] Rename
itemMetaMap=>itemMetaLookupto make clear it is an object and not aMap(now that we do have map in the state) - [x] Rename
itemMap=>itemModelLookupto make clear it is an object and not aMap(now that we do have map in the state) and to be more precise about what it contains - [x] Rename
itemOrderedChildrenIds=>itemOrderedChildrenIdsLookupto make its purpose more clear - [x] Rename
itemChildrenIndexes=>itemChildrenIndexesLookupto make its purpose more clear - [x] Move
disabledItemsFocusableto the state instead of passing it to thecontextValue, it allows droppinginstance.isItemNavigablein favor of a selector - [x] Move the focus state in a
focuskey for consistency - [x] Move the label editing state in a
labelkey for consistency - [x] Store the
selectedItemsMapin the state to be usable in selectors - [x] Store the
expandedItemsMapin the state to be usable in selectors - [x] Store the
defaultFocusableItemIdin the state to be usable in selectors
Introduce selectors
-
[x] Replace the state with a store stored in a ref
// Access -const itemsReordering = state.itemsReordering; +const itemsReordering = store.value.itemsReordering; // without selectors (should not do it); +const itemsReordering = selectorItemsReordering(store.value); // with selectors // Updates -setState((prevState) => ({ ...prevState, itemsReordering: null })); +store.update((prevState) => ({ ...prevState, itemsReordering: null })); -
[x] Create selectors for every state access
The selectors are defined by each plugin in a
useTreeViewXXX.selectors.tsfile:// Each plugin has a root selector that returns its entire state. // This selector is not exported by the file const selectorTreeViewItemsState: TreeViewRootSelector<UseTreeViewItemsSignature> = (state) => state.items; // The plugin then defines selectors using `createSelectors` const selectorItemMetaLookup = createSelector( selectorTreeViewItemsState, (items) => items.itemMetaLookup, ); // Those selectors can also accept additional arguments const selectorItemOrderedChildrenIds = createSelector( [selectorTreeViewItemsState, (_, itemId: string | null) => itemId], (itemsState, itemId) => itemsState.itemOrderedChildrenIdsLookup[itemId ?? TREE_VIEW_ROOT_PARENT_ID] ?? EMPTY_CHILDREN, ); -
[x] Replace every
instancecall in the render with selectors// For selectors without additional parameters const itemMetaLookup = useSelector(store, selectorItemMetaLookup); // For selectors with additional parameters -const isExpanded = instance.isItemExpanded(itemId); +const isExpanded = useSelector(store, selectorIsItemExpanded, itemId); -
[x] BREAKING: Add
idAttributetoTreeItemProviderProps(to stop getting it fromitemMeta, see https://github.com/mui/mui-x/pull/14210#discussion_r1815198146 for context)
Add memoization
- [x] BREAKING: Add
React.memoaround theWrappedTreeItemcomponent inRichTreeViewItems - [x] Make sure every Tree Item do not re-render every time the Tree View re-renders (even when it has children)
- [x] Add tests
Docs
- [x] Update the doc examples to not use
publicAPIin the render
Extracted PRs
- #14661
- #14665
- #14667
- #14749
Deploy preview: https://deploy-preview-14210--material-ui-x.netlify.app/
Updated pages:
- docs/data/migration/migration-tree-view-v7/migration-tree-view-v7.md
- docs/data/tree-view/rich-tree-view/headless/headless.md
- docs/data/tree-view/tree-item-customization/tree-item-customization.md
Generated by :no_entry_sign: dangerJS against fa353bdafdb00ecdaa979d032a3459a732fa0d5a
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
@noraleonte I'm opening for review mostly so we can start discussing the DX and the impact on the doc We are still missing lots of tests plus the migration guide.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.
This pull request has conflicts, please resolve those before we can evaluate the pull request.