인프런 커뮤니티 질문&답변

윤영인님의 프로필 이미지
윤영인

작성한 질문수

[2024] 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지

새 일기 등록이 안됩니다..(Uncaught TypeError: Cannot read properties of undefined (reading 'date'))

해결된 질문

작성

·

573

·

수정됨

0

새 일기를 쓰려고 하면 다음과 같은 에러가 발생합니다.

해결해보려고 했는데 어떻게 손대야할지 막막하더라구요ㅠ

코드를 뒤져보고 강의도 몇번씩 돌려보고 깃헙 소스코드랑 비교도 해봤습니다... 어떤식으로 에러를 해결해야하는지 찾는 방법도 함께 알려주시면 정말정말 감사하겠습니다..!!

 

아래는 제가 작성한 소스코드입니다!

Home.js

import { useContext, useEffect, useState } from "react";
import { DiaryStateContext } from "../App";

//components
import MyButton from "./../components/MyButton";
import MyHeader from "./../components/MyHeader";
import DiaryList from "../components/DiaryList";

const Home = () => {
  const diaryList = useContext(DiaryStateContext);

  const [data, setData] = useState([]);
  // 날짜 저장 state
  const [curDate, setCurDate] = useState(new Date());
  const headText = `${curDate.getFullYear()}년 ${curDate.getMonth() + 1}월`;

  useEffect(() => {
    if (diaryList.length >= 1) {
      const firstDay = new Date(curDate.getFullYear(), curDate.getMonth(), 1).getTime();
      const lastDay = new Date(curDate.getFullYear(), curDate.getMonth() + 1, 0).getTime();
      setData(diaryList.filter((it) => firstDay <= it.date && it.date <= lastDay));
    } else {
      setData([]);
    }
  }, [diaryList, curDate]);

  // useEffect(() => {
  //   console.log(data);
  // }, [data]);

  const increaseMonth = () => {
    setCurDate(new Date(curDate.getFullYear(), curDate.getMonth() + 1, curDate.getDate()));
  };

  const decreaseMonth = () => {
    setCurDate(new Date(curDate.getFullYear(), curDate.getMonth() - 1, curDate.getDate()));
  };

  return (
    <div>
      <MyHeader
        headText={headText}
        leftChild={<MyButton text={"<"} onClick={decreaseMonth} />}
        rightChild={<MyButton text={">"} onClick={increaseMonth} />}
      />
      <DiaryList diaryList={data} />
    </div>
  );
};

export default Home;

 

App.js

import React, { useReducer, useRef } from "react";

import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";

import Home from "./pages/Home";
import New from "./pages/New";
import Edit from "./pages/Edit";
import Diary from "./pages/Diary";

const reducer = (state, action) => {
  let newState = [];
  switch (action.type) {
    case "INIT": {
      return action.data;
    }
    case "CREATE": {
      newState = [action.data, ...state];
      break;
    }
    case "REMOVE": {
      newState = state.filter((it) => it.id !== action.targetId);
      break;
    }
    case "EDIT": {
      newState = state.map((it) => (it.id === action.data.Id ? { ...action.data } : it));
      break;
    }
    default:
      return state;
  }
  return newState;
};

export const DiaryStateContext = React.createContext();
export const DiaryDispatchContext = React.createContext();

const dummyData = [
  {
    id: 1,
    emotion: 3,
    content: "오늘의 일기 1번",
    date: 1702273692142,
  },
  {
    id: 2,
    emotion: 2,
    content: "오늘의 일기 2번",
    date: 1702273692143,
  },
  {
    id: 3,
    emotion: 5,
    content: "오늘의 일기 3번",
    date: 1702273692144,
  },
  { id: 4, emotion: 1, content: "오늘의 일기 4번", date: 1702273692145 },
  {
    id: 5,
    emotion: 4,
    content: "오늘의 일기 5번",
    date: 1702273692146,
  },
];

function App() {
  const [data, dispatch] = useReducer(reducer, dummyData);

  const dataId = useRef(0);
  // CREATE
  const onCreate = (date, content, emotion) => {
    dispatch({
      type: "CREATE",
      date: {
        id: dataId.current,
        date: new Date(date).getTime(),
        content,
        emotion,
      },
    });
    dataId.current += 1;
  };
  // REMOVE
  const onRemove = (targetId) => {
    dispatch({ type: "REMOVE", targetId });
  };
  // EDIT
  const onEdit = (targetId, date, content, emotion) => {
    dispatch({
      type: "EDIT",
      data: {
        id: targetId,
        date: new Date(date).getTime(),
        content,
        emotion,
      },
    });
  };

  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider value={{ onCreate, onEdit, onRemove }}>
        <BrowserRouter>
          <div className="App">
            <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/new" element={<New />} />
              <Route path="/edit/:id" element={<Edit />} />
              <Route path="/diary/:id" element={<Diary />} />
            </Routes>
          </div>
        </BrowserRouter>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );
}

export default App;


DiaryList.js

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import MyButton from "./MyButton";
import DiaryItem from "./DiaryItem";

const sortOptionList = [
  { value: "latest", name: "최신순" },
  { value: "oldest", name: "오래된 순" },
];

const filterOption = [
  { value: "all", name: "전부 다" },
  { value: "good", name: "좋은 감정만" },
  { value: "bad", name: "안좋은 감정만" },
];

const ControlMenu = ({ value, onChange, optionList }) => {
  return (
    <select className="ControlMenu" value={value} onChange={(e) => onChange(e.target.value)}>
      {optionList.map((it, idx) => (
        <option value={it.value} key={idx}>
          {it.name}
        </option>
      ))}
    </select>
  );
};

const DiaryList = ({ diaryList }) => {
  const navigator = useNavigate();
  const [sortType, setSortType] = useState("latest");
  const [filter, setFilter] = useState("all");

  const getProcessedDiaryList = () => {
    const filterCallBack = (item) => {
      if (filter === "good") {
        return parseInt(item.emotion) <= 3;
      } else {
        return parseInt(item.emotion) > 3;
      }
    };
    // 비교함수
    const compare = (a, b) => {
      if (sortType === "latest") {
        return parseInt(b.date) - parseInt(a.date);
      } else {
        return parseInt(a.date) - parseInt(b.date);
      }
    };
    const copyList = JSON.parse(JSON.stringify(diaryList));

    const filteredList = filter === "all" ? copyList : copyList.filter((it) => filterCallBack(it));
    const sortedList = filteredList.sort(compare);
    return sortedList;
  };

  return (
    <div className="DiaryList">
      <div className="menu_wrapper">
        <div className="left_col">
          <ControlMenu value={sortType} onChange={setSortType} optionList={sortOptionList} />
          <ControlMenu value={filter} onChange={setFilter} optionList={filterOption} />
        </div>
        <div className="right_col">
          <MyButton type={"positive"} text={"새 일기쓰기"} onClick={() => navigator("/new")} />
        </div>
      </div>
      {getProcessedDiaryList().map((it) => (
        <DiaryItem key={it.id} {...it} />
      ))}
    </div>
  );
};

DiaryList.defaultProps = {
  diaryList: [],
};

export default DiaryList;

답변 2

1

윤영인님의 프로필 이미지
윤영인
질문자

계속 코드를 봐보니...찾았습니다..ㅠㅠ

App.js에서 오타가 있었습니다.

// CREATE
  const onCreate = (date, content, emotion) => {
    dispatch({
      type: "CREATE",
      date: {          // 오타!! date -> data로 변경해야함!
        id: dataId.current,
        date: new Date(date).getTime(),
        content,
        emotion,
      },
    });
    dataId.current += 1;
  };

혹시 저와 같은 에러를 만나는 분이 계실수도 있으니 글은 남겨두겠습니다!

0

이정환 Winterlood님의 프로필 이미지
이정환 Winterlood
지식공유자

아이고..! data -> date로 잘못 쓰셨군요 찾으셨다니 다행입니다 😀

 

PS...

여담으로 이런 오타 등의 이슈는 TS에서는 빨간줄로 알려주기 때문에 사전에 방지됩니다

혹시 관심있으시다면 한입 타스도 많은 관심 부탁드려요! (소곤소곤)

윤영인님의 프로필 이미지
윤영인
질문자

넵 리액트 강의 수강 후 타입스크립트도 배우겠습니다~~!

윤영인님의 프로필 이미지
윤영인

작성한 질문수

질문하기