AntD Date picker + formik

4 weeks ago 26
ARTICLE AD BOX

enter image description here

I am trying to use an Ant Design DatePicker inside a form, which is rendered inside a modal.

The DatePicker works, but the calendar panel behaves very strangely: the numbers in the calendar grid look messy and change every time the user moves the mouse over the component, almost as if they were changing randomly.

There are no console errors, and the selected value does not change — only the visual layout of the calendar changes on hover (it looks like a kind of a race condition or looping or a state that is broken).

Here is my DatePicker component:

import useTranslation from '@qulture/core/services/hooks/use-translation'; import { onlyIconDatepickerFormat, smallYearDateFormat, } from '@qulture/core/services/locale/locale'; import { moment, Moment } from '@qulture/core/utils/qr-time-utils'; import { DatePicker as AntDatePicker } from 'antd'; import type { DatePickerProps } from 'antd/es/date-picker'; import React, { Fragment, useMemo, useState } from 'react'; import styled from 'styled-components'; import withSpaceProps from '../../../hocs/with-space'; import useDynamicOpenDirection from '../../../hooks/use-dynamic-open-direction'; import ButtonDanger from '../../button/button-danger/button-danger.component'; import Layout from '../../layout/layout.component'; import { INPUT_STYLE } from '../input-style-mapper'; import { IBaseInput } from '../input.interface'; import { CalendarInfoDivider, IDatePickerStyledProps, } from './date-picker.styles'; /* 🔧 FIX TIPOGRÁFICO DO POPUP (ANTD DATEPICKER) */ const DatePickerTypographyFix = styled.div` .ant-picker-dropdown { font-family: ${props => props.theme.fontFamily.default}; * { font-family: ${props => props.theme.fontFamily.default}; font-variant-numeric: tabular-nums; font-feature-settings: 'tnum'; box-sizing: border-box; } .ant-picker-cell-inner { line-height: 24px; } } `; export interface IDatePickerProps extends IBaseInput<string, (date: string | null) => void, () => void>, IDatePickerStyledProps { showInputIcon?: boolean; placeholder?: string; startFocused?: boolean; className?: string; customButtonContent?: React.ReactElement; smallYearFormat?: boolean; showClearButton?: boolean; onClose?: (date: string | null) => void; isOutsideRange?: (day: Moment) => boolean; appendToBody?: boolean; showClearDate?: boolean; openDirection?: 'up' | 'down'; anchorDirection?: 'left' | 'right'; block?: boolean; noBorder?: boolean; } const StyledDatePicker = styled(AntDatePicker)<{ $block?: boolean; $noBorder?: boolean; $error?: boolean; $onlyIcon?: boolean; $width?: string; }>` &&& { ${props => props.$block && 'width: 100%;'} ${props => props.$width && `width: ${props.$width};`} ${props => props.$noBorder && ` border: none !important; &:hover, &:focus { border: none !important; } `} ${props => props.$error && ` border-color: ${props.theme.colors.important} !important; &:hover, &:focus, &:focus-within { border-color: ${props.theme.colors.important} !important; } `} ${props => props.$onlyIcon && ` .ant-picker-input > input { display: none; } `} font-family: ${props => props.theme.fontFamily.default}; .ant-picker-input > input { font-family: ${props => props.theme.fontFamily.default}; } } `; const BaseDatePicker: React.FC<IDatePickerProps> = ({ width, id, showInputIcon = true, placeholder, value, onChange, startFocused, onBlur, isOutsideRange, appendToBody, disabled, className, onlyIcon, showClearDate, openDirection, anchorDirection, block, noBorder, customButtonContent, smallYearFormat, openFromText, textColor, inputStyle = INPUT_STYLE.base, showClearButton, onClose, error, }) => { const [focused, setFocus] = useState(startFocused); const { verticalDirection: dynamicOpenDirection, containerRef } = useDynamicOpenDirection(); const { t } = useTranslation(); const memoizedValue = useMemo(() => (value ? moment(value) : null), [value]); const calculatedOpenDirection = openDirection || dynamicOpenDirection; const handleDateChange: DatePickerProps['onChange'] = date => { const newDate = date ? moment(date).utc().toISOString() : null; onChange(newDate); }; const handleOpenChange = (open: boolean): void => { setFocus(open); if (!open) { onBlur?.(); onClose?.(value); } }; const handleRemove = (): void => { onChange(null); onClose?.(null); }; const disabledDate: DatePickerProps['disabledDate'] = current => { if (isOutsideRange && current) { return isOutsideRange(moment(current)); } return false; }; const onlyIconDateFormat = smallYearFormat ? moment(value).format(smallYearDateFormat()) : moment(value).format(onlyIconDatepickerFormat()); const renderExtraFooter = (): React.ReactElement => { return showClearButton ? ( <Fragment> <CalendarInfoDivider /> <Layout display="flex" justifyContent="flex-end" mb="px2"> <ButtonDanger my="px8" mx="px16" onClick={handleRemove} disabled={!value} > {t('shared:remove')} </ButtonDanger> </Layout> </Fragment> ) : null; }; console.log('memoizedValue', memoizedValue); return ( <DatePickerTypographyFix> <div ref={containerRef} className={className}> <StyledDatePicker id={id} value={memoizedValue} onChange={handleDateChange} onOpenChange={handleOpenChange} open={focused} disabled={disabled} placeholder={onlyIcon && value ? onlyIconDateFormat : placeholder} disabledDate={disabledDate} suffixIcon={showInputIcon ? customButtonContent : null} getPopupContainer={ appendToBody ? undefined : trigger => trigger.parentElement || document.body } placement={ calculatedOpenDirection === 'up' ? 'topLeft' : 'bottomLeft' } $block={block} $noBorder={noBorder} $error={error} $onlyIcon={onlyIcon} $width={width} format="DD/MM/YYYY" allowClear={showClearDate} autoFocus={startFocused} renderExtraFooter={renderExtraFooter} /> </div> </DatePickerTypographyFix> ); }; const DatePicker = withSpaceProps(BaseDatePicker); DatePicker.displayName = 'DatePicker'; export default DatePicker;

My form field component:

import React from 'react'; import withField from '../../../hocs/field/with-field'; import DatePicker, { IDatePickerProps, } from '../../input/datepicker/date-picker.component'; export interface IFieldDatePickerProps extends IDatePickerProps {} const BaseFieldDatePickerProps: React.FC<IFieldDatePickerProps> = ({ ...props }) => <DatePicker {...props} />; const FieldDatePicker = withField(BaseFieldDatePickerProps); export default FieldDatePicker;
Read Entire Article