[Udemy] React 실전(감정일기장) - 일기 수정(/edit)

productuidev·2022년 5월 26일
0

React Study

목록 보기
42/52
post-thumbnail

React 실전 (Project)

Udemy - 한입크기로 잘라 먹는 리액트


📌 감정일기장 페이지 구현 - 일기 수정(/edit)

☑️ 일기 수정(/edit) results

☑️ Edit.js

  • 수정을 하려면?! 원본일기의 데이터가 있어야
  • 원본일기의 데이터를 가져오기 (App 컴포넌트의 date state) : useContext
    ㄴ id와 일치하는 원본 데이터
    ㄴ App 컴포넌트의 DiaryStateContext 공급
  • /edit 라우팅 : id pathVariable 받을 수 있도록 지정 : useParams
  • useEffect : 컴포넌트가 mount된 시점에 데이터 가져오기
  • originData, setOriginData : 조건 설정, DiaryEditor 컴포넌트에 원본데이터 받기

src/pages/Edit.js

import { useContext, useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { DiaryStateContext } from "../App";

import DiaryEditor from "../components/DiaryEditor";

const Edit = () => {

  const [originData, setOriginData] = useState();
  const navigate = useNavigate();
  const {id} = useParams(); // 현재 전달받은 id
  const diaryList = useContext(DiaryStateContext);
  // console.log(id); // id 출력
  // console.log(diaryList); // 5개의 원본데이터 출력

  // 데이터는 컴포넌트가 mount된 시점에서 가져온다
  // 조건 : 일기데이터가 1개라도 있을 때만 가져온다 (id 오류 방지 형변환)
  // deps : id나 diaryList가 변할 때만 가져온다
  useEffect(()=>{
    if(diaryList.length>=1){
      const targetDiary = diaryList.find((it)=>parseInt(it.id) === parseInt(id));
      // console.log(targetDiary); // 가져온 id의 일기데이터 출력

      // 조건 : id가 있을 때 setOriginData로 전달 
      // 조건 : 경로에 id가 잘못 전달되었을 때 홈으로 (뒤로가기 방지) 
      if(targetDiary) {
        setOriginData(targetDiary);
      }
      else {
        navigate('/', {replace:true});
      }
    }
  },[id, diaryList, navigate]);

  // targetDiary를 통해서 originData의 state를 저장해놓고
  // originData가 있으면, DiaryEditor를 렌더링
  // prop으로 원본데이터를 전달해주자 (isEdit, originData)
  return (
    <div>
      {originData && <DiaryEditor isEdit={true} originData={originData} />}
    </div>
  );
}

export default Edit;

☑️ DiaryEditor 컴포넌트 수정

  • DiaryEditor 함수에 isEdit, originData을 prop으로 전달 (Edit 페이지처럼)
  • 작성 완료 시 onEdit 공급 (+confirm 조건 추가)
  • 수정 시 useEffect로 원본데이터 받아오기 (isEdit, originData) : 수정 페이지에서만 useEffect가 동작
  • 수정 시 MyHeader 제목 변경 (일기 수정하기)

src/components/DiaryEditor.js

import { useState, useRef, useContext, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { DiaryDispatchContext } from "./../App";

import MyHeader from "./MyHeader";
import MyButton from "./MyButton";
import EmotionItem from "./EmotionItem";

const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";

const emotionList = [
  {
    emotion_id : 1,
    emotion_img : process.env.PUBLIC_URL + `/assets/emotion1.png`,
    emotion_descript : '완전 좋음'
  },
  {
    emotion_id : 2,
    emotion_img : process.env.PUBLIC_URL + `/assets/emotion2.png`,
    emotion_descript : '좋음'
  },
  {
    emotion_id : 3,
    emotion_img : process.env.PUBLIC_URL + `/assets/emotion3.png`,
    emotion_descript : '그럭저럭'
  },
  {
    emotion_id : 4,
    emotion_img : process.env.PUBLIC_URL + `/assets/emotion4.png`,
    emotion_descript : '나쁨'
  },
  {
    emotion_id : 5,
    emotion_img : process.env.PUBLIC_URL + `/assets/emotion5.png`,
    emotion_descript : '끔찍함'
  },
]

const getStringDate = (date) => {
  return date.toISOString().slice(0, 10);
};

// DiaryEditor 함수에 isEdit, originData prop 넣어주기

const DiaryEditor = ({isEdit, originData}) => {
  const [emotion, setEmotion] = useState(3); // 3번 default
  const [date, setDate] = useState(getStringDate(new Date()));
  const [content, setContent] = useState();
  const contentRef = useRef();

  const handleClickEmote = (emotion) => {
    setEmotion(emotion);
  };

  // 작성완료에 onEdit 공급
  const {onCreate, onEdit} = useContext(DiaryDispatchContext);
  const handleSubmit = () => {
    if(content.length < 1) {
      contentRef.current.focus();
      return;
    }

    // 작성완료 confirm 조건 추가 (새 일기 작성/수정 시)
    // 조건에 따라 수정/작성 완료 후 alert창으로 묻기
    if(window.confirm(isEdit ? "일기를 수정하시겠습니까?" : "새로운 일기를 작성하시겠습니까?")){
      // 새 일기 작성인 경우(수정이 아닌 경우)
      if(!isEdit) {
        onCreate(date, content, emotion);
      }
      // 수정중인 경우 (onEdit의 props : 원본 id, 날짜, 내용, 감정)
      else {
        onEdit(originData.id, date, content, emotion);
      }
    };

    navigate('/', {replace:true});
  };

  // useEffect deps(isEdit, originData)가 바뀌면 원본데이터 받아오기
  // EditPage에서 렌더링하는 DiaryEditor에서만 useEffect가 동작하도록
  // 캘린더 setDate 당일 날짜, 원본 감정, 원본 내용
  useEffect(()=>{
    if(isEdit) {
      setDate(getStringDate(new Date(parseInt(originData.date))));
      setEmotion(originData.emotion);
      setContent(originData.content);
    }
  },[isEdit, originData]);

  const navigate = useNavigate();

  return (
    <div className="DiaryEditor">
      {/* 제목 표시 조건 */}
      <MyHeader
        headText={isEdit ? "일기 수정하기" : "새 일기쓰기"}
        leftChild={<MyButton text={"< 뒤로가기"} onClick={()=>navigate(-1)} />}
      />
      <div>
        <section>
          <h4>오늘은 언제인가요?</h4>
          <div className="inputBox">
            <input
              className="inputDate"
              type="date"
              value={date}
              onChange={(e)=>setDate(e.target.value)} />
          </div>
        </section>
        <section>
          <h4>오늘의 감정</h4>
          <div className="inputBox emotionListWrapper">
            {emotionList.map((it)=>(
              <EmotionItem key={it.emotion_id} {...it} onClick={handleClickEmote} isSelected={it.emotion_id === emotion} />
              // <div key={it.emotion_id}>{it.emotion_descript}</div>
            ))}
          </div>
        </section>
        <section>
          <h4>오늘의 일기</h4>
          <div className="inputBox textWrapper">
            <textarea
              placeholder="오늘은 어땠나요?"
              ref={contentRef}
              value={content}
              onChange={(e)=>setContent(e.target.value)}
            />
          </div>
        </section>
        <section>
          <div className="controlBox">
            <MyButton text={"취소하기"} onClick={()=>navigate(-1)} />
            <MyButton text={"작성완료"} type={"positive"} onClick={handleSubmit} />
          </div>
        </section>
      </div>
    </div>
  );
};

export default DiaryEditor;

💬 얼마남지 않았어 😀🙂

profile
필요한 내용을 공부하고 저장합니다.

0개의 댓글