[React,Firebase] 투두리스트 Complete, Delete 구현

이은지·2023년 12월 5일
0

사용자가 할 일을 완료했을 때 처리하는 기능과 할 일을 삭제하는 기능을 구현하려 한다.

🌻 Complete 기능 구현

다음과 같이 할 일을 완료하고 사용자가 체크 박스를 클릭하면, 체크된 아이콘으로 바뀌게 되고, todo 내용에 취소선이 생긴다.

🔆 TodoItem컴포넌트

  1. useSelector 훅을 이용해 리덕스 스토어로부터 todo리스트 데이터 정보를 가져온다.
  2. map 함수를 이용해 각 todo item을 매핑하여 렌더링하고, todo데이터의 Completed 상태에 따라 다른 디자인의 컴포넌트를 렌더링한다.
  3. Completed 상태에 따라 아이콘을 클릭했을 때, handleComplete함수에 전달되는 인자 값이 달라진다.
    • Completed가 true일 때, 클릭 된 todo item의 TodoId, index 와 업데이트할 Completed 상태로 false가 프롭으로 전달된다.
    • Completed가 false일 때, 클릭 된 todo item의 TodoId, index와 업데이트할 Completed 상태로 true 가 프롭으로 전달된다.
    • handleComplete함수는 전달된 프롭을 기반으로 액션 함수 completeTodo를 실행한다
import React, { useEffect } from 'react'
import * as m from '../../../../style/MainPagestyle'
import { useSelector, useDispatch } from 'react-redux'
import {BsCircle, BsCheckCircleFill} from 'react-icons/bs'
import {AiOutlineDelete} from 'react-icons/ai'
import { completeTodo, deleteTodo } from '../../../../_redux/todo'
function TodoItem() {
    const todos = useSelector(state=>state.Todo.todos);
    const dispatch = useDispatch();

    const handleComplete = (todoId, index, completed) => {
        console.log(todoId, index);
        dispatch(completeTodo(todoId, index, completed))
     
    }

    return (
        <div style={{width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
            {todos && todos.map((todo,index)=>(
                todo.Completed ?   
                <m.TodoContainer key={index} >
                <BsCheckCircleFill className='checkbox' onClick={()=>handleComplete(todo.TodoId, index, false)}/>
                <li className='content'style={{ textDecoration: 'line-through' }}>{todo.Content}</li>
                </m.TodoContainer>
                :
                <m.TodoContainer key={index}>
                <BsCircle className='checkbox' onClick={()=>handleComplete(todo.TodoId, index, true)}/>
                <li className='content'  style={{ textDecoration: 'none' }}>{todo.Content}</li>
                </m.TodoContainer>
       
            ))}
     </div>
    )
}

export default TodoItem

🔆 리덕스 파일 - todo.js

  1. 액션 정의
  • COMPLETE_TODO: To-Do 항목의 완료 상태를 업데이트하는 액션을 나타냄
  1. 액션 생성자
  • completeTodo(todoId, index, completed): To-Do 항목의 완료 상태를 업데이트하는 액션 생성자
    • Firebase를 통해 updateCompleted(todoId, completed) 함수를 호출하여 To-Do 항목의 완료 상태를 업데이트하고, COMPLETE_TODO 액션의 페이로드로 인덱스와 업데이트된 완료 상태를 반환
  1. 리듀서
  • COMPLETE_TODO: 특정 To-Do 항목의 완료 상태를 업데이트
    • 기존 state를 복제
    • 해당 index에 해당되는 state의 completed 상태를 페이로드 completed 값으로 변환
    • 업데이트된 배열을 리턴
import { saveTodo, findallTodo, updateCompleted, deleteItem } from "../firebase/firebase_todo";

//Actions
const COMPLETE_TODO = 'todo_reducer/completetodo'

// Action Creators
export async function completeTodo(todoId, index, completed){
    try{

        updateCompleted(todoId, completed);
        // console.log(todos);
        return{
            type: COMPLETE_TODO,
            payload: {
                index: index,
                completed: completed
            }
        }
    }catch(error){
        console.error("Todo 데이터 업데이트 실패:", error);
        throw error;
    }
    
}

//Reducer

const initialState = {
    todos:[]
}

export default function (state = initialState, action){
    switch (action.type) {
        case ADD_TODO:
            // 현재 todos 배열에 새로운 todo 데이터 추가
            const newTodos = [...state.todos, action.payload];
            return { ...state, todos: newTodos };
        case INITIALIZE_TODOS:
            return {...state, todos: action.payload}
        case COMPLETE_TODO:
            const index = action.payload.index;
            const updatedTodos = [...state.todos];
            state.todos[index].Completed = action.payload.completed;
            return {...state, todos: updatedTodos}
        default:
            return state;
    }
}

🔆 파이어베이스 파일 - firebase_todo.js

  • todoId를 이용해 todos 컬렉션 내에 있는 문서를 검색하여 가져옴
  • 파이어베이스 update 메서드를 사용하여 todo 문서를 업데이트
export async function updateCompleted(todoId, completed){

    const todo = firestore.collection("todos").doc(todoId);
    
    try{
  
        await todo.update({Completed: completed});
        console.log("Document successfully updated!")
        
    }catch(error){
        console.log(error);
        throw error;
    }
}

🌻 Delete 기능 구현

삭제 버튼을 누르면 해당 todo가 삭제되게 구현하였다.

업로드중..

🔆 TodoItem컴포넌트

  • 삭제 아이콘을 띄우고, 클릭 시 handleDelete 함수가 실행되며, todoId 프롭을 전달
    • handleDelete는 전달된 프롭을 기반으로 deleteTodo 액션 함수를 실행
(...생략...)
function TodoItem() {
    const todos = useSelector(state=>state.Todo.todos);
    const dispatch = useDispatch();

(...생략...)

    const handleDelete = (todoId) =>{
        dispatch(deleteTodo(todoId))
    }
    return (
        <div style={{width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
            {todos && todos.map((todo,index)=>(
                todo.Completed ?   
                <m.TodoContainer key={index} >
                <BsCheckCircleFill className='checkbox' onClick={()=>handleComplete(todo.TodoId, index, false)}/>
                <li className='content'style={{ textDecoration: 'line-through' }}>{todo.Content}</li>
                <AiOutlineDelete className='delete' onClick={()=>handleDelete(todo.TodoId)}/>
                </m.TodoContainer>
        
                :
                <m.TodoContainer key={index}>
                <BsCircle className='checkbox' onClick={()=>handleComplete(todo.TodoId, index, true)}/>
                <li className='content'  style={{ textDecoration: 'none' }}>{todo.Content}</li>
                <AiOutlineDelete className='delete' onClick={()=>handleDelete(todo.TodoId)}/>
                </m.TodoContainer>
       
            ))}
     </div>
    )
}

export default TodoItem

🔆 리덕스 파일 - todo.js

  1. 액션 정의
  • DELETE_TODO: To-Do 항목을 삭제하는 액션을 나타냄
  1. 액션 생성자
  • deleteTodo(todoId, index): To-Do 항목을 삭제하기 위한 액션 생성자
    • Firebase를 통해 deleteItem(todoId) 함수를 호출하여 To-Do 항목을 삭제하고, DELETE_TODO 액션의 페이로드로 삭제된 항목의 ID를 반환합니다.
  1. 리듀서
  • const Todos = state.todos.filter(todo => todo.TodoId !== action.payload);:
    • filter 함수는 배열의 각 요소를 순회하면서 조건을 만족하는 요소만 남기고 나머지 요소를 제외함
    • 현재 상태에서 삭제할 todo 항목을 필터링하여 새로운 배열 Todos를 생성하고 이를 반환함
import { saveTodo, findallTodo, updateCompleted, deleteItem } from "../firebase/firebase_todo";

//Actions
const DELETE_TODO = 'todo_reducer/deletetodo'

// Action Creators
export async function deleteTodo(todoId, index){
    try{

        deleteItem(todoId);
        // console.log(todos);
        return{
            type: DELETE_TODO,
            payload: todoId
        }
    }catch(error){
        console.error("Todo 데이터 업데이트 실패:", error);
        throw error;
    }
    
}
   
//Reducer

const initialState = {
    todos:[]
}

export default function (state = initialState, action){
    switch (action.type) {
        case ADD_TODO:
            // 현재 todos 배열에 새로운 todo 데이터 추가
            const newTodos = [...state.todos, action.payload];
            return { ...state, todos: newTodos };
        case INITIALIZE_TODOS:
            return {...state, todos: action.payload}
        case COMPLETE_TODO:
            const index = action.payload.index;
            const updatedTodos = [...state.todos];
            state.todos[index].Completed = action.payload.completed;
            return {...state, todos: updatedTodos}

        case DELETE_TODO:
            const Todos = state.todos.filter(todo => todo.TodoId !== action.payload);
            return {
                ...state,
                todos: Todos,
              };
        default:
            return state;
    }
}
profile
소통하는 개발자가 꿈입니다!

0개의 댓글