React Datepicker Custom

이수빈·2023년 10월 12일
1

React

목록 보기
6/20
post-thumbnail

React Datepicker란?

  • 달력을 쉽게 구현하기 위한 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);
      }}
    />
  );
};
  • ReactDatePickerProps은 다음과 같다.
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;
}

onChange 이벤트 핸들러 Prop

  • 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, onCalendarClose

  • onCalendarOpen은 date-picker가 열렸을 때 호출되는 이벤트 핸들러이고, onCalendarClose은 date-picker가 닫혔을 때 호출되는 이벤트 핸들러이다.

  • 여러 컴포넌트에서 사용하기 때문에, onCalendarOpen 이벤트핸들러를 통해 캘린더가 현재 열려있는지의 여부를 나타내는 상태값을 관리해아한다.

  • onCalendarClose은 캘린더가 닫혔을 때 일어날 작업들을 정의한다. => 캘린더 값에 대한 기본값 설정이라던가 date로 표시되는 input값에 대한 세부 제어가 가능하다.

shouldCloseOnSelect, dateFormat, showMonthYearPicker

  • shouldCloseOnSelect : 달력에서 날짜를 선택했을 때 달력이 바로 닫히는가를 나타내는 속성이다.

  • 선택이후 footer나 옆에 sidebar에서 다른 작업을 해야 할 때 추가적으로 제어를 해줘야하는 속성이다.

  • dateFormat : 지정된 날짜형식대로 input에 표시하는 속성이다.

  • showMonthYearPicker : 날짜말고 월을 선택 할 수 있도록 해주는 캘린더를 표시하는 속성이다.

CustomInput

  • 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로 wrapping된 input 컴포넌트를 전달해주는 형태로 사용한다.

forwardRef란

  • 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값으로 자식 컴포넌트를 제어하려고 사용한다

  1. 자식 컴포넌트(Input)를 React의 forwardRef() 로 감싼다. 두 번째 인자인 ref를 참조하려고 하는 엘리먼트에 할당한다.
  2. 부모 컴포넌트(Field)에서 참조를 위한 useRef() 를 생성한다. (클래스형 컴포넌트의 경우엔 React.createRef())
  3. 참조를 위한 ref값(inputRef)을 자식 컴포넌트의 props로 할당해준다. 별도의 에러가 발생하지 않는다.
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> 
        </> 
    ); 
};

Custom Header, Custom Time Input


  • 달력의 Header나 Footer를 custom 할 수있게 해주는 속성들이다.

  • showTimeInput 과 timeInputLabel 속성도 customTimeInput을 만들 때 같이 제어하는 속성들이다.

profile
응애 나 애기 개발자

0개의 댓글