Skip to content

Commit

Permalink
[MenuItem][Base] Pass idGenerator function (#37364)
Browse files Browse the repository at this point in the history
  • Loading branch information
sai6855 authored May 29, 2023
1 parent 1bede6a commit f3f93a3
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 36 deletions.
6 changes: 5 additions & 1 deletion packages/mui-base/src/useMenuItem/useMenuItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import {
import { useListItem } from '../useList';
import { useCompoundItem } from '../utils/useCompoundItem';

function idGenerator(existingKeys: Set<string>) {
return `menu-item-${existingKeys.size}`;
}

/**
*
* Demos:
Expand Down Expand Up @@ -40,7 +44,7 @@ export default function useMenuItem(params: UseMenuItemParameters): UseMenuItemR
item: id,
});

const { index, totalItemCount } = useCompoundItem(id, itemMetadata);
const { index, totalItemCount } = useCompoundItem(id ?? idGenerator, itemMetadata);

const {
getRootProps: getButtonProps,
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/useTab/useTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function useTab(parameters: UseTabParameters): UseTabReturnValue {
id: value,
index,
totalItemCount: totalTabsCount,
} = useCompoundItem<string | number, TabMetadata>(valueParam, tabMetadata, tabValueGenerator);
} = useCompoundItem<string | number, TabMetadata>(valueParam ?? tabValueGenerator, tabMetadata);

const {
getRootProps: getTabProps,
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/useTabPanel/useTabPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function useTabPanel(parameters: UseTabPanelParameters): UseTabPanelReturnValue
const handleRef = useForkRef(ref, externalRef);
const metadata = React.useMemo(() => ({ id, ref }), [id]);

const { id: value } = useCompoundItem(valueParam, metadata, tabPanelValueGenerator);
const { id: value } = useCompoundItem(valueParam ?? tabPanelValueGenerator, metadata);

const hidden = value !== selectedTabValue;

Expand Down
3 changes: 1 addition & 2 deletions packages/mui-base/src/utils/useCompound.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,8 @@ describe('compound components', () => {
function Child() {
const ref = React.useRef<HTMLLIElement>(null);
const { id } = useCompoundItem<string, { ref: React.RefObject<HTMLLIElement> }>(
undefined,
React.useMemo(() => ({ ref }), []),
idGenerator,
React.useMemo(() => ({ ref }), []),
);

return <li ref={ref}>{id}</li>;
Expand Down
32 changes: 10 additions & 22 deletions packages/mui-base/src/utils/useCompound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,20 @@ interface RegisterItemReturnValue<Key> {
deregister: () => void;
}

export type KeyGenerator<Key> = (existingKeys: Set<Key>) => Key;

export type CompoundComponentContextValue<Key, Subitem> = {
/**
* Registers an item with the parent.
* This should be called during the effect phase of the child component.
* The `itemMetadata` should be a stable reference (e.g. a memoized object), to avoid unnecessary re-registrations.
*
* @param id Id of the item.
* @param itemMetadata Arbitrary metadata to pass to the parent component.
* @param missingKeyGenerator A function that generates a unique id for the item.
* @param id Id of the item or A function that generates a unique id for the item.
* It is called with the set of the ids of all the items that have already been registered.
* Return `existingKeys.size` if you want to use the index of the new item as the id.
* Return `existingKeys.size` if you want to use the index of the new item as the id..
* @param itemMetadata Arbitrary metadata to pass to the parent component.
*/
registerItem: (
id: Key | undefined,
item: Subitem,
missingKeyGenerator?: (existingKeys: Set<Key>) => Key,
) => RegisterItemReturnValue<Key>;
registerItem: (id: Key | KeyGenerator<Key>, item: Subitem) => RegisterItemReturnValue<Key>;
/**
* Returns the 0-based index of the item in the parent component's list of registered items.
*
Expand Down Expand Up @@ -116,20 +113,11 @@ export function useCompoundParent<
}, []);

const registerItem = React.useCallback(
function registerItem(
id: Key | undefined,
item: Subitem,
missingKeyGenerator?: (existingKeys: Set<Key>) => Key,
) {
function registerItem(id: Key | KeyGenerator<Key>, item: Subitem) {
let providedOrGeneratedId: Key;
if (id === undefined) {
if (missingKeyGenerator === undefined) {
throw new Error(
"The compound component's child doesn't have a key. You need to provide a missingKeyGenerator to generate it.",
);
}

providedOrGeneratedId = missingKeyGenerator(subitemKeys.current);

if (typeof id === 'function') {
providedOrGeneratedId = (id as KeyGenerator<Key>)(subitemKeys.current);
} else {
providedOrGeneratedId = id;
}
Expand Down
20 changes: 11 additions & 9 deletions packages/mui-base/src/utils/useCompoundItem.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react';
import { unstable_useEnhancedEffect as useEnhancedEffect } from '@mui/utils';
import { CompoundComponentContext, CompoundComponentContextValue } from './useCompound';
import {
CompoundComponentContext,
CompoundComponentContextValue,
KeyGenerator,
} from './useCompound';

export interface UseCompoundItemReturnValue<Key> {
/**
Expand Down Expand Up @@ -31,19 +35,17 @@ export interface UseCompoundItemReturnValue<Key> {
*
* @ignore - internal hook.
*/
export function useCompoundItem<Key, Subitem extends { ref: React.RefObject<Node> }>(
id: Key | undefined,
export function useCompoundItem<Key, Subitem>(
id: Key | KeyGenerator<Key>,
itemMetadata: Subitem,
missingKeyGenerator: (existingKeys: Set<Key>) => Key,
): UseCompoundItemReturnValue<Key>;
export function useCompoundItem<Key, Subitem>(
id: Key,
itemMetadata: Subitem,
): UseCompoundItemReturnValue<Key>;
export function useCompoundItem<Key, Subitem>(
id: Key | undefined,
id: Key | KeyGenerator<Key>,
itemMetadata: Subitem,
missingKeyGenerator?: (existingKeys: Set<Key>) => Key,
): UseCompoundItemReturnValue<Key> {
const context = React.useContext(CompoundComponentContext) as CompoundComponentContextValue<
Key,
Expand All @@ -55,13 +57,13 @@ export function useCompoundItem<Key, Subitem>(
}

const { registerItem } = context;
const [registeredId, setRegisteredId] = React.useState(id);
const [registeredId, setRegisteredId] = React.useState(typeof id === 'function' ? undefined : id);

useEnhancedEffect(() => {
const { id: returnedId, deregister } = registerItem(id, itemMetadata, missingKeyGenerator);
const { id: returnedId, deregister } = registerItem(id, itemMetadata);
setRegisteredId(returnedId);
return deregister;
}, [registerItem, itemMetadata, missingKeyGenerator, id]);
}, [registerItem, itemMetadata, id]);

return {
id: registeredId,
Expand Down

0 comments on commit f3f93a3

Please sign in to comment.