https://yeomss.tistory.com/202?category=976597
이전 글은 위에 링크로 달아놓겠습니다. 이번에는 List 를 만들어보겠습니다.
List에서 투 두 리스트를 완료 체크하고, 요소 하나를 삭제하는 기능을 추가하도록 하겠습니다.
ListView
import React from "react";
import { useSelector } from "react-redux";
import "../styles/list.css";
import Todo from "../components/list/Todo";
import TodoLoading from "../components/loading/todo";
const ListView = () => {
const todo = useSelector((state) => state.todo.data.list);
console.log(todo);
return (
<div>
{!todo ? (
<TodoLoading />
) : todo.length === 0 ? (
<div>투두 리스트를 작성해주세요</div>
) : (
<Todo todos={todo} />
)}
</div>
);
};
export default ListView;
ListView 모습은 다음과 같습니다. todo 데이터가 없다면 로딩 화면을 띄우고 그렇지 않으면 todo 데이터의 길이를 확인합니다.
만약에 길이가 0이라면 (요소가 없다면) 리스트를 작성해달라는 화면을 띄우고, 데이터가 있다면 투 두 리스트를 보여줍니다.
map을 사용하여 반복문 처리하기
<ul>
{todos.map((todo, id) => (
<li className="shadow" key={id}>
<i
className={
todo.isCompleted
? "checkBtn fas fa-check checkBtnCompleted"
: "checkBtn fas fa-check"
}
onClick={(e) => {
toggleTodo(e, id);
}}></i>
<span
className={todo.isCompleted ? "todoText textCompleted" : "todoText"}
onClick={(e) => {
toggleTodo(e, id);
}}>
{todo.text}
</span>
<span className="removeBtn">
<i
className="fas fa-trash-alt"
onClick={(e) => {
removeTodo(e, id);
}}></i>
</span>
</li>
))}
</ul>
리액트에서 컴포넌트 반복문을 처리할 때는 map() 함수를 사용해야 합니다.
그리고 해당 child 컴포넌트는 key 가 있어야 합니다. map() 에서 두번째 인자는 인덱스를 나타내며 이를 key 로 사용하면 됩니다.
리스트에서 값을 나타낼 때는 brace curly 를 사용합니다.
class 바인딩하기
<i
className={
todo.isCompleted
? "checkBtn fas fa-check checkBtnCompleted"
: "checkBtn fas fa-check"
}
onClick={(e) => {
toggleTodo(e, id);
}}></i>
클래스를 바인딩을 할 때는 React는 brace curly 와 삼항조건연산자를 사용합니다.
만약 todo.isCompleted 가 true 이면 첫번째 연산자가 class가 되고, false 면 두번째 연산자가 class가 됩니다.
리스트 요소 체크하기 (리액트 함수 매개변수 넘기기)
const toggleTodo = useCallback(
(e, id) => {
dispatch(todoSlice.actions.toggleTodo(id));
},
[dispatch]
);
리스트 요소 하나를 체크할 때 toggleTodo 함수를 사용하여 이벤트를 수행하겠습니다.
리듀서에 있는 동기 함수인 toggleTodo 를 일으키는 action 을 해당 함수에 설정하도록 합니다.
actions.toggleTodo() 로 실행할 수 있습니다. reducer 함수를 실행할 때는 () 괄호까지 다 붙여야 실행이 됩니다.
onClick={(e) => {
toggleTodo(e, id);
}}
onClick 함수를 작성할 때는 onClick={toggleTodo(e, id)} 이렇게가 아니라 onClick={e => {toggleTodo(e, id)}}
이런식으로 넘겨야합니다. 전자처럼 넘기면 콜백이 계속 생성되어 오류가 납니다.
// reducer/todo.js
reducers: {
toggleTodo(state, action) {
const id = action.payload;
state.data.list[id].isCompleted = !state.data.list[id].isCompleted;
},
}
리듀서에 있는 동기 함수인 toggleTodo 를 일으키는 action 을 해당 함수에 설정하도록 합니다.
매개인자로 넘긴 id 는 action.payload 에 있습니다.
리스트 요소 삭제하기
reducers: {
toggleTodo(state, action) {
const id = action.payload;
state.data.list[id].isCompleted = !state.data.list[id].isCompleted;
},
removeTodo(state, action) {
const id = action.payload;
state.data.list.splice(id, 1);
},
}
리스트 요소를 삭제할 때는 splice 를 사용합니다.
전체 코드
// Todo.js
import React, { useCallback } from "react";
import todoSlice from "../../reducers/todo";
import { useDispatch } from "react-redux";
const TodoList = ({ todos }) => {
const dispatch = useDispatch();
const toggleTodo = useCallback(
(e, id) => {
dispatch(todoSlice.actions.toggleTodo(id));
},
[dispatch]
);
const removeTodo = useCallback(
(e, id) => {
console.log("todo 삭제", id);
dispatch(todoSlice.actions.removeTodo(id));
},
[dispatch]
);
return (
<ul>
{todos.map((todo, id) => (
<li className="shadow" key={id}>
<i
className={
todo.isCompleted
? "checkBtn fas fa-check checkBtnCompleted"
: "checkBtn fas fa-check"
}
onClick={(e) => {
toggleTodo(e, id);
}}></i>
<span
className={todo.isCompleted ? "todoText textCompleted" : "todoText"}
onClick={(e) => {
toggleTodo(e, id);
}}>
{todo.text}
</span>
<span className="removeBtn">
<i
className="fas fa-trash-alt"
onClick={(e) => {
removeTodo(e, id);
}}></i>
</span>
</li>
))}
</ul>
);
};
export default TodoList;
// todo.js
const { createSlice } = require("@reduxjs/toolkit");
const { getTodo } = require("../actions/todo");
const initialState = {
isTodoGetting: false,
data: {
list: [
{ isCompleted: false, text: "hi1" },
{ isCompleted: false, text: "hi2" },
],
},
};
const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {
toggleTodo(state, action) {
const id = action.payload;
state.data.list[id].isCompleted = !state.data.list[id].isCompleted;
},
removeTodo(state, action) {
const id = action.payload;
state.data.list.splice(id, 1);
},
},
extraReducers: {
[getTodo.pending](state, action) {
state.isTodoGetting = true;
},
[getTodo.fulfilled](state, action) {
state.isTodoGetting = false;
state.data = action.payload;
},
[getTodo.rejected](state, action) {
state.isTodoGetting = false;
state.data = null;
},
},
});
module.exports = todoSlice;
// TodoView.js
import React from "react";
import { useSelector } from "react-redux";
import "../styles/list.css";
import Todo from "../components/list/Todo";
import TodoLoading from "../components/loading/todo";
const ListView = () => {
const todo = useSelector((state) => state.todo.data.list);
console.log(todo);
return (
<div>
{!todo ? (
<TodoLoading />
) : todo.length === 0 ? (
<div>투두 리스트를 작성해주세요</div>
) : (
<Todo todos={todo} />
)}
</div>
);
};
export default ListView;
ul {
list-style-type: none;
padding-left: 0px;
margin-top: 0;
text-align: left;
}
li {
display: flex;
min-height: 50px;
height: 50px;
line-height: 50px;
margin: 0.5rem 0;
padding: 0 0.9rem;
background: white;
border-radius: 5px;
cursor: pointer;
justify-content: space-between;
}
.todoText {
width: 100%;
}
.checkBtn {
line-height: 45px;
color: #62acde;
margin-right: 5px;
}
.checkBtnCompleted {
color: #b3adad;
}
.textCompleted {
text-decoration: line-through;
color: #b3adad;
}
.removeBtn {
color: #de4343;
}
결과 화면
#React 함수 매개인자 매개변수 #리액트 리스트 요소 삭제 #리액트 투두앱
다음 글
https://yeomss.tistory.com/207
'🧶 𝗪𝗲𝗯 > React' 카테고리의 다른 글
[to-do-app] React react-bootstrap으로 모달 창 생성 (0) | 2022.04.04 |
---|---|
[to-do-app] React 할 일 추가 기능 (0) | 2022.04.04 |
[to-do-app] React 할 일 모두 삭제 기능 (0) | 2022.03.28 |
[to-do-app] React Header 만들기 (0) | 2022.03.24 |
[to-do-app] React 환경설정 + Redux (0) | 2022.03.24 |