(React-Library) 1. react-datepicker

김동우·2021년 8월 22일
1

wecode

목록 보기
30/32
post-thumbnail

잠깐! 시작하기 전에

이 글은 wecode에서 실제 공부하고, 이해한 내용들을 적는 글입니다. 글의 표현과는 달리 어쩌면 실무와는 전혀 상관이 없는 글일 수 있습니다.

또한 해당 글은 다양한 자료들과 작성자 지식이 합성된 글입니다. 따라서 포스팅이 틀린 정보이거나, 해당 개념에 대한 작성자의 이해가 부족할 수 있습니다.

설명하듯 적는게 습관이라 권위자 발톱만큼의 향기가 날 수 있으나, 엄연히 학생입니다. 따라서 하나의 참고자료로 활용하시길 바랍니다.

글의 내용과 다른 정보나 견해의 차이가 있을 수 있습니다.
이럴 때, 해당 부분을 언급하셔서 제가 더 공부할 수 있는 기회를 제공해주시면 감사할 것 같습니다.


서론

이번 2차 프로젝트에서는 캘린더를 사용할 일이 있었습니다.

그런데, react-datepicker의 경우 커스텀이 쉬운 편인지, 어려운 편인지는 모르겠으나, 꽤나 까다로웠다고 생각합니다.

예를 들어, '추가적인 정보를 달력에 기입하고 싶으면 어떤 props를 주면 될까?' 같은 부분에서 말이죠.

그런데 다행히 공식 홈페이지에 잘 정리된 내용들이 많지 않은가? 하는 생각도 있습니다.

어떻게 특정 날짜를 제외할까? 하는 생각에 대해서는 특히나 그렇습니다.

어쩌면 라이브러리를 쓰는 제가 시야가 좁아진 나머지 공식문서를 더 열심히 보지 않았기 때문에 그런 상황이 일어난 것은 아닐까 싶기도 합니다.

또 다른 사이트들은 react-datepicker를 사용하나? 하고 보면 아쉽게도 프레임워크를 사용하는지 react 사이트가 아닌 경우가 다분했기에 이번 제 커스텀을 자랑해볼까 하고 글을 씁니다.

필요한 기능은 대부분 들어있습니다.

위에서 언급한 두 기능(숫자 아래 특정 데이터 추가, 특정 일자 제외) 외에 내부 함수에서 어떻게 백엔드로 Date객체를 변환해서 보내는지도 올려보겠습니다.

Calendar.js

import React, { useState } from 'react';
import DatePicker from 'react-datepicker';
import { ko } from 'date-fns/locale';
import { CalendarWrapper } from './CalendarWrapper';
import { NextLink } from './NextLink';

import styled from 'styled-components';
import 'react-datepicker/dist/react-datepicker.css';
import { flexSet } from '../../styles/Mixins';

import { useFetch } from '../../hooks';
import { dateConversion } from './CalendarLogic';

export const Calendar = ({
  priceDisplay,
  excludeOn,
  setCalendarOff,
  stayLocation,
  linkButtonText,
  linkUrl,
}) => {
  const [dateRange, setDateRange] = useState([null, null]);
  const [startDate, endDate] = dateRange;
  const [bookInfo] = useFetch('http://localhost:3000/data/CALENDAR.json', {
    method: 'GET',
  });

  const excludeDay = bookInfo => {
    let arr = [];
    for (const date in bookInfo.result) {
      if (bookInfo.result[date].quantity === 0) {
        arr.push(new Date(date));
      }
    }
    return arr;
  };

  const renderDayContents = (day, date) => {
    const tooltipText = `Tooltip for date: ${date}`;
    return (
      <>
        <span title={tooltipText}>{date.getDate()}</span>
        {bookInfo.result ? (
          <span title={tooltipText}>
            {bookInfo.result[dateConversion(date)]
              ? '₩ ' +
                bookInfo.result[dateConversion(date)].price.toLocaleString()
              : null}
          </span>
        ) : null}
      </>
    );
  };

  let bookDisable = excludeDay(bookInfo);

  return (
    <CalendarWrapper>
      <Button onClick={setCalendarOff}>X</Button>
      <StyledDatePicker
        locale={ko}
        dateFormat="yyyy-mm-dd"
        selectsRange={true}
        startDate={startDate}
        endDate={endDate}
        onChange={date => {
          setDateRange(date);
        }}
        isClearable={true}
        monthsShown={2}
        renderDayContents={priceDisplay && renderDayContents}
        excludeDates={excludeOn && bookDisable}
        minDate={new Date()}
        showDisabledMonthNavigation
        inline
      />
      {linkUrl && linkButtonText && (
        <NextLink
          linkInfo={{ stayLocation, linkUrl, linkButtonText }}
          dateRange={dateRange}
        />
      )}
    </CalendarWrapper>
  );
};

괴랄한 props가 보이시나요... 좋은 아이디어 있으면 꼭 제보해주세요.

CalendarLogic.js

export function dateConversion(date) {
  let month = date.getMonth() + 1;
  let day = date.getDate();
  return `${date.getFullYear()}-${month < 10 ? `0` + month : month}-${
    day < 10 ? `0` + day : day
  }`;
}

export function calcDateRange(dateRange) {
  let stay = new Date();
  stay =
    (Date.parse(dateRange[1]) - Date.parse(dateRange[0])) /
      (1000 * 60 * 60 * 24) +
    1;

  let startDay =
    dateRange[0] &&
    `${dateRange[0].getFullYear()}-${dateRange[0].getMonth()}-${dateRange[0].getDate()}`;

  let endDay =
    dateRange[1] &&
    `${dateRange[1].getFullYear()}-${dateRange[1].getMonth()}-${dateRange[1].getDate()}`;

  return [startDay, endDay, stay];
}

이 로직은 어떻게 Date 객체로 변환해서 원하는 형태 (ex: yyyy-mm-dd)로 보내줄 것인지 conversion하는 로직과 두 Date 사이의 stay, 일수를 계산하는 로직입니다.

데일리 호텔 누가 낸 아이디어일까...!

CalendarWrapper(custom).js

import styled from 'styled-components';
import 'react-datepicker/dist/react-datepicker.css';
import { flexSet, fullScreen } from '../../styles/Mixins';

export const CalendarWrapper = styled.div`
  position: fixed;
  ${fullScreen}
  background-color: white;
  top: 0px;
  z-index: 99;

  > div {
    ${flexSet('center', 'center')}
    width: 100%;
    height: 100vh;

    .react-datepicker {
      ${flexSet('center', 'center', 'column')}
      width: 100%;
      max-width: 1180px;
      border: none;

      > button {
        display: none;
      }

      .react-datepicker__month-container {
        ${flexSet('center', 'center', 'column')}
        width: 100%;
        /* height: 30vh; */
        margin: 0px;

        &:last-child {
          .react-datepicker__header {
            .react-datepicker__day-names {
              display: none;
            }
          }
        }

        .react-datepicker__header {
          ${flexSet('center', 'center', 'column')}
          width: 100%;
          border: none;
          background-color: transparent;

          .react-datepicker__current-month {
            ${flexSet('center', 'center')}
            font-size: 25px;
            margin: 10px 0 30px 0;
          }

          .react-datepicker__day-names {
            ${flexSet('space-around', 'center', 'row')}
            width: 100%;
            padding-bottom: 20px;
            border-bottom: 1px solid lightgray;
            font-size: 20px;

            .react-datepicker__day-name {
              &:first-child {
                color: red;
              }

              &:last-child {
                color: red;
              }
            }
          }
        }

        .react-datepicker__month {
          ${flexSet('center', 'center', 'column')}
          width: 100%;

          .react-datepicker__week {
            ${flexSet('space-around', 'center', 'row')}
            width: 100%;
            font-size: 25px;

            .react-datepicker__day--keyboard-selected {
              background-color: #ff7675;
              color: black;
            }

            .react-datepicker__day--in-selecting-range {
              background-color: transparent;
              color: black;
            }

            .react-datepicker__day--in-range {
              background-color: #ff7675;
              opacity: 0.5;
            }

            .react-datepicker__day--selecting-range-start,
            .react-datepicker__day--range-start,
            .react-datepicker__day--range-end {
              background-color: #ff7675;
              color: black;
              opacity: 1;
            }

            .react-datepicker__day--outside-month {
              background-color: transparent;
            }

            .react-datepicker__day--today {
              border: 1px solid #ff7675;
            }

            .react-datepicker__day--disabled {
              background-color: white;
            }

            .react-datepicker__day {
              ${flexSet('center', 'center', 'column')}
              border-radius: 0px;
              width: 168.5px;
              height: 75px;
              margin: 5px 0;
            }

            .react-datepicker__day--weekend {
              color: red;
            }
          }
        }
      }
    }
  }
`;

이건 단순한 css 커스텀입니다.

+@ NavLink.js

이 코드는 사전에 말씀드리지만, 그냥 제가 지역 data를 받지 않으면 Calendar 내에서 Pagenation이 일어나지 않게 하기 위해 작성중인 로직입니다.

따라서, NavLink 파트는 본문의 Calendar에서 제거해도 무방합니다.

import React from 'react';
import { Link } from 'react-router-dom';

import styled from 'styled-components';

export const NextLink = ({ linkInfo, dateRange }) => {
  const { stayLocation, linkUrl, linkButtonText } = linkInfo;

  return (
    <StyledLink to={stayLocation ? linkUrl : ''}>
      <NextStepButton
        disabled={dateRange[1] !== null || stayLocation ? false : true}
        onClick={() => {
          console.log(dateRange);
        }}
      >
        {linkButtonText}
      </NextStepButton>
    </StyledLink>
  );
};

const StyledLink = styled(Link)`
  display: flex;
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translate(-50%, 0);
`;

const NextStepButton = styled.button`
  width: 70vw;
  height: 50px;
  border: none;
  border-radius: 5px;
  background-color: #ff7675;
  color: white;

  &:disabled {
    background-color: lightgray;
    color: black;
  }
`;

마치며

라이브러리 하나 쓰기 쉽지 않다.

이렇게 오늘 글의 총평을 내릴 수 있겠습니다.

직접 구현하는 것과 라이브러리 사용 사이에서 어떤 것이 효율적인지 때로는 고민하는 사람이 되어야겠습니다.

datepicker는 애매한 그 사이에 있지 않을까? 싶습니다.

그럼 다음 글로 뵙겠습니다. 감사합니다.

1개의 댓글

comment-user-thumbnail
2022년 5월 20일

회사에서 캘린더 추가하느라고 검색해서 들어와봤는데 와보니 동우님 블로그네요 ㅋㅋ 잘 계시죠?! ㅋㅋ 7월5일에 동창회때 뵈요

답글 달기