[Udemy] React 기본 - 일기장 만들기(4) CREATE

productuidev·2022년 4월 24일
1

React Study

목록 보기
22/52
post-thumbnail

React 기본 (Project)

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


📌 일기장 만들기 (4) - CREATE

☑️ CREATE

  • Real 일기장 : 진짜로 리스트에 저장되는 작성폼

☑️ 컴포넌트 트리를 그려보기 (유연한 사고력의 증진)

  • 설계 구조 : 부모인 App 컴포넌트 하위에 자식인 작성폼인 DiaryEditor 컴포넌트와 일기목록인 DiaryList 컴포넌트가 쌍둥이처럼 연결되있는 구조 (부모와 자식이 얽힌 계층구조 트리 형태)
  • 같은 레벨끼리는 데이터 주고 받기가 안됨 (DiaryEditor에서 DiaryList로 동일 레벨에서는 데이터가 흐르지 않음)
  • 리액트는 단방향 데이터 흐름 : 위에서 아래로만 데이터를 보낼 수 있음
  • State 활용 : 공통 부모요소로 끌어 올려서 해결
  • 부모인 App 컴포넌트가 배열 형식의 State 데이터를 갖고, data 상태의 값을 DiaryList에게 전달하면서 list를 렌더링하게 하고 이 data라는 상태를 변화시킬 수 있는 상태 변화함수인 setData를 DiaryEditor에게 prop으로 전달해주기만 하면 됨
  • 예) item1 이라는 하나의 일기 데이터(배열)를 가진 상태 > 현재는 하나만 렌더링중일 것임
    새로운 일기가 작성되면 App 컴포넌트가 prop으로 전달한 setData 함수를 호출 (data의 상태를 바꾸는 상태변화함수) > item2로 새로운 아이템 추가되면서 추가된 data가 DiaryList에 prop으로 내려가면서 리렌더링

📌 정리 : EVENT는 위로 DATA는 아래로 흐르는 패턴

  • 데이터를 추가하는 로직을 시뮬레이션해보면 살펴볼 수 있는 사실은 리액트로 만든 컴포넌트들은 이런 식으로 트리 형태 구조를 띰
  • 데이터는 위에서 아래로 움직이는 단방향 데이터 구조
  • 추가/수정/삭제 같은 이벤트들은 이렇게 핸들링하는 setData 상태변화함수를 통해 props으로 전달
  • 전달받은 이런 이벤트들은 아래에서 위로 올라간다 : 새로운 일기를 작성(create)할 경우 이벤트가 발생하고, 이 이벤트는 부모인 App 컴포넌트에서 전달한 setData 함수를 호출해서 데이터를 변화시킴
  • 이 데이터가 변화하게 되면 다시 아래로 떨구기 때문에 아래로 흐르게 되는 패턴
  • state 끌어올리기 : 이렇게 여러 개의 컴포넌트에 엮인 데이터를 공통 부모인 App 컴포넌트의 state로 설정하여 문제 해결하는 방법

☑️ src/App.js

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

function App() {

  // 전역적으로 상태관리
  // 일기데이터, 빈 배열로 시작 (일기가 없는 상태로 출발)
  const [data, setData] = useState([]);
  
  // 데이터가 하나씩 추가될 때마다 id가 1,2,3.. 이렇게 추가되도록
  // 변수처럼 사용할 고유 id가 생성되도록 useRef를 통해 추가
  // 0번 index부터 시작 
  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 // 어떤 DOM도 선택하지 않고 현재 0이란 값을 갖고 있음
    };
    dataId.current += 1; // 새 아이템을 만들고 0번은 1번으로 증가

    // 원래 data를 spread연산자로 쓰고(원래 배열에 있던 data를 여기에)
    // 새로운 아이템을 추가하고 원래 데이터를 이어붙이는 형태
    setData([newItem, ...data]); 
  };

  return (
    <div className="App">
      <DiaryEditor onCreate={onCreate} /> {/*  새 일기 추가를 prop으로 내려준다 */}
      <DiaryList diaryList={data} /> {/* data를 prop으로 내려준다 */}
    </div>
  );
}

export default App;

☑️ src/DiaryEditor.js

import { useState, useRef } from "react";

// DiaryEditor로 내린 onCreate 함수를 prop으로 전달
const DiaryEditor = ({onCreate}) => {

  const [state, setState] = useState({
    author: "", content: "", emotion: 1,
  });

  const authorInput = useRef();
  const contentInput = useRef();

  const handleChangeState = (e) => {
    setState({
      ...state,
      [e.target.name]: e.target.value,
    });
  };

  const handleSubmit = () => {
    if(state.author.length < 1) {
      // alert("작성자는 최소 1글자 이상 입력해주세요.");
      // focus
      authorInput.current.focus();
      return;
    }

    if(state.content.length < 5){
      // alert("일기 본문은 최소 5글자 이상 입력해주세요.");
      // focus
      contentInput.current.focus();
      return;
    }

    // props로 받은 onCreate 함수를 호출하기
    onCreate(state.author, state.content, state.emotion);
    alert("저장 성공");

    // 저장 후 작성폼이 초기화되도록 setState 설정
    // 일기를 한번 더 쓰면 원래 상태로 초기화
    setState({
      author: "", content: "", emotion: 1
    });
  };

  return (
    <div className="DiaryEditor">
      <h2>오늘의 일기</h2>
      <div>
        <input ref={authorInput} name="author" value={state.author} onChange={handleChangeState} />
        <span className="notification">작성자명을 입력하세요 (최소 1글자 이상)</span>
      </div>
      <div>
        <textarea ref={contentInput} name="content" value={state.content} onChange={handleChangeState} />
        <span className="notification">일기 본문을 입력하세요 (최소 5글자 이상)</span>
      </div>
      <div>
        <select name="emotion" value={state.emotion} onChange={handleChangeState}>
          <option value={1}>1</option>
          <option value={2}>2</option>
          <option value={3}>3</option>
          <option value={4}>4</option>
          <option value={5}>5</option>
        </select>
        <span className="notification">감정 점수를 선택하세요 (택 1)</span>
      </div>
      <div>
        <button onClick={handleSubmit}>저장하기</button>
      </div>
    </div>
  );
};

export default DiaryEditor;

📌 정리

  • App 컴포넌트 : DiaryEditor 컴포넌트와 DiaryList 컴포넌트로 함께 사용할 일기 데이터 하나를 가지고 있음

  • 초기 상태
    state로 일기 데이터는 빈 배열로 시작 (처음엔 작성된 일기가 없으므로)
    일기상태변화 함수 setData를 가지고 있음
    이 상태에서는 DiaryList한테 현재 이 App 컴포넌트가 가진 일기 배열 데이터의 상태를 넘겨주기만 하면, DiaryList도 알아서 다시 렌터하고 추가하면 추가, 삭제하면 삭제 (상태 바뀜)

  • 상태 변화
    DiaryEditor 컴포넌트가 일기를 저장해서 반영되면 App 컴포넌트 데이터의 상태가 변화가 일어남
    (DiaryList 컴포넌트가 App 컴포넌트의 data state를 DiaryList로 사용하고 있기 때문)

  • onCreate 함수 : 새로운 일기 추가 (DiaryEditor 컴포넌트에 props으로 전달)
    일기 저장이 일어날 때 onCreate 함수를 호출
    (emotion data, 현재 시간 객체, id ref +1 증가 등등)
    가장 최신 일기 작성 시 가장 위로 배열에 들어감

  • 단방향 데이터 흐름 그리고 역방향 이벤트 흐름으로 state를 끌어올려서 화면에 렌더링


✔️ 중간에 강의 듣다 헥갈릴까봐 클로바노트로 녹음하는데,
헥갈리는 부분은 글로 보면 이해하기 쉽다 🙂 (클로바짱짱!)

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

0개의 댓글