달력을 쉽게 구현하기 위한 react 라이브러리이다.
여러가지 기능들을 제공하기 때문에 상황에 맞게 커스텀해서 사용 할 수 있다.
커스텀에 제공하는 기능들은 공식문서를 참고하자.
(공식문서 : https://reactdatepicker.com/)
date picker는 기본적으로 date기반으로 동작한다.
selectsRange 라는 boolean 속성값으로 날짜 범위를 선택할 지, 날짜 하루를 선택 할 지 결정할 수 있다.
여기서 selectsRange에 따라 onChange라는 이벤트 핸들러가 작동하게 된다.
사용시 registerLocale함수를 사용해서 한글적용이 가능하다.
() => {
const [dateRange, setDateRange] = useState([null, null]);
const [startDate, endDate] = dateRange;
registerLocale("ko", ko);
return (
<DatePicker
selectsRange={true}
startDate={startDate}
endDate={endDate}
onChange={(update) => {
setDateRange(update);
}}
/>
);
};
export interface ReactDatePickerProps<
CustomModifierNames extends string = never,
WithRange extends boolean | undefined = undefined,
> {
adjustDateOnChange?: boolean | undefined;
allowSameDay?: boolean | undefined;
ariaDescribedBy?: string | undefined;
ariaInvalid?: string | undefined;
ariaLabelClose?: string | undefined;
ariaLabelledBy?: string | undefined;
ariaRequired?: string | undefined;
autoComplete?: string | undefined;
autoFocus?: boolean | undefined;
calendarClassName?: string | undefined;
calendarContainer?(props: CalendarContainerProps): React.ReactNode;
calendarStartDay?: number | undefined;
children?: React.ReactNode | undefined;
chooseDayAriaLabelPrefix?: string | undefined;
className?: string | undefined;
clearButtonClassName?: string | undefined;
clearButtonTitle?: string | undefined;
closeOnScroll?: boolean | ((e: Event) => boolean) | undefined;
customInput?: React.ReactNode | undefined;
customInputRef?: string | undefined;
customTimeInput?: React.ReactNode | undefined;
dateFormat?: string | string[] | undefined;
dateFormatCalendar?: string | undefined;
dayClassName?(date: Date): string | null;
weekDayClassName?(date: Date): string | null;
monthClassName?(date: Date): string | null;
timeClassName?(date: Date): string | null;
disabledDayAriaLabelPrefix?: string | undefined;
disabled?: boolean | undefined;
disabledKeyboardNavigation?: boolean | undefined;
dropdownMode?: 'scroll' | 'select' | undefined;
endDate?: Date | null | undefined;
excludeDates?: Date[] | undefined;
excludeDateIntervals?: Array<{ start: Date; end: Date }> | undefined;
excludeTimes?: Date[] | undefined;
filterDate?(date: Date): boolean;
filterTime?(date: Date): boolean;
fixedHeight?: boolean | undefined;
forceShowMonthNavigation?: boolean | undefined;
formatWeekDay?(day: string): React.ReactNode;
formatWeekNumber?(date: Date): string | number;
highlightDates?: Array<HighlightDates | Date> | undefined;
id?: string | undefined;
includeDates?: Date[] | undefined;
includeDateIntervals?: Array<{ start: Date; end: Date }> | undefined;
includeTimes?: Date[] | undefined;
injectTimes?: Date[] | undefined;
inline?: boolean | undefined;
focusSelectedMonth?: boolean | undefined;
isClearable?: boolean | undefined;
locale?: string | Locale | undefined;
maxDate?: Date | null | undefined;
maxTime?: Date | undefined;
minDate?: Date | null | undefined;
minTime?: Date | undefined;
monthsShown?: number | undefined;
name?: string | undefined;
nextMonthAriaLabel?: string | undefined;
nextMonthButtonLabel?: string | React.ReactNode | undefined;
nextYearAriaLabel?: string | undefined;
nextYearButtonLabel?: string | React.ReactNode | undefined;
onBlur?(event: React.FocusEvent<HTMLInputElement>): void;
onCalendarClose?(): void;
onCalendarOpen?(): void;
onChange(
date: WithRange extends false | undefined ? Date | null : [Date | null, Date | null],
event: React.SyntheticEvent<any> | undefined,
): void; // required!!!
onChangeRaw?(event: React.FocusEvent<HTMLInputElement>): void;
onClickOutside?(event: React.MouseEvent<HTMLDivElement>): void;
onDayMouseEnter?: ((date: Date) => void) | undefined;
onFocus?(event: React.FocusEvent<HTMLInputElement>): void;
onInputClick?(): void;
onInputError?(err: { code: number; msg: string }): void;
onKeyDown?(event: React.KeyboardEvent<HTMLDivElement>): void;
onMonthChange?(date: Date): void;
onMonthMouseLeave?: (() => void) | undefined;
onSelect?(date: Date, event: React.SyntheticEvent<any> | undefined): void;
onWeekSelect?(
firstDayOfWeek: Date,
weekNumber: string | number,
event: React.SyntheticEvent<any> | undefined,
): void;
onYearChange?(date: Date): void;
open?: boolean | undefined;
openToDate?: Date | undefined;
peekNextMonth?: boolean | undefined;
placeholderText?: string | undefined;
popperClassName?: string | undefined;
popperContainer?(props: { children: React.ReactNode[] }): React.ReactNode;
popperModifiers?: ReadonlyArray<Modifier<StrictModifierNames | CustomModifierNames>> | undefined;
popperPlacement?: Popper.Placement | undefined;
popperProps?: {} | undefined;
preventOpenOnFocus?: boolean | undefined;
previousMonthAriaLabel?: string | undefined;
previousMonthButtonLabel?: string | React.ReactNode | undefined;
previousYearAriaLabel?: string | undefined;
previousYearButtonLabel?: string | React.ReactNode | undefined;
readOnly?: boolean | undefined;
renderCustomHeader?(params: ReactDatePickerCustomHeaderProps): React.ReactNode;
renderDayContents?(dayOfMonth: number, date?: Date): React.ReactNode;
renderMonthContent?(monthIndex: number, shortMonthText: string, fullMonthText: string): React.ReactNode;
required?: boolean | undefined;
scrollableMonthYearDropdown?: boolean | undefined;
scrollableYearDropdown?: boolean | undefined;
selected?: Date | null | undefined;
selectsEnd?: boolean | undefined;
selectsStart?: boolean | undefined;
selectsRange?: WithRange;
shouldCloseOnSelect?: boolean | undefined;
showDisabledMonthNavigation?: boolean | undefined;
showFullMonthYearPicker?: boolean | undefined;
showMonthDropdown?: boolean | undefined;
showMonthYearDropdown?: boolean | undefined;
showMonthYearPicker?: boolean | undefined;
showPopperArrow?: boolean | undefined;
showPreviousMonths?: boolean | undefined;
showQuarterYearPicker?: boolean | undefined;
showTimeInput?: boolean | undefined;
showTimeSelect?: boolean | undefined;
showTimeSelectOnly?: boolean | undefined;
showTwoColumnMonthYearPicker?: boolean | undefined;
showFourColumnMonthYearPicker?: boolean | undefined;
showWeekNumbers?: boolean | undefined;
showYearDropdown?: boolean | undefined;
showYearPicker?: boolean | undefined;
showIcon?: boolean | undefined;
startDate?: Date | null | undefined;
startOpen?: boolean | undefined;
strictParsing?: boolean | undefined;
tabIndex?: number | undefined;
timeCaption?: string | undefined;
timeFormat?: string | undefined;
timeInputLabel?: string | undefined;
timeIntervals?: number | undefined;
title?: string | undefined;
todayButton?: React.ReactNode | undefined;
useShortMonthInDropdown?: boolean | undefined;
useWeekdaysShort?: boolean | undefined;
weekAriaLabelPrefix?: string | undefined;
monthAriaLabelPrefix?: string | undefined;
value?: string | undefined;
weekLabel?: string | undefined;
withPortal?: boolean | undefined;
portalId?: string | undefined;
portalHost?: ShadowRoot | undefined;
wrapperClassName?: string | undefined;
yearDropdownItemNumber?: number | undefined;
excludeScrollbar?: boolean | undefined;
enableTabLoop?: boolean | undefined;
yearItemNumber?: number | undefined;
}
ReactDatePickerProps 중 유일하게 optional 이 아닌 prop이다, 즉 사용하는 쪽에서 필수적으로 전달해줘야한다.
onChange 이벤트핸들러는 두가지 parmas를 받는다. date와 event인데,
여기서 withRange 값에 따라 Date 객체 하나를 받을수도, [Date|null, Date|null] 배열을 받을 수도 있다. => 이벤트핸들러에서 startDate, endDate의 값을 변경해줘야한다.
withRange 값을 변경하는 것은 selectsRange 라는 prop이다.
onChange(
date: WithRange extends false | undefined ? Date | null : [Date | null, Date | null],
event: React.SyntheticEvent<any> | undefined,
): void; // required!!!
onCalendarOpen은 date-picker가 열렸을 때 호출되는 이벤트 핸들러이고, onCalendarClose은 date-picker가 닫혔을 때 호출되는 이벤트 핸들러이다.
여러 컴포넌트에서 사용하기 때문에, onCalendarOpen 이벤트핸들러를 통해 캘린더가 현재 열려있는지의 여부를 나타내는 상태값을 관리해아한다.
onCalendarClose은 캘린더가 닫혔을 때 일어날 작업들을 정의한다. => 캘린더 값에 대한 기본값 설정이라던가 date로 표시되는 input값에 대한 세부 제어가 가능하다.
shouldCloseOnSelect : 달력에서 날짜를 선택했을 때 달력이 바로 닫히는가를 나타내는 속성이다.
선택이후 footer나 옆에 sidebar에서 다른 작업을 해야 할 때 추가적으로 제어를 해줘야하는 속성이다.
dateFormat : 지정된 날짜형식대로 input에 표시하는 속성이다.
showMonthYearPicker : 날짜말고 월을 선택 할 수 있도록 해주는 캘린더를 표시하는 속성이다.
Input값을 custom 할 수 있게 해준다.
ImaskInput과 함께 사용해서 정보가 표시되는 형식을 제어 할 수도 있다.
() => {
const [startDate, setStartDate] = useState(new Date());
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<button className="example-custom-input" onClick={onClick} ref={ref}>
{value}
</button>
));
return (
<DatePicker
selected={startDate}
onChange={(date) => setStartDate(date)}
customInput={<ExampleCustomInput />}
/>
);
};
forwardRef란 (https://ko.legacy.reactjs.org/docs/forwarding-refs.html)
React에서 ref는 기본적으로 HTML 엘리먼트를 참조하기 위한 props이기에, 컴포넌트 props로는 기본적으로 사용이 불가하다.
key와 마찬가지로 ref는 React에서 다르게 처리합니다. HOC에 ref를 추가하면 ref는 래핑 된 컴포넌트가 아니라 가장 바깥쪽 컨테이너 컴포넌트를 참조합니다.
React 컴포넌트를 forwardRef() 메서드로 감싸면 컴포넌트 함수는 추가적으로 두 번째 매개변수로 ref를 받을 수 있다.
부모에서 ref값으로 자식 컴포넌트를 제어하려고 사용한다
import { useRef, forwardRef } from "react";
const Input = forwardRef((props, ref) => {
return <input type="text" ref={ref} />;
});
const Field = () => {
const inputRef = useRef(null);
const focusHandler = () => {
inputRef.current.foucus();
};
return(
<>
<Input ref={inputRef} />
<button onClick={focusHandler}>focus</button>
</>
);
};
달력의 Header나 Footer를 custom 할 수있게 해주는 속성들이다.
showTimeInput 과 timeInputLabel 속성도 customTimeInput을 만들 때 같이 제어하는 속성들이다.