[to-do-app] React 할 일 완료 & 삭제 기능
🧶 𝗪𝗲𝗯/React

[to-do-app] React 할 일 완료 & 삭제 기능

https://yeomss.tistory.com/202?category=976597 

 

[to-do-app] React Header 만들기

https://yeomss.tistory.com/201 [to-do-app] React 환경설정 + Redux React 프로젝트를 할 때 필수로 Redux 를 사용하는데, 항상 까먹습니다. 🥹 그래서 간단한 To do App 을 만들어서 블로그에 기록을 하고자..

yeomss.tistory.com

이전 글은 위에 링크로 달아놓겠습니다. 이번에는 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

 

[to-do-app] React 할 일 모두 삭제 기능

https://yeomss.tistory.com/205?category=976597 [to-do-app] React 할 일 완료 & 삭제 기능 https://yeomss.tistory.com/202?category=976597 [to-do-app] React Header 만들기 https://yeomss.tistory.com/201..

yeomss.tistory.com

 

728x90