일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 프로그래머스 K_Digital Training
- 프로그래머스 프론트엔드 데브코스
- postcss
- Vue
- 리스트 렌더링
- KDT 프로그래머스
- vue 이벤트 수신
- 쌓임맥락
- 다른컴퓨터에서 git사용
- 고양이 사진 검색기
- Spacer
- SCSS import
- SCSS forward
- 이벤트 수식어
- 프로그래머스 데브코스
- SCSS extend
- 프로그래머스 데브코스 프론트엔드
- 리액트
- 폼 입력 바인딩
- nextjs사용법
- flex
- netlify redirect
- vuex map
- react next
- SCSS use
- vue mixin
- KDT 프로그래머스 데브코스 프론트엔드
- intersection opserver
- git 같은계정 다른 컴퓨터
- vue 지역 컴포넌트
- Today
- Total
혼자 적어보는 노트
[React] Todo List 기한 설정 기능 추가 해보기 본문
지난번 포스팅에서 라이브러리 없이 달력 만들기를 했었는데
해당 달력을 토대로, 작업하고 있는 todo list에 기한을 추가하는 기능을 넣어보기로 했다.
구현사항은
1. 기한 설정 버튼을 누르면 달력이 나타난다.
2. 달력에 있는 날짜 선택 시 해당 날짜가 데이터에 담기며, 표시된다.
달력에 표시되어있는 지난 달, 다음 달의 날짜 데이터를 누를 경우 해당 달에 맞는 데이터가 담긴다.
3. 달력이 아닌 다른 영역을 클릭 했을 때 팝업으로 나타난 달력이 사라진다.
건드릴 component는 TodoAddForm과 Calendar이다.
1. 기한 설정 버튼을 누르면 달력이 나타난다.
[TodoAddForm.js]
const [dueDate, setDueDate] = useState("");
const [openCalendar, setOpenCalendar] = useState(false);
const showCalendar = () => {
setOpenCalendar(true);
};
const closeCalender = () => {
setOpenCalendar(false);
};
//자식 컴포넌트인 calendar에 전달해 줄 함수
const selectDeadline = (date) => {
setDueDate(date);
closeCalender();
};
기한에 관한 날짜데이터와 calendar의 노출 여부를 담을 state를 생성해주고
캘린더를 열고 닫는 함수를 만들어준다.
<div className="due-date">
<button className="date" onClick={showCalendar}>
기한 설정</button>
{openCalendar && (
<Calendar selectDueDate={selectDueDate} />
)}
</div>
onClick에 캘린더를 보여주는 함수를 연결시키고
Calendar에 selectDueDate 함수를 전달해준다.
2. 달력에 있는 날짜 선택 시 해당 날짜를 데이터에 담고 표시
이전에 작업했던 캘린더에서 날짜정보를 담는 thisCalendar state에는
{prev: [26,27,28,29,30], thisDate: [1,2,3,4,5…], next: [1,2,3,4,5]}
위와 같은 형식으로 데이터를 담고 전부 따로따로 출력을 해주었었는데
달력의 날짜를 클릭한 후 클릭한 날짜가 지난달인지 이번달인지 다음달인지 알 수 있는 코드로
변경하기 위해 데이터 형식을 변경했다.
[Calendar.js]
const calendarMaker = () => {
const prevLast = new Date(currentDate.year, currentDate.month, 0);
const thisLast = new Date(currentDate.year, currentDate.month + 1, 0);
const prevLastDate = prevLast.getDate();
const prevLastDay = prevLast.getDay();
const thisLastDate = thisLast.getDate(); // 31
const thisLastDay = thisLast.getDay(); // 1
const prevDates = [];
// const thisDates = [...Array(thisLastDate + 1).keys()].slice(1); //
const thisDates = [];
const nextDates = [];
for (let i = 1; i < thisLastDate + 1; i++) {
thisDates.push([i, "this"]);
}
if (prevLastDay !== 6) {
for (let i = 0; i < prevLastDay + 1; i++) {
prevDates.unshift([prevLastDate - i, "prev"]);
}
}
for (let i = 1; i < 7 - thisLastDay; i++) {
nextDates.push([i, "next"]);
}
const dates = prevDates.concat(thisDates, nextDates);
console.log(dates);
setThisCalendar(dates);
};
date들을 array로 묶어서 해당 데이터가 어떤 것인지 표시를 해 주었다.
즉 dates에는 [[26, "prev"], [26, "prev"], [26, "prev"] …, [1,"thisDate"],[2,"thisDate"],[3,"thisDate"]…, [1,"next"],[2,"next"],[3,"next"]…]
이런 식으로 담겨 있게 된다.
불필요한 데이터가 많이 들어가는것 같아서 다른방식을 사용할까 했지만
보여지는 코드가 조금 짧았으면 해서 해당 방식을 사용하였다.
<div className="calendar-dates">
{thisCalendar &&
thisCalendar.map((date, i) => (
<div
key={i}
className={` ${
date[1] == "prev"
? "prev-dates"
: date[1] == "next"
? "next-dates"
: "date"
} cell`}
onClick={(event) => onClickDate(event, date)}
>
{date[0]}
</div>
))}
</div>
안에 있는 데이터가 prev, next인지에 따라 className을 다르게 설정해주었고
onClickDate이벤트에는 date정보 (ex [1, "prev"])를 전달해주었다.
const onClickDate = (event, date) => {
let month = currentDate.month;
if (date[1] == "prev") {
month = currentDate.month - 1;
}
if (date[1] == "next") {
month = currentDate.month + 1;
}
const newDate = new Date(currentDate.year, month, date[0]);
seleteDueDate(newDate);
};
data[1]의 값이 prev, next면 현재 currentDate.month값을 수정해주고
thisDate면 기본 설정한 month값인 currentDate.month값이 전달되도록 하였다.
month값이 -1이 되면 인식을 못하는가 했는데 newDate에서 알아서 년도까지 변경을 해준다.
아까 부모 컴포넌트로부터 전달 받은 selectDueDate함수에 newDate를 전달해주면
DueDate의 state에 값이 들어간다.
<button className="date" onClick={showCalendar}>
기한 설정 {DueDate && <span>{dateToString(DueDate)}</span>}
</button>
export const dateToString = (date) => {
const today = new Date();
const getMonth = date.getMonth();
const getDate = date.getDate();
const week = ["일", "월", "화", "수", "목", "금", "토"];
const getDay = week[date.getDay()];
return `${getMonth + 1}월 ${getDate}일 (${getDay})`;
};
일단은 날짜 데이터를 변환하는 함수를 만들고 데이터를 꺼내 쓸 때 변환하는 식으로
코드를 짰는데 생각해보니 데이터에 집어넣을 때 어느정도 변환을 시켜서 집어넣는게 좋을 것 같다.
3. 달력이 아닌 다른 영역을 클릭 시 달력 팝업 숨기기.
[TodoAddForm.js]
const calendarRef = useRef();
...생략
const handleClickOutside = ({ target }) => {
if (openCalendar && !calendarRef.current?.contains(target)) {
closeCalender();
}
};
useEffect(() => {
window.addEventListener("click", handleClickOutside);
return () => {
window.removeEventListener("click", handleClickOutside);
};
}, [openCalendar]);
..생략
{openCalendar && (
<Calendar ref={calendarRef} selectDueDate={selectDueDate} />
)}
useRef로 달력팝업을 지정해주고
useEffect로 window에 클릭이벤트를 넣어주었고,
컴포넌트가 언마운트 되었을 때 해당이벤트를 지워주었다.
!calendarRef.current.contains(target)를 그냥 사용하면 다른 이벤트로 인해 달력팝업이 닫히게 될 경우
달력 컴포넌트가 사라져서 그런지 contains가 undefind로 에러가 생기기 때문에 optional chaining을 해주어야 했다.
[Calendar.js]
const Calendar = forwardRef((props, ref) => {
//...생략
return(
<div className="todo-calendar" ref={ref}>
...
<div>
)
}
참고로 useRef를 이용하여 컴포넌트에 직접적으로 ref를 넣으면 에러를 맞이하기 때문에.
forwardRef를 이용하여 하위 컴포넌트의 div를 잡아주었다.
생각보다 은근 오래 걸린 것 같다.
기한 설정 하는 코드는 만들어 놓았으니
설정해 놓은 기한을 변경하는 작업을 할 예정이다.
'React' 카테고리의 다른 글
[React] 리렌더링 조건 / React memo (0) | 2022.01.14 |
---|---|
[React] Date를 활용하여 오늘/내일/지난 날짜 변환 해보기 (0) | 2022.01.12 |
[React] 라이브러리 없이 캘린더 만들어 보기 (0) | 2022.01.10 |
[React] Drag and Drop 이벤트 응용해보기 / 문제해결 (0) | 2022.01.08 |
[React] Redux 초기 세팅, 구조 with Ducks (0) | 2022.01.07 |