React Datepicker 사용기

sanha_OvO·2021년 12월 25일
2

React

목록 보기
9/13
post-thumbnail

노가다의 서막

airpnp 프로젝트 진행중 Nav.js 검색바와 호스트 상세 페이지에 사용되어질 Datepicker를 제작할 필요가 있었다.

물론 처음부터 다 만들 수도 있었지만 2주라는 짧은 시간 내에 지도 API와 등록페이지까지 다 만들기는 시간이 부족하다는 판단하에, 기존의 라이브러리를 끌어와서 개발하기로 했다.

설치

npm i react-datepicker --save

// yarn 사용시
yarn add react-datepicker

공식문서

스타일

위의 모습이 라이브러리 본래의 모습이다.

그대로 적용하는 것보단, 전체 페이지의 테마에 맞게 스타일을 변경하는게 우선이라고 생각하였다.

우선 라이브러리 내의 datepicker.scss(와 연결되어있는 기타 등등...)을 끌어와서 import해주었다.

문제는 공식문서에서 스타일시트에 대한 정보를 따로 제공하지 않았다.

파일 한줄 한줄 살펴보며 어떤 속성이 어떻게 작용하는지 확인하는데만 하루를 투자하였다.
(스타일만 따지면 그냥 처음부터 만드는게 나을 수도...)

그 결과....

나름 괜찮은 결과물이 나온 것 같다!

기능 구현

라이브러리에서 기본적으로 지원하는 기능들이 강력해서 라이브러리 내부적으로 손댈 것은 별로 없었다.

다만 내가 구현을 꼭 해야된다고 생각했던 것은 시작 날짜가 종료 날짜보다 뒤에 선택되었을 때 종료 날짜도 시작 날짜와 같은 날로 자동적으로 선택되는 것과 전체적인 스타일에 맞게 커스텀 인풋을 적용하는 것이었다.

DayPicker.js

import React, { useState, useEffect } from 'react';
import DatePicker from 'react-datepicker';
import CustomInput from './CustomInput/CustomInput';
import { ko } from 'date-fns/esm/locale';
import './datepicker.scss';
import { subDays } from 'date-fns';

const DayPicker = ({ type, dateInput, adjustDate, bookingDate }) => {
  const [bookedData, setBookedData] = useState([]);

  useEffect(() => {
    if (bookingDate) {
      const handdledData = bookingDate.map(({ start_date, end_date }) => ({
        start: subDays(new Date(start_date), 1),
        end: new Date(end_date),
      }));

      setBookedData(handdledData);
    }
  }, [bookingDate]);

// ...

  return (
    <DatePicker
      selected={type === 'start' ? dateInput.startDate : dateInput.endDate}
      startDate={dateInput.startDate}
      endDate={dateInput.endDate}
      minDate={type === 'start' ? new Date() : dateInput.startDate}
      dateFormat={DATE_FORMAT}
      dateFormatCalendar={DATE_FORMAT_CALENDAR}
      onChange={date =>
        adjustDate(type === 'start' ? START_DATE_TYPE : END_DATE_TYPE, date)
      }
      excludeDateIntervals={bookedData}
      locale={ko}
      customInput={<CustomInput inputType={type} />}
    />
  );
};

export default DayPicker;

const START_DATE_TYPE = 'startDate';
const END_DATE_TYPE = 'endDate';
const DATE_FORMAT = 'yyyy년 MM월 dd일';
const DATE_FORMAT_CALENDAR = 'yyyy년 MM월';

예약 페이지 내에서 컴포넌트를 사용할 때를 위해 이미 예약된 날짜들을 선택하지 못하게 excludeDateIntervals를 설정해 주었다.

CustomInput.js

import React, { forwardRef } from 'react';
import styled from 'styled-components';

const CustomInput = forwardRef(({ value, onClick, inputType }, ref) => {
  return (
    <PannelButton onClick={onClick} ref={ref}>
      <ButtonName>{inputType === 'end' ? '끝' : '시작'}</ButtonName>
      <SelectedDate>{value}</SelectedDate>
    </PannelButton>
  );
});

export default CustomInput;

참고로 DatePicker라는 컴포넌트 안으로 들어가는 컴포넌트였기 때문에 추후 직접 접근하기 위해(또한 라이브러리 권고사항이었다) forwardRef로 감싸준 후 데이트피커의 props로 내려주었다.

ExtendedNavBar.js

import React, { useState, useRef, useEffect } from 'react';
import styled, { keyframes } from 'styled-components';
import { useNavigate } from 'react-router-dom';
import { BiSearch } from 'react-icons/bi';
import AutoCompleteInput from './AutoCompleteInput/AutoCompleteInput';
import DayPicker from '../../DayPicker/DayPicker';

const ExtendedSearchBar = ({ setIsSearchExtend }) => {
  const [searchInput, setSearchInput] = useState('');
  const [dateInput, setDateInput] = useState({
    startDate: new Date(),
    endDate: new Date(),
  });
  
  // ...
  
  const adjustDate = (type, date) => {
    if (type === 'startDate' && date > dateInput.endDate) {
      setDateInput({
        startDate: date,
        endDate: date,
      });
    } else {
      setDateInput(prevDate => ({ ...prevDate, [type]: date }));
    }
  };
  
// ...
  
};

export default ExtendedSearchBar;
// Styled-component를 위해 작성했던 스타일들 생략

선택된 날짜를 상태값에 담는 함수인 adjustDate()에서는 예약 종료일보다 시작일이 뒤에 선택될 때 종료일 또한 시작일과 같은 날로 선택되어 상태값에 저장할 수 있도록 하였다.


결과


이틀의 노가다 끝에 완성!

profile
Web Developer / Composer

0개의 댓글