Skip to content

[fields] Clean the useField hook (part 1) #16944

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

Merged
merged 14 commits into from
Mar 19, 2025
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
'use client';
import {
useField,
useFieldInternalPropsWithDefaults,
PickerRangeValue,
} from '@mui/x-date-pickers/internals';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { useField } from '@mui/x-date-pickers/internals';
import { UseSingleInputDateRangeFieldProps } from './SingleInputDateRangeField.types';
import { useDateRangeManager } from '../managers';

export const useSingleInputDateRangeField = <
TEnableAccessibleFieldDOMStructure extends boolean,
TAllProps extends UseSingleInputDateRangeFieldProps<TEnableAccessibleFieldDOMStructure>,
TProps extends UseSingleInputDateRangeFieldProps<TEnableAccessibleFieldDOMStructure>,
>(
props: TAllProps,
props: TProps,
) => {
const manager = useDateRangeManager(props);
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'date');
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
manager,
internalProps,
});

return useField<
PickerRangeValue,
TEnableAccessibleFieldDOMStructure,
typeof forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
getOpenPickerButtonAriaLabel: manager.internal_useOpenPickerButtonAriaLabel(),
});
return useField({ manager, props });
};
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
'use client';
import {
useField,
useFieldInternalPropsWithDefaults,
PickerRangeValue,
} from '@mui/x-date-pickers/internals';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { useField } from '@mui/x-date-pickers/internals';
import { UseSingleInputDateTimeRangeFieldProps } from './SingleInputDateTimeRangeField.types';
import { useDateTimeRangeManager } from '../managers';

export const useSingleInputDateTimeRangeField = <
TEnableAccessibleFieldDOMStructure extends boolean,
TAllProps extends UseSingleInputDateTimeRangeFieldProps<TEnableAccessibleFieldDOMStructure>,
TProps extends UseSingleInputDateTimeRangeFieldProps<TEnableAccessibleFieldDOMStructure>,
>(
props: TAllProps,
props: TProps,
) => {
const manager = useDateTimeRangeManager(props);
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'date-time');
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
manager,
internalProps,
});

return useField<
PickerRangeValue,
TEnableAccessibleFieldDOMStructure,
typeof forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
getOpenPickerButtonAriaLabel: manager.internal_useOpenPickerButtonAriaLabel(),
});
return useField({ manager, props });
};
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
'use client';
import {
useField,
useFieldInternalPropsWithDefaults,
PickerRangeValue,
} from '@mui/x-date-pickers/internals';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { useField } from '@mui/x-date-pickers/internals';
import { UseSingleInputTimeRangeFieldProps } from './SingleInputTimeRangeField.types';
import { useTimeRangeManager } from '../managers';

export const useSingleInputTimeRangeField = <
TEnableAccessibleFieldDOMStructure extends boolean,
TAllProps extends UseSingleInputTimeRangeFieldProps<TEnableAccessibleFieldDOMStructure>,
TProps extends UseSingleInputTimeRangeFieldProps<TEnableAccessibleFieldDOMStructure>,
>(
props: TAllProps,
props: TProps,
) => {
const manager = useTimeRangeManager(props);
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'time');
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
manager,
internalProps,
});

return useField<
PickerRangeValue,
TEnableAccessibleFieldDOMStructure,
typeof forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
getOpenPickerButtonAriaLabel: manager.internal_useOpenPickerButtonAriaLabel(),
});
return useField({ manager, props });
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
PickerManagerFieldInternalProps,
useControlledValue,
useFieldInternalPropsWithDefaults,
UseFieldResponse,
UseFieldReturnValue,
} from '@mui/x-date-pickers/internals';
import { useValidation } from '@mui/x-date-pickers/validation';
import { UseTextFieldBaseForwardedProps, useTextFieldProps } from './useTextFieldProps';
Expand Down Expand Up @@ -187,7 +187,7 @@ export type UseMultiInputRangeFieldTextFieldProps<
TEnableAccessibleFieldDOMStructure extends boolean,
TForwardedProps extends UseTextFieldBaseForwardedProps,
> = Omit<
UseFieldResponse<
UseFieldReturnValue<
TEnableAccessibleFieldDOMStructure,
TForwardedProps & {
onKeyDown: React.KeyboardEventHandler;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import useEventCallback from '@mui/utils/useEventCallback';
import { useDateManager, useDateTimeManager, useTimeManager } from '@mui/x-date-pickers/managers';
import { useSplitFieldProps } from '@mui/x-date-pickers/hooks';
import { UseValidationReturnValue } from '@mui/x-date-pickers/validation';
import { PickerValueType } from '@mui/x-date-pickers/models';
import {
Expand All @@ -15,8 +14,7 @@ import {
RangePosition,
useField,
UseFieldInternalProps,
useFieldInternalPropsWithDefaults,
UseFieldResponse,
UseFieldReturnValue,
useNullableFieldPrivateContext,
useNullablePickerContext,
usePickerPrivateContext,
Expand Down Expand Up @@ -152,28 +150,11 @@ export function useTextFieldProps<
onChange: handleChange,
};

const splittedProps = useSplitFieldProps(allProps, valueType);
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
const { clearable, onClear, openPickerAriaLabel, ...fieldResponse } = useField({
manager,
internalProps: splittedProps.internalProps,
props: allProps,
skipContextFieldRefAssignment: rangePosition !== position,
});

const { clearable, onClear, openPickerAriaLabel, ...fieldResponse } = useField<
PickerValue,
TEnableAccessibleFieldDOMStructure,
typeof splittedProps.forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps: splittedProps.forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
// TODO v8: Add a real aria label before moving the opening logic to the field on range pickers.
getOpenPickerButtonAriaLabel: () => '',
}) as UseFieldResponse<TEnableAccessibleFieldDOMStructure, typeof allProps>;
}) as unknown as UseFieldReturnValue<TEnableAccessibleFieldDOMStructure, typeof allProps>;

React.useEffect(() => {
if (!pickerContext?.open || pickerContext?.variant === 'mobile') {
Expand Down
11 changes: 4 additions & 7 deletions packages/x-date-pickers-pro/src/managers/useDateRangeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,13 @@ export function useDateRangeManager<TEnableAccessibleFieldDOMStructure extends b
);
}

function useOpenPickerButtonAriaLabel() {
function useOpenPickerButtonAriaLabel(value: PickerRangeValue) {
const utils = useUtils();
const translations = usePickerTranslations();

return React.useCallback(
(value: PickerRangeValue) => {
return translations.openRangePickerDialogue(formatRange(utils, value, 'fullDate'));
},
[translations, utils],
);
return React.useMemo(() => {
return translations.openRangePickerDialogue(formatRange(utils, value, 'fullDate'));
}, [value, translations, utils]);
}

export interface UseDateRangeManagerParameters<TEnableAccessibleFieldDOMStructure extends boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,13 @@ export function useDateTimeRangeManager<TEnableAccessibleFieldDOMStructure exten
);
}

function useOpenPickerButtonAriaLabel() {
function useOpenPickerButtonAriaLabel(value: PickerRangeValue) {
const utils = useUtils();
const translations = usePickerTranslations();

return React.useCallback(
(value: PickerRangeValue) => {
return translations.openRangePickerDialogue(formatRange(utils, value, 'fullDate'));
},
[translations, utils],
);
return React.useMemo(() => {
return translations.openRangePickerDialogue(formatRange(utils, value, 'fullDate'));
}, [value, translations, utils]);
}

export interface UseDateTimeRangeManagerParameters<
Expand Down
15 changes: 6 additions & 9 deletions packages/x-date-pickers-pro/src/managers/useTimeRangeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,16 @@ export function useTimeRangeManager<TEnableAccessibleFieldDOMStructure extends b
}

function createUseOpenPickerButtonAriaLabel(ampm: boolean | undefined) {
return function useOpenPickerButtonAriaLabel() {
return function useOpenPickerButtonAriaLabel(value: PickerRangeValue) {
const utils = useUtils();
const translations = usePickerTranslations();

return React.useCallback(
(value: PickerRangeValue) => {
const formatKey =
(ampm ?? utils.is12HourCycleInCurrentLocale()) ? 'fullTime12h' : 'fullTime24h';
return React.useMemo(() => {
const formatKey =
(ampm ?? utils.is12HourCycleInCurrentLocale()) ? 'fullTime12h' : 'fullTime24h';

return translations.openRangePickerDialogue(formatRange(utils, value, formatKey));
},
[translations, utils],
);
return translations.openRangePickerDialogue(formatRange(utils, value, formatKey));
}, [value, translations, utils]);
};
}

Expand Down
29 changes: 4 additions & 25 deletions packages/x-date-pickers/src/DateField/useDateField.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
'use client';
import { useField, useFieldInternalPropsWithDefaults } from '../internals/hooks/useField';
import { useField } from '../internals/hooks/useField';
import { UseDateFieldProps } from './DateField.types';
import { useSplitFieldProps } from '../hooks';
import { useDateManager } from '../managers';
import { PickerValue } from '../internals/models';

export const useDateField = <
TEnableAccessibleFieldDOMStructure extends boolean,
TAllProps extends UseDateFieldProps<TEnableAccessibleFieldDOMStructure>,
TProps extends UseDateFieldProps<TEnableAccessibleFieldDOMStructure>,
>(
props: TAllProps,
props: TProps,
) => {
const manager = useDateManager(props);
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'date');
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
manager,
internalProps,
});

return useField<
PickerValue,
TEnableAccessibleFieldDOMStructure,
typeof forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
getOpenPickerButtonAriaLabel: manager.internal_useOpenPickerButtonAriaLabel(),
});
return useField({ manager, props });
};
29 changes: 4 additions & 25 deletions packages/x-date-pickers/src/DateTimeField/useDateTimeField.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
'use client';
import { useField, useFieldInternalPropsWithDefaults } from '../internals/hooks/useField';
import { useField } from '../internals/hooks/useField';
import { UseDateTimeFieldProps } from './DateTimeField.types';
import { useSplitFieldProps } from '../hooks';
import { useDateTimeManager } from '../managers';
import { PickerValue } from '../internals/models';

export const useDateTimeField = <
TEnableAccessibleFieldDOMStructure extends boolean,
TAllProps extends UseDateTimeFieldProps<TEnableAccessibleFieldDOMStructure>,
TProps extends UseDateTimeFieldProps<TEnableAccessibleFieldDOMStructure>,
>(
props: TAllProps,
props: TProps,
) => {
const manager = useDateTimeManager(props);
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'date-time');
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
manager,
internalProps,
});

return useField<
PickerValue,
TEnableAccessibleFieldDOMStructure,
typeof forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
getOpenPickerButtonAriaLabel: manager.internal_useOpenPickerButtonAriaLabel(),
});
return useField({ manager, props });
};
31 changes: 5 additions & 26 deletions packages/x-date-pickers/src/TimeField/useTimeField.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,14 @@
'use client';
import { useField, useFieldInternalPropsWithDefaults } from '../internals/hooks/useField';
import { UseTimeFieldProps } from './TimeField.types';
import { useSplitFieldProps } from '../hooks';
import { useField } from '../internals/hooks/useField';
import { useTimeManager } from '../managers';
import { PickerValue } from '../internals/models';
import { UseTimeFieldProps } from './TimeField.types';

export const useTimeField = <
TEnableAccessibleFieldDOMStructure extends boolean,
TAllProps extends UseTimeFieldProps<TEnableAccessibleFieldDOMStructure>,
TProps extends UseTimeFieldProps<TEnableAccessibleFieldDOMStructure>,
>(
props: TAllProps,
props: TProps,
) => {
const manager = useTimeManager(props);
const { forwardedProps, internalProps } = useSplitFieldProps(props, 'time');
const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
manager,
internalProps,
});

return useField<
PickerValue,
TEnableAccessibleFieldDOMStructure,
typeof forwardedProps,
typeof internalPropsWithDefaults
>({
forwardedProps,
internalProps: internalPropsWithDefaults,
valueManager: manager.internal_valueManager,
fieldValueManager: manager.internal_fieldValueManager,
validator: manager.validator,
valueType: manager.valueType,
getOpenPickerButtonAriaLabel: manager.internal_useOpenPickerButtonAriaLabel(),
});
return useField({ manager, props });
};
2 changes: 1 addition & 1 deletion packages/x-date-pickers/src/hooks/useSplitFieldProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const SHARED_FIELD_INTERNAL_PROP_NAMES = [
'focused',
] as const;

type InternalPropNames<TValueType extends PickerValueType> =
export type InternalPropNames<TValueType extends PickerValueType> =
| (typeof SHARED_FIELD_INTERNAL_PROP_NAMES)[number]
| (TValueType extends 'date' | 'date-time' ? (typeof DATE_VALIDATION_PROP_NAMES)[number] : never)
| (TValueType extends 'time' | 'date-time' ? (typeof TIME_VALIDATION_PROP_NAMES)[number] : never)
Expand Down
Loading