본문 바로가기

Libraries/React

[React] 데이터 추가하기, 배열 리스트 만들고 추가하기 (일기장 만들기 4탄)

728x90

 

코드가 1탄부터 이어지기 때문에 1탄부터 보고 오셔야 합니다~!

 

 

 

 1탄 

https://dev-ini.tistory.com/73

 

[React] useState로 사용자의 입력 처리하기: input, button에 이벤트 달기 (일기장 만들기 1탄)

이렇게 생긴 일기장을 만들어볼 것이다. 1. 작성자 입력 받기 DiaryEditor.js import { useState } from "react"; // (1) 사용자의 input 입력값을 처리하기 위함 const DiaryEditor = () => { const [author, setAuthor] = useState("

dev-ini.tistory.com

 

 

 2탄 

https://dev-ini.tistory.com/75

 

[React] useRef로 DOM 조작하기 (일기장 만들기 2탄)

useRef로 DOM 조작하는 법을 알아보자 코드가 1탄과 이어지기 때문에 1탄부터 보고 오셔야합니다~ https://dev-ini.tistory.com/73 { const [state, setState] = useState({ author: "", content: "", emotion: 1, }); const handleChangeS

dev-ini.tistory.com

 

 

 3탄 

https://dev-ini.tistory.com/77

 

[React] 데이터 리스트 렌더링 (조회) (일기장 만들기 3탄)

코드가 1탄부터 이어지기 때문에 쭉 보고 오셔야합니다~ 1탄 https://dev-ini.tistory.com/73 { return ( 일기 리스트 ); } export default DiaryList; 2. App.js에서 컴포넌트를 불러온다. import './App.css'; import DiaryEditor f

dev-ini.tistory.com

 

 


 

 

 컴포넌트&데이터 구조 생각해보기 

 

 

 1. 리액트는 기본적으로 트리구조이다. 

 

 

- <App />에서 <DiaryEditor />와 <DiaryList />를 렌더링 한다.
- <DiaryEditor />에서 새로운 일기를 작성한다.
- <DiaryList />에서 일기 리스트들을 보여준다.

 

 

 

 2. 리액트에서는 같은 레벨 끼리는 데이터를 주고 받을 수 없다.  

 

 

- <DiaryEditor />에서 작성된 일기 아이템이  <DiaryList />로 바로 추가 될 수는 없다. 
- 위에서 아래로만 데이터를 보낼 수가 있다. (단방향 데이터 흐름) 

 

 

 

 3. 해결책은 state를 <DiaryEditor />와 <DiaryList />의 공통부모 요소로 끌어올리는 것이다. 

 

 

data는 하나의 일기 데이터이고, [item1]은 하나의 아이템만 렌더링 중이다.

 

- "state 끌어올리기" : 공통부모인 컴포넌트인 <App />에 state를 설정하여 문제를 해결한다.
- <App />이 일기데이터를 배열 형식의 state를 가지고 있게끔 한다.

- state인 data 값을 <DiaryList />에 전달하면서 리스트를 렌더링한다.
- 그리고, (state를 변화시킬수 있는 상태변환함수인) setdata를 <DiaryEditor />에 props로 전달한다.

 

 

 

 4. <DiaryEditor />에서 새로운 일기를 작성 하면 setData를 호출한다. 

 

 

- setData는 새로운 item을 추가할 수 있도록 data의 값을 바꾼다.
- <DiaryList />에 props로 추가된 data까지 포함한 새로운 data가 내려간다.
- <DiaryList />는 전달받은 props가 바껴서 리렌더링된다.
- 결론적으로 [item1, item2] 리스트를 렌더링하게된다.

 

 

 

 5. Event와 Data의 흐름 

 

 

< Event의 흐름 >
- 추가, 수정, 삭제와 같은 이벤트들은 setData 같은 함수를 props로 전달해서 아래에서 위로 올라가는 구조이다.
- <DiaryEditor />에서 새로운 일기를 작성하면 "생성"이라는 이벤트가 발생한다.
- 이벤트는 <App />에서 전달한 상태변화함수(setData)를 호출해서 data를 변화시킴으로써 이뤄진다.

 

< Data의 흐름 >
- data는 위에서 아래로만 움직인다.
- data가 변화하게 되면 다시 아래로 떨군다.

 

 


 

 

 위의 설명을 토대로 일기장 리스트 추가 기능 구현해보기 

 

 App.js 

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


function App() {

  
  const [data, setData] = useState([]);  // (1) 상태변화함수 만들기, 빈배열로 시작 (일기 없는 상태로 출발)

  const dataId = useRef(0); // (7) id 인덱스 추가-> 변수 처럼 사용 필요 -> useRef 사용

  const onCreate = (author, content, emotion) => {; // (3) data를 새로 생성하는 함수 만들기 - 새로운 일기 추가하는 함수
  const created_date = new Date().getTime(); // (5) 현재시간
  const newItem = { // (6) 새로운 일기 아이템으로 추가돼야하는것
  author, 
  content,
  emotion,
  created_date,
  id: dataId.current 
  };
    dataId.current += 1; // (8) 하나 만들면 id가 +1 증가해야함
    setData([newItem,...data]); // (9) 원래 아이템에서 맨 위에 새로운 일기 올라오게 하기
  }

return (
    <div className="App">
<DiaryEditor onCreate={onCreate}/> {/* (4) (글쓰기로 data 업데이트, 생성) */}
<DiaryList diaryList={data} /> {/* (2) 리스트에 data 상태 전달 */} 
    </div>
  );
};

export default App;

 

 

 DiaryEditor.js 

import {useState} from "react";
import {useRef} from "react"; 
import './DiaryEditor.css';

const DiaryEditor = ({onCreate}) => { // (10) onCreate 함수를 props로 받음
  const authorInput = useRef();
  const contentInput = useRef();


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

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

  };
 

  // 입력 강제하기
  const handleSubmit = ()=>{
    if(state.author.length < 1){
      // focus
      authorInput.current.focus();
      return;
    } 

    if(state.content.length < 5){
      contentInput.current.focus();
      // focus
      return;
    } 
     
    onCreate(state.author, state.content, state.emotion);  // (11) 일기 저장이 발생했을때 onCreate 함수 호출
    alert("저장성공");
    setState({ // (12) 기본값으로 초기화
      author: "",
      content: "",
      emotion: 1,
    })
  };

  

  return (
    <div className="DiaryEditor">
      <h2>오늘의 일기</h2>
      <div>
        <input ref={authorInput} name="author" value={state.author} onChange={handleChangeState} />  
      </div>
      <div>
        <textarea ref={contentInput} name="content" value={state.content} onChange={handleChangeState} />
      </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>
      
      </div>
      <div> 
      <button onClick={handleSubmit}>일기 저장하기</button> 
      </div>
    </div>
  );
};

export default DiaryEditor;

 

 


 

 

 정리 

 

(1) <App />가 <DiaryEditor />와 <DiaryList />가 함께 사용할  일기데이터를 state로 가지고 있다.

(2) 일기데이터는 빈배열로 시작한다. (일기가 없는 상태로 시작)

(3) 상태변화함수인 setData도 있다.

(4) <DiaryList />한테는 <App />가 가진 state를 넘겨주기만 하면 된다.

(5) state가 바뀌면 리렌더링이 된다. (추가, 삭제 등)

(6) <DiaryEditor />에서 일기를 저장했을 때 일기 리스트에 반영되게 만들려면,
<App />의 data의 상태를 변화시키면 된다. (<DiaryList />가 일기장 리스트로 <App />의 state를 사용하고 있기 때문이다.)

(7) <App />에 onCreate라는 함수를 만든다. (onCreate는 새로운 일기를 추가하는 함수)

(8) 이 onCreate함수는 작성자, 내용, 감정 점수를 전달 받는다.

(9) <DiaryEditor />에서는< App />로부터  onCreate라는 함수를 props로 받아서, 일기 저장이 일어났을때 onCreate함수를 호출한다.

(10) 동시에 현재 컴포넌트가 가지고 있는 작성자, 내용, 감정 점수 데이터를 < App />으로 전달한다.

(11) < App />이 받아서 가지고 오면, setData 함수를 호출해서 "원래있던 데이터+새로운 데이터"를 리스트 렌더링한다.

 

 


 

 

 완성본 

 

 

 

728x90