본문 바로가기

Sparta x 이노베이션 캠프/팀 프로젝트

4주차 : React로 Redux를 활용한 My To Do List 만들기. map is not a function

반응형

Goal: My Todo List 만들기

React 입문 주차 개인 과제를 react-router-dom, styled-components, redux를 사용해서 My Todo List를 다시 만들어봅니다.

 

features : 구현해야 할 기능

  • Create Todo
  • Read Todos, Todo
  • Update Todo
  • Delete Todo

Requirement : 과제에 요구되는 사항

  • 공통
    • todos 데이터는 리덕스를 사용해서 전역으로 상태를 관리합니다.
    • todos 모듈은 **Ducks 패턴**으로 구현합니다.
    • 컴포넌트 구조는 자유롭게 구현하되, 본인이 그렇게 나눈 이유에 대해서 README에 작성합니다.
  • 메인 페이지
    • 디자인과 화면 구성은 자유롭게 구현합니다.
    • Todo의 상태에 “완료” 그룹과 “진행중" 그룹을 나뉘어서 보이도록 구현합니다. 예시 영상 꼭 위, 아래가 아니어도 되며 창의적으로 구현해도 됩니다.
    • Todo를 추가하면 제목 input과 내용 input은 다시 빈 값이 되도록 구현합니다.
    • input에 값이 있는 상태에서 상세페이지로 이동하는 경우, input의 value가 초기화 되도록 구현합니다.
    • Todo의 완료상태가 true이면, 상태 버튼의 라벨을 “취소”, false 이면 라벨을 “완료” 로 보이도록 구현합니다.
    • 전체 화면의 최대 넓이는 1200px, 최소 넓이는 800px로 제한하고, 컨텐츠를 화면의 가운데로 배치 합니다.
    • 상세보기 클릭하면 Todo의 상세 페이지로 이동합니다. 상세 페이지에서 보여야 하는 내용은 아래 토글에서 별도 안내합니다.
    • 상세 페이지의 디자인과 화면 구성은 자유롭게 구현하되, 아래 요소들은 보여야 합니다.
      • Todo의 ID
      • Todo의 제목
      • Todo의 내용
      • 이전으로 버튼: 이전으로 버튼을 구현하고, 이전으로 버튼을 클릭하면 리스트 화면으로 되돌아 갑니다.
    • 제한사항
      • map을 사용할 때 반드시 key을 넣어야 하며, map 의 index를 사용을 금지합니다. 이것을 금지하는 이유는 강의에 다루었습니다.
      • Todo Id 생성 시 todos.length 사용해서 생성하지 않습니다. todos.length 을 사용해서 id 생성 시 발생할 수 있는 문제점에 대해 고민해보시길 바랍니다.

Troubleshooting

1. URL parameter가 id의 값으로 넘어가도록 구현했으나 detail page로 연결되지 않음.

<Route path = "/detail">  =>    <Route path = "/detail/:id"> 로 고쳐서 해결함

 

2. Todo.jsx 에서 useHistory를 이용해 history.push("/detail/"+initialState.id) 로 이전으로 돌아가는 기능을 구현했지만 메인 페이지가 아닌 이전 페이지로 돌아가는 기능이어서 react-router-dom 상위 버전 패키지를 다시 설치하고 useNavigate를 이용해서 

메인페이지로 이동하도록 구현함.

 

3. Array에서 배열 1개만 출력하지 못하는 문제 => 필터 함수 적용하여 해결함

 

4.react-router-dom 상위버전을 받자 바로 오류남. version의 업그레이드로 문법이 살짝 바뀌었음!! <Router> 가 <Routes>를 감싸고 <Routes>가 elment를 품은 <Route>를 품는다.

 <Router>  
      <Routes>
        <Route path="/" element={<Home/>}exact/>
        <Route path="/detail/:id"  element={<Detail/>}exact/>
      </Routes>
    </Router>

 

5. 배열속의 배열?

새로운 Todo를 추가할 때 initialstate가 같이 추가되는 현상 발생
문제는 useState에서 initialstate를 대괄호로 감싸줘서였음. 대괄호 없애니 문제가 해결되었다.

6.ERROR

name:['22'] value값이 리스트 형식으로 콘솔에 찍혀있음. 왜인가 보니
[name]: value 로 바꾸어 해결했음.

7. map is not a function Error

Detail에서 소스코드를 참고하여 useEffect를 사용해서 id값을 가져오도록 해서 state 하나를 가져오도록 했는데, 디테일 페이지에서 이전으로 버튼을 누르면 메인 화면이 하얗게 바뀌면서 콘솔에 에러가 찍힘. map이 function 이 아니라는 메시지였다.

다음은 문제의 코드, 순서대로 Detail.js  , todos.js(reducer가 있는)의 일부이다.

import React from "react";
import { useSelector, useDispatch  } from "react-redux";
import { get_todo_id } from "../redux/modules/todos";
import { useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";

const Detail = () => {
    
    const navigate = useNavigate();
    const dispatch = useDispatch();
    const todos = useSelector((state) => state.todos.todos)
    console.log (todos)
    const {id} = useParams();
    // const todo = todos.find((todo) => {
    //     return String(todo.id) === id;})

    useEffect(() => {
        dispatch(get_todo_id(id))
    },[dispatch, id]);

    return (
        <>
            <button
                onClick={() => {
                    navigate("/");
                }}>이전으로</button>
            <h3>ID:{id}</h3>
            <h1>할 일:{todos[0].title}</h1>
            <h3>내용:{todos[0].body}</h3>
        </>
    )
}
        case GET_TODO_ID:
            return {
                ...state,
                todos: state.todos.filter((todo) => {
                    return todo.id == action.payload;
                })
            };

 

여기서 문제가 되는것은 GET_TODO_ID 라는 case문이었는데 todos 의 state를 바꾸어 버리는 것이 문제였다.

해결책은 디테일 정보만 따로 새로만들어주는 코드를써서 정보를 보관하도록 하는 것, 아니면 useEffect 함수를 쓰지 않고 dispatch 없이 Detail.js에 직접 filter아니면 find 함수를 통하여 todos 배열 1개를 받아내는것.

후자가 더 간편하기에 후자로 변경함. 아래가 변경된 코드이다. todos.js에서 내가 부여한 initialstate가 문자열이고 실제로 부여하는 id는 숫자이기때문에 리턴할때 todo.id를 문자열로 바꾸어 엄격한 같음? 을 비교 할 수 있게 해주었다. 

 

import React from "react";
import { useSelector, useDispatch  } from "react-redux";
import { useParams, useNavigate } from "react-router-dom";

const Detail = () => {
    
    const navigate = useNavigate();
    const todos = useSelector((state) => state.todos.todos)
    console.log (todos)
    const {id} = useParams();
    const todo = todos.find((todo) => {
        return String(todo.id) === id;}) 
    // useEffect(() => {
    //     dispatch(get_todo_id(id))           // 주석처리함
    // },[dispatch, id]);

    return (
        <>
            <button
                onClick={() => {
                    navigate("/");
                }}>이전으로</button>
            <h3>ID:{id}</h3>
            <h1>할 일:{todo.title}</h1>
            <h3>내용:{todo.body}</h3>
        </>
    )
}

export default Detail;

 

GitHub주소 : https://github.com/nonjee888/w4_todoList

 

회고

이번주는 같은 틀에서 응용된 프로젝트였기 때문에 살짝 부담이 덜했지만 여전히 처음에 많이 서툴렀고 또 실수가 많았다. 응원해주신 팀원분들과 다른 팀원분들, 또 멘토링에 힘써주신 기술매니저님들의 도움으로 무사히 개인 과제물을 제출 할 수 있었다. 나는 뭐든지 먼저 부딫혀보고 깨닫는 습관 때문에 섣부르게 깊이있는 사전 공부없이 이것 저것 해보다가 코드를 회생 불능으로 만드는 일이 허다하다. 

이번에도 그렇게 프로젝트 하나를 초반에 날려먹고 새로 시작하는 기분으로 다시 컴포넌트 구조를 잡아서 만들었다. 예전 강의를 보면서 했더니 리액트 라우터 돔 버전 문제 때문에 다운그레이드를 했다가 업그레이드를 했다가 삽질을 많이 했다. 결국엔 최신버전으로 깔고 바뀐 문법을 적용하였다. 좀 차근차근 서두르지 않고 시작해야겠다. 

반응형