[React] 간단한 일기장 만들기(3) - 리스트 추가, 삭제하기

jwww·2023년 8월 16일
0

React

목록 보기
7/11

오늘 목표

(1) 리스트 데이터 추가하기

  • 배열을 이용한 React의 리스트 아이템을 동적으로 추가된다.

(2) 리스트 삭제하기

  • DiaryItem에 삭제 버튼을 추가하고 버튼을 클릭하면 리스트 아이템이 삭제된다.

1. 리스트 추가하기

리스트에 데이터를 추가하기 위해서는 DiaryEditor에서 글이 저장되면 DiaryList에 나타나야한다.

하지만 React에서는 같은 레벨의 데이터를 주고 받을 수 없다.
(부모에서 자식으로만 데이터를 보낼 수 있음. 단방향 데이터 흐름)

그러므로 공통 부모인 App 컴포넌트가 일기데이터를 state를 가지고 state 값을 DiaryList에게 전달하고, 상태변화 함수인 setState를 DiaryEditor에게 prop으로 전달하면된다.


(1) App 컴포넌트에서 일기 데이터를 가지는 state인 date를 만든다.

(2) DiaryList 컴포넌트에게는 일기 배열 date를 전달한다.

(3) 새로운 일기를 추가하는 함수, onCreate라는 함수를 만든다.
일기 리스트를 변화시키려면 App 컴포넌트의 date를 변화시키면 되기에 onCreate에서 setData를 변화시킨다.

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

function App() {
  // 전역적으로 data를 관리할 state
  const [data, setData] = useState([]); 

  const dataId = useRef(0);

  // 일기 배열에 새로운 일기를 추가하는 함수
  const onCreate = (author, content, emotion) => {
    const created_date = new Date().getTime();
    const newItem = {
      id: dataId.current,
      author,
      content,
      emotion,
      created_date,
    }
    dataId.current += 1;
    setData([newItem, ...data])
  }

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

export default App;
  • const [data, setData] = useState([]);
    : 일기배열을 저장할거기 때문에 초기값은 배열로 저장.

  • const dataId = useRef(0);
    : 아이템마다 id가 있어야하는데 useRef(0)로 초기값 0으로 만들어준다.

  • dataId.current += 1;
    : 아이템이 추가될 때마다 1씩 더해서 새 id를 부여한다.

  • setData([newItem, ...data])
    : 새 아이템을 추가하고 기존 아이템 (...data)을 뿌려줌.


(4) DiaryEditor에서 일기 저장이 일어났을 때, onCreate 함수를 호출한다.
호출된 onCreate 함수는 작성된 작성자 (state.author), 본문 (state.content) 등을 받아 newItem을 만들고 setData는 data에 newItem을 추가한다.

  • DiaryEditor.js
import React, { useState, useRef } from 'react';
import styles from './DiaryEditor.module.css';

export default function DiaryEditor({onCreate}) {
    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) {
            alert("작성자는 최소 1글자 이상 입력해주세요.");
            authorInput.current.focus();
            return;
        }
        if (state.content.length < 5) {
            alert("일기 본문은 최소 5글자 이상 입력해주세요.");
            contentInput.current.focus();
            return;
        }

        //state에 값이 저장되어 있으므로
        onCreate(state.author, state.content, state.emotion); 
        alert("저장완료!");
        // 저장되면 글이 남아있기 때문에 초기화
        setState({
            author: "",
            content: "",
            emotion: 1,
        });
    }

    return <section className={styles['diary-editor']}>
        <h2>오늘의 일기</h2>
        <div className={styles.inner}>
            <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 className={styles['score-area']}>
                <label className={styles.label}>오늘의 감정점수 : </label>
                <select className={styles.score} 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 className={styles['save-btn']} onClick={handleSubmit}>일기 저장하기</button>
            </div>
        </div>
    </section>
}
  • onCreate(state.author, state.content, state.emotion);
    : 작성자, 본문, 감정점수를 받아와 onCreate 실행.

  • setState({ author: "", content: "", emotion: 1});
    : 저장이 성공한 후에도 작성한 글이 Editor에 남아있기 때문에 초기화를 해준다.


2. 리스트 삭제하기

(1) DiaryItem에 삭제 버튼을 만든다.

(2) 최상위 컴포넌트 App에서 onDelete 함수를 만든다.

(3) onDelete 함수에 삭제할 요소인 targetId, targetId를 제외한 배열을 반환하는 newDiaryList를 만든다.

(4) setData로 newDiaryList를 전달한다.
상태가 변화하면서 리렌더되므로 targetId를 제외한 새로운 배열이 만들어진다.

  • App.js
// 리스트 아이템 삭제
 const onDelete = (targetId) => {
    const newDiaryList = data.filter((item)=> item.id !== targetId);
    setData(newDiaryList);
}
  • const onDelete = (targetId)
    : 선택한 아이템의 id를 매개변수로 받는다.

  • data.filter((item)=> item.id !== targetId)
    : 삭제를 선택한 targetId를 제외한 아이템들의 모든 아이디를 반환한다.


(5) onDelete를 DiaryItem 컴포넌트까지 내려줘야하므로 DiaryItem의 부모인 DiaryList에 onDelete를 전달한다.
  • App.js
<DiaryList onDelete={onDelete} diaryList={data}/>
  • DiaryList.js
export default function DiaryList({ onDelete, diaryList }) {
	...
}
  • DiaryItem.js
export default function DiaryItem({ id, author, content, emotion, created_date, onDelete }) {
	...
}

(6) DiaryItem의 삭제 버튼이 클릭했을 때 삭제가 되기때문에 onClick 이벤트에 onDelete 함수 연결. 이전에 정말 삭제할 건지 확인이 필요하므로 if문도 작성.
  • DiaryItem.js
<button 
    className={styles['remove-btn']}
  	onClick={()=> {
       if (window.confirm(`${id}번째 일기를 삭제하시겠습니까?`)) {
            onDelete(id);
       }
    }}
>
  삭제하기
</button>
  • if (window.confirm(${id}번째 일기를 삭제하시겠습니까?))
    : 삭제하기 전 확인.

profile
퍼블리셔 공부 블로그 입니다

0개의 댓글