React/2. React 기본 - 간단한 일기장 프로젝트

[인프런] 배열 사용 - 데이터 수정

pancakemaker 2022. 4. 10. 13:22

- App.js

import { useRef, useState } from 'react';
import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList';

function App() {
  //DiaryEditor에서 setData를 이용하여 data의 state를 변화시킨 후
  //그 변화된 data를 DiaryList로 보내서 출력할 것임
  const [data, setData] = useState([]);
  
  //id(index) 증가하도록 DOM에 접근 - useRef() 사용
  const dataId = useRef(0);

  //일기 추가 함수
  const onCreate = (author, content, emotion) => {
    const created_date = new Date().getTime();
    const newItem = {
      author,
      content, 
      emotion,
      created_date,
      id: dataId.current,
    }
    dataId.current+=1;  //index 1씩 증가
    setData([newItem, ...data]);  //새로운 데이터 + 원래 데이터
  };

  //일기 삭제 함수
  const onRemove = (targetId) => {
    console.log(`${targetId}가 삭제되었습니다.`);
    //id가 targetId(삭제한 id)가 아닌 것들만 조회됨
    const newDiaryList = data.filter((it) => it.id !== targetId);
    setData(newDiaryList);
  };

  //일기 수정 함수
  const onEdit = (targetId, newContent) => {
    setData(
      // id가 수정대상이라면 content -> newContent로 교체. 아니라면 원본 data를 넣어줌
      data.map((it) => it.id === targetId ? {...it, content: newContent} : it)
    );
  };

  return (
    <div className="App">
      <DiaryEditor onCreate={onCreate}/>
      {/* Prop(diaryList)을 통해 배열(dummyList)을 DiaryList 컴포넌트로 전달  */}
      {/* onDelete() 함수는 App.js -> DiaryList -> DiaryItem 루트로 Props 전달 -> App.js에서 DairyItem을 안쓰니까!*/}
      <DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data}/>
    </div>
  );
}

export default App;

- DiaryList.js (수정함수를 App.js -> DiaryList -> DiaryItem 경로로 전달)

import DiaryItem from "./DiaryItem";

const DiaryList = ({onEdit, onRemove, diaryList}) => {
    return (
    <div className="DiaryList">
        <h2>일기 리스트</h2>
        <h4>{diaryList.length}개의 일기가 있습니다.</h4>
        <div>
            {diaryList.map((it) => (
                // Prop 전달 {...it} = {id, author, content, created_date, emotion}
                <DiaryItem key={it.id} {...it} onRemove={onRemove} onEdit={onEdit} />
            ))}            
        </div>
    </div>
    );
};

//Prop의 undefined을 방지
DiaryList.defaultProps = {
    diaryList: [],
};

export default DiaryList;

 

- DiaryItem.js

import { useRef, useState } from "react";

const DiaryItem = ({onEdit, onRemove, id, author, content, created_date, emotion}) => {

    //수정중인지 아닌지 여부(boolean)
    const [isEdit, setIsEdit] = useState(false);

    //수정상태가 아닐 때(false-기본값) 수정하기 버튼을 누르면 수정상태(isEdit)은 true로 변해서 textarea가 나타나고
    //수정상태일 때(isEdit === true) 수정하기 버튼을 누르면 수정상태(isEdit)은 false로 변해서 textarea가 사라지고 기본 content만 나타남
    const toggleIsEdit = () => {
        setIsEdit(!isEdit);
    };

    //content -> localContent 로 수정
    const [localContent, setLocalContent] = useState(content);

    //수정 취소시 수정상태 false로 및 content 초기화(원래값으로)
    const handleQuitEdit = () => {
        setIsEdit(false);
        setLocalContent(content);
    };

    //수정완료 버튼 클릭 시 동작될 함수
    const handleEdit = () => {
        if(localContent.length < 5) {
            localContentInput.current.focus();
            return;
        }
        if(window.confirm(`${id}번째 일기를 수정하시겠습니까?`)) {
            onEdit(id, localContent);
            toggleIsEdit(!isEdit);  //수정 완료이므로 수정 상태는 false가 되어 수정form 닫음
        }        
    };

    //useRef 사용하여 수정시 글자수 부족할 경우 focus 시킴
    const localContentInput = useRef();

    //삭제하기 버튼 클릭 시 동작될 함수
    const handleRemove = () => {
        //console.log(id);
        if(window.confirm(`${id}번째 일기를 삭제하시겠습니까?`)) {
            onRemove(id);
        }
    };
    return (
        <div className="DiaryItem">
            <div className="info">
                <span> 작성자: {author} | 감정점수: {emotion}</span>
                <br/>
                <span className="date">{new Date(created_date).toLocaleString()}</span>
            </div>
            <div className="content">
                {/* isEdit이 true면 수정form(textarea)를 보여주고, false면 그냥 content를 보여줌 */}
                {isEdit ? 
                (<><textarea ref={localContentInput} value={localContent} onChange={(e) => setLocalContent(e.target.value)}/></>) : 
                (<>{content}</>)
                }
            </div>

            {isEdit ? 
            // 수정 true
            (<>
            <button onClick={handleQuitEdit}>수정 취소</button>
            <button onClick={handleEdit}>수정 완료</button>
            </>) : 
            // 수정 false
            (<>
            <button onClick={handleRemove}>삭제하기</button>
            <button onClick={toggleIsEdit}>수정하기</button>
            </>)}            
        </div>
    );
};

export default DiaryItem;