[Udemy] React 기본 - 일기장 만들기(6) UPDATE

productuidev·2022년 4월 26일
0

React Study

목록 보기
24/52
post-thumbnail

React 기본 (Project)

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


📌 일기장 만들기 (6) - UPDATE

☑️ UPDATE

  • 리스트 데이터를 동작으로 수정하기
  • 수정하기(수정취소/수정완료) 기능 추가

🛠️ 복습

  • 삭제하기 함수명, Props 수정(onDelete > onRemove)을 통해서 복습
  • App 컴포넌트 > DiaryList 컴포넌트 > DiaryItem 컴포넌트
  • 컴포넌트 트리 전역에 공급되는 함수명, Props을 바꾸면 굉장히 불편할 수 있음 (나중에 해결방법 배울 예정)
// src/App.js
const onRemove = (targetId) => {
    console.log(`${targetId}가 삭제되었습니다`);
    const newDiaryList = data.filter((it) => it.id !== targetId);
    //console.log(newDiaryList);
    setData(newDiaryList);
};
  
<DiaryList onRemove={onRemove} diaryList={date} />


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


// src/DiaryItem.js
const DiaryItem = ({
  onRemove, id, author, content, emotion, created_date }) => {

☑️ 수정 기능 버튼 만들기

  • 일기를 작성하면 수정 기능 버튼이 생성되고, 일기의 내용을 변경할 때 수정 폼과 수정 취소/완료로 버튼이 변경되도록 만들기

☑️ handleRemove 함수 생성

  • 삭제하기 onClick 이벤트 이동 (return 위로 이동)
  • 삭제하기 onClick 이벤트 넣고, 삭제하기 button에 Props으로 전달

☑️ 수정하기 상태 추가

  • useState import
  • useState default는 false
  • isEdit state : boolean(t/f) 값, 수정중인지? 수정중이지 않은지?의 데이터 보관
  • toggleIsEdit 함수 : 호출이 되는 순간 isEdit 값을 반전시킴 (not 논리연산), 수정하기 버튼에 Props으로 전달
    const [isEdit, setIsEdit] = useState(false);
    const toggleIsEdit = () => setIsEdit(!isEdit);
    

☑️ 수정하기 버튼 클릭 시 수정 폼 생성하기 추가

  • content devision 수정
  • isEdit 삼항연산자로 True/False일 때 렌더링될 것을 작성
  • True일 때는 textarea(수정 작성 폼)
  • False일 때는 버튼을 누리지 않은 상태로 돌아감

☑️ 수정 폼 관리 - state로 핸들링하기

  • localContent(textarea의 value) : 수정 폼으로 변경 시
  • setLocalContent (target.value) : 수정 폼 상태 변경 시
  • 수정하지 않는 경우면 원래대로 content
    const [localContent, setLocalContent] = useState();
    const localContentInput = useRef();
    
        <div className="content">
          {isEdit
            ? <><textarea ref={localContentInput}
            value={localContent}
            onChange={(e) => setLocalContent(e.target.value)} /></>
            : <>{content}</>
          }
        </div>

☑️ 수정하기 버튼 클릭 시 수정취소/수정완료 버튼으로 렌더링

  • handleQuitEdit 수정 취소, handleEdit 수정 완료
  • 아닌 경우 원래 버튼으로(삭제하기, 수정하기)
        {isEdit
          ? <>
              <button onClick={handleQuitEdit}>수정 취소</button>
              <button onClick={handleEdit}>수정 완료</button>
            </>
          : <>
              <button onClick={handleRemove}>삭제하기</button>
              <button onClick={toggleIsEdit}>수정하기</button>
            </>
        }

☑️ 수정 폼이 나타났을 때 원본 데이터가 들어오도록 기능 추가

  • 수정 폼을 관리하는 localContent를 원래 content로 바꾸기 (수정하기 이전 상태로 자동으로 나옴)
    const [localContent, setLocalContent] = useState(content);

☑️ 수정 폼 초기화 적용

  • localContent가 훼손되도 초기화됨
    const handleQuitEdit = () => {
      setIsEdit(false); // false
      setLocalContent(content); // 원래대로
    };

☑️ 일기 아이템이 진짜 수정되서 저장되도록 적용

  • 리액트 특성 (EVENT는 위로, DATA는 아래로)
  • 수정 완료 이벤트는 DiaryItem 컴포넌트에서 App 컴포넌트로 가야함
  • Data를 가지고 있는 App 컴포넌트에 수정하는 기능을 넣고, DiaryItem 컴포넌트로 보내주기
  • onEdit 함수 : 수정하기

src/App.js

	// 어떤 id를 가진 일기를 수정할 것인지(targetId) 수정된 데이터가 뭔지(newContent)
    // onEdit 특정 일기 데이터를 수정하는 함수
    
  const onEdit = (targetId, newContent) => {
  
    // setData를 통해서 어떤 값을 전달한다
	setData(
    
    	// 변경 시키는 값 만들기
        // 원본데이터에 map 내장함수 사용 
        // 모든 요소(it)을 순회한 후 새로운 배열을 만들어서 setData에 저장
        // 새로운 배열은? 수정이 완료된 배열을 전달해야 한다
        
      data.map( (it) =>
      
      	// 수정대상이라면?
        it.id === targetId
        
        	// 컨텐츠가 뉴컨텐츠로 바뀜(수정된 배열)로 교체해줄 것인지
          ? { ...it, content: newContent }
          
          	// 수정대상이 아니면 원래있던 배열을 다시 반환할 것인지 (원본값)
          : it
      )
    );
  };

☑️ onEdit 함수 DiaryList 컴포넌트에 전달

src/App.js

  return (
    <div className="App">
      <DiaryEditor onCreate={onCreate} />
      <DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
    </div>
  );

☑️ DiaryList 컴포넌트는 Prop으로 전달

src/DiaryList.js

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

☑️ DiaryItem 컴포넌트는 DiaryList를 통해 전달받기

  • 입력 정확하게 하도록 일기 5글자 이상 작성 하도록 focus 추가 (useRef import)
  • localContentInput 함수 usRef (textarea에도 추가)
  • 수정 완료 버튼 이벤트 추가 (id값과 새로 바뀌는 localContent 전달)
  • 삭제 전 dialog 추가(몇번째 일기 삭제할 것인지 alert으로 묻기)
  • 수정 완료 시 수정 폼(textarea) 닫아주기 (toggleIsEdit)

src/DiaryItem.js

import { useRef, useState } from "react";

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

    const [isEdit, setIsEdit] = useState(false);
    const toggleIsEdit = () => setIsEdit(!isEdit);

    const [localContent, setLocalContent] = useState(content);
    const localContentInput = useRef();

    const handleRemove = () => {
      if (window.confirm(`${id}번 째 일기를 정말 삭제하시겠습니까?`)) {
        onRemove(id);
      }
    };

    const handleQuitEdit = () => {
      setIsEdit(false);
      setLocalContent(content);
    };

    const handleEdit = () => {
      if(localContent.length < 5) {
        localContentInput.current.focus();
        return;
      }

      if(window.confirm(`${id}번 째 일기를 수정하시겠습니까?`)){
        onEdit(id, localContent);
        toggleIsEdit();
      }
    };

    return (
      <div className="DiaryItem">
        <div className="info">
          <span className="author_info">
            | 작성자 : {author} | 감정점수 : {emotion} |
          </span>
          <br />
          <span className="date">{new Date(created_date).toLocaleString()}</span>
        </div>
        <div className="content">
          {isEdit
            ? <><textarea ref={localContentInput} value={localContent} onChange={(e) => setLocalContent(e.target.value)} /></>
            : <>{content}</>
          }
        </div>
        {/* 삭제하기/수정하기 버튼 추가 */}

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

export default DiaryItem;

☑️ 결과


📖 참조하기

컴포넌트 설계 구조와 데이터 전달 흐름


출처 - https://velopert.com/3528


출처 - https://velog.io/@youthfulhps/What-is-Redux-and-why-use-it


출처 - https://jeffgukang.github.io/react-native-tutorial/docs/state-tutorial/redux-tutorial/02-what-is-redux/what-is-redux-kr.html

복잡성


출처 - 생활코딩


💬 작업하다가 결국엔... sourcetree를 설치했다.
예전에 다녔던 회사에서 전임자분이 알려주셨던 그 환경설정이 아직도 익숙하다는 게(흠)

CLI에 익숙해져야 하는데 아직은 GUI가 더 익숙한 나
(사고전환중🙃)

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

0개의 댓글