오늘은 1번째 팀별 과제 TodoList 만들기에서 각자 맡은 추가 기능을 구현하였다.
나의 역할은
1. 카테고리 별 리스트 나오기
2. 카테고리 전체보기
두 가지 였다. 결국은 카테고리를 눌렀을때 해당 카테고리에 맞는 리스트들이 분류되서 보여지는 것이다.
일단 우리 프로젝트의 폴더 구조를 살펴보면
이렇게 되어있다.
일단 팀에서 중간 머지 한 부분을 풀 땡겨와서 작업을 시작했다.
Category 가
const CATEGORY = ['work', 'exercise', 'study']
export default function TodoCategory() {
return (
<section>
<h3>category</h3>
<ul className={styles.categoryInner}>
{CATEGORY.map((cate) => {
return <li key={`category-${cate}`}>{cate}</li>
})}
</ul>
</section>
)
}
이렇게 단순하게 되어있다.
그런데 우리가 더미 데이터 구조를
{
id: 1651747060687,
text: '방청소하기',
date: '2022-05-05',
category: 'red',
done: false,
isLike: true,
},
{
id: 1651747060688,
text: 'Typescript 강의 듣기',
date: '2022-05-05',
category: 'orange',
done: true,
isLike: false,
},
{
id: 1651747060689,
text: '경주 여행 계획 세우기',
date: '2022-05-07',
category: 'yellow',
done: false,
isLike: true,
},
이런식으로 만들기로 했었고 category 종류 (color)에 따라 분리 되어야 하니
현재 TodoCategory 컴포넌트에 있는
CATEGORY 배열부터 수정을 시작했다.
const CATEGORY = [
{
'title' : 'work',
'category' : 'red'
},
{
'title' : 'exercise',
'category' : 'orange'
},
{
'title' : 'study',
'category' : 'yellow'
},
{
'title' : 'clean',
'category' : 'green'
},
]
이렇게 수정해 놓고
<ul className={styles.categoryInner}>
{CATEGORY.map(({title, category}) => {
return (
<li key={title}>
<button
type="button"
onClick={handleCategory}
value={category}>
{title}
</button>
</li>
)
})}
</ul>
위와 같이 수정해 놓으면
카테고리 요소 마다 value 로 category 값을 가지고 있기 때문에 클릭했을때 그 요소의 카테고리가 무엇인지 가져오고 가져온 것만 보이게 메서드로 만들면 되기 때문이다.
onClick 했을때 호출되는 메서드인 handleCategory 는
최상위 컴포넌트인 TodoApp 으로 보내어 props로 내려받았다.
최상위 태그인 TodoApp 컴포넌트에
const [todos, setTodos] = useState([...INITIAL_TODO])
todos state 가 있어서 이것이 화면에 뿌려지는 것이였다.
나는 이 todos state 를 컨트롤 하면 되는데
처음에는 카테고리에 해당하는 Todoitem 들을 setTodos를 이용해서 filter 로 거르면 된다고 생각했는데 처음 클릭에는 잘 작동하는데 그 다음 클릭에는 작동을 안했다.
그도 그럴만한것이 이미 특정 카테고리로 분리되어 todos에 담겼는데, 다른 카테고리를 클릭했을때 업데이트된 todos에는 당연히 없는 것이다.
기존의 todos를 유지해야했고, delete 기능처럼 아이템들이 사라질 수 있는 filter 메서들를 사용하면 안됐다.
그래서 내가 고안한 방법은
더미데이터로 만든것에 invisible 이라는 상태값을 하나더 추가한것이다.
{
id: 1651747060687,
text: '방청소하기',
date: '2022-05-05',
category: 'red',
done: false,
isLike: true,
invisible: false,
},
{
id: 1651747060688,
text: 'Typescript 강의 듣기',
date: '2022-05-05',
category: 'orange',
done: true,
isLike: false,
invisible: false,
},
{
id: 1651747060689,
text: '경주 여행 계획 세우기',
date: '2022-05-07',
category: 'yellow',
done: false,
isLike: true,
invisible: false,
},
일단은 다 false로 셋팅해 두어 전체가 보이도록 하고 카테고리를 클릭했을때 해당 카테고리는 그대로 보이고 아닌것들만 true로 바꾸어 안보이게 만드는 것이다.
그렇게 만든 메서드 handleCategory는 아래와 같다.
const handleCategory = ({currentTarget}) => {
const category = currentTarget.value
if(category){
setTodos((todos) => todos.map(
(todo) => (todo.category === category ?
{ ...todo, invisible: false } :
{ ...todo, invisible: true } )
)
)
}else{
setTodos((todos) => todos.map((todo) => ({...todo, invisible: false}) ))
}
}
category 변수는 내가 클릭한 카테고리의 종류(color 값으로 되어있음)가 무엇인지 가져온다.
그리고 전체보기 가능해야 했기 때문에,
CATEGORY 배열에 전체에 해당하는 요소도 추가했다.
const CATEGORY = [
{
'title' : 'all',
'category' : ''
},
{
'title' : 'work',
'category' : 'red'
},
{
'title' : 'exercise',
'category' : 'orange'
},
{
'title' : 'study',
'category' : 'yellow'
},
{
'title' : 'clean',
'category' : 'green'
},
]
all 을 선택했을때는 category에 빈값을 두어
if 문으로 구분을 했다.
const handleCategory = ({currentTarget}) => {
const category = currentTarget.value
if(category){
}else{
}
}
all 선택시
setTodos((todos) => todos.map((todo) => ({...todo, invisible: false}) ))
모든 아이템의 invisible : false 가 되어 보여지도록,
각각의 카테고리를 선택할 시
setTodos((todos) => todos.map(
(todo) => (todo.category === category ?
{ ...todo, invisible: false } :
{ ...todo, invisible: true } )
)
)
선택된 카테고리 와 일치하는 것은 invisible: false 해서 보이게
일치하지 않는 것은 invisible: true로 안보이게 조건문을 만들었다.
그리고 TodoItem 컴포넌트에 가서
const { id, text, done, invisible} = todo
invisible 를 추가하고
cx 라이브러리를 로드해서
import { cx } from '../../styles/index'
TodoItem 컴포넌트 최상위 태그인 li 에다가
return (
<li
className={cx(styles.todoElement, {[styles.isHidden]:invisible})}
key={`todo-${id}`}>
...
</li>
)
invisible 이 true 일 때만 isHidden 클래스를 넣어서 안보이게 하라는 조건문을 넣어주었다.
TodoItem.module.scss
cx 는 두개 이상의 class 를 보기 좋게 쓸수 있게 해주고, 위 코드처럼 조건문도 사용할 수 있어서 가독성이 더 좋아진다.
그리고 TodoItem.propTypes 에도 invisible를 추가했다. 뭔지는 잘 몰랐는데 타입을 설정해줘야하는 부분인것 같아서 다른 상태값 한거 보고 참고했다.
TodoItem.propTypes = {
todo: PropTypes.shape({
id: PropTypes.number,
text: PropTypes.string,
done: PropTypes.bool,
invisible: PropTypes.bool,
}),
handleToggle: PropTypes.func,
handleRemove: PropTypes.func,
handleEditMode: PropTypes.func,
}
리액트가 익숙치도 않고 그래서 너무 막막했는데, 그래도 팀 과제 할때는 민폐는 안 되야지 하는 마음으로 끝까지 하니까 성공했다. 처음에는 막막할 것 같았는데 하고 나니 그래도 뿌듯하고 또 다른것을 도전해 보고싶은 욕심이 생긴다.