상세 컨텐츠

본문 제목

MEMO 프로젝트 (함수형) - DB 연결 없는 Ver.

21-22/21-22 리액트 마스터

by 도리에몽 2021. 12. 1. 23:00

본문

728x90

DB 연결 없는 Version

const [memos, setMemos] = useState([
    {
      id: 0,
      title: '제목1',
      author: '익명1',
      content: '메모1'
    },
    {
      id: 1,
      title: '제목2',
      author: '익명2',
      content: '메모2'
    },
    {
      id: 2,
      title: '제목3',
      author: '익명3',
      content: '메모3'
    },
  ]);

⇒ 배열에 데이터 저장하여 사용

<동작 원리>

CREATE

//App.js
  const nextId = useRef(3);
  const onInsert = useCallback(
    (title, author, content) => {
      const memo = {
        id: nextId.current,
        title,
        author,
        content
      };
      setMemos(memos.concat(memo));
      nextId.current += 1;
    },
    [memos],
  );

<Modal state={modalState} closeModal={closeModal} onInsert={onInsert} />

onInsert( )

  • memo에 title, author, content 값 입력
  • setMemos()를 이용하여 memos 배열에 값 저장

→ Modal.js에 전달

//Modal.js
const onSubmit = useCallback(
    e => {
      onInsert(title, author, content);
      e.preventDefault();
      setTitle('');
      setAuthor('');
      setContent('');

      closeModal();
    },
    [onInsert, title, author, content],
  )

등록 버튼 클릭 시 - onSubmit()

  • onInsert() <- form에 작성한 title, author, content값 전달
  • Modal의 초기값 공백으로 설정
  • closeModal()

READ

1. 데이터 목록 보여주기

{memos.map(memo => (
  <td className='cell' onClick={() => openReModal(memo.id)}>
    <div className='inner'>
      <h2>{memo.title}</h2>
      <h5>{memo.author}</h5>
      <h4>{memo.content}</h4>
    </div>
  </td>
))}

2. ReModal 창에 기존 정보 값 표시

//App.js
const [clickmemo, setClickmemo] = useState({
    id: '',
    title: '',
    author: '',
    content: '',
  })

const openReModal = (id) => {
    setClickmemo({
      id: id,
      title: memos[id].title,
      author: memos[id].author,
      content: memos[id].content,
    });
    setReModalState(true);
  }

<ReModal state={reModalState} closeModal={closeReModal} onRemove={onRemove} onUpdate={onUpdate} />

openReModal 시, 클릭한 메모에 대한 정보를 clickMemo에 저장

  → ReModal.js에 clickmemo값을 data로 전달

//ReModal.js
const [title, setTitle] = useState(data.title);
const [author, setAuthor] = useState(data.author);
const [content, setContent] = useState(data.content);

useEffect(() => {
    console.log(title, author, content);
    setTitle(data.title);
    setAuthor(data.author);
    setContent(data.content);
  }, [data]);

useState 이용하여 title, author, content에 초기값 지정

  → useEffect 이용해서 data 값이 변경되면 값을 변경해줌

🚨 ISSUE 🚨
useState로 초기값 설정을 주었으나, 의도한 대로 표시되지 않음
클래스형으로 표현할 때는 componentWillReceiveProps()를 사용했으나, 함수형은 이를 사용할 수 없음.
따라서, useEffect를 이용해서 해결하기

UPDATE

//App.js
const onUpdate = useCallback(
    (id, change_memo) => {
      closeReModal();
      setMemos(memos.map((memos, index) =>
        index === id ? { id, ...change_memo } : memos
      ))
    }, [memos],
  )

<ReModal state={reModalState} closeModal={closeReModal} data={clickmemo} onRemove={onRemove} onUpdate={onUpdate} />

onUpdate()

  • 해당 id값과 변경된 내용의 memo값으로 memo 값 변경
💡 spread 연산자(...)
    ⇒ state 관리 시, 불변성 유지한 채 일부 특정 값만 변경(update) 시, 사용
    const objA = { a:1, b:2, c:3 };
    const objB = { ...objA, b:4 }; //objA 복제 후, b속성만 덮어쓰기

→ ReModal.js로 전달

//ReModal.js
const handleUpdate = useCallback(e => {
    onUpdate(data.id, {
      title,
      author,
      content
    })
    e.preventDefault();
    closeModal();
  })

handleUpdate()

  • onUpdate 함수에 변경된 title, author, content 값 전달
  • closeModal()

DELETE

//App.js
const onRemove = useCallback(
    id => {
      closeReModal();
      setMemos(memos.filter(memo => memo.id !== id));
    },
    [memos],
  );

<ReModal state={reModalState} closeModal={closeReModal} data={clickmemo} onRemove={onRemove} onUpdate={onUpdate} />

onRemove()

  • filter를 이용하여 해당 id 값의 메모 삭제

→ ReModal.js에 전달

//ReModal.js
<button type="button" onClick={() => onRemove(data.id)}>
  <p>삭제하기</p>
</button>

전체 코드

▶App.js

더보기
import React, { useState, useRef, useCallback } from 'react';
import './App.css';
import plus from './plus.png';
import Modal from './components/Modal';
import ReModal from './components/ReModal';

function App() {
  const [memos, setMemos] = useState([
    {
      id: 0,
      title: '제목1',
      author: '익명1',
      content: '메모1'
    },
    {
      id: 1,
      title: '제목2',
      author: '익명2',
      content: '메모2'
    },
    {
      id: 2,
      title: '제목3',
      author: '익명3',
      content: '메모3'
    },
  ]);
  const [modalState, setModalState] = useState(false);
  const [reModalState, setReModalState] = useState(false);
  const [clickmemo, setClickmemo] = useState({
    id: '',
    title: '',
    author: '',
    content: '',
  })
  // 모달창 열기
  const openModal = () => {
    setModalState(true);
  }
  // 모달창 닫기
  const closeModal = () => {
    setModalState(false);
  }
  // CREATE
  const nextId = useRef(3);
  const onInsert = useCallback(
    (title, author, content) => {
      const memo = {
        id: nextId.current,
        title,
        author,
        content
      };
      setMemos(memos.concat(memo));
      nextId.current += 1;
    },
    [memos],
  );

  //re모달창 열기 (READ)
  const openReModal = (id) => {
    setClickmemo({
      id: id,
      title: memos[id].title,
      author: memos[id].author,
      content: memos[id].content,
    });
    setReModalState(true);
  }
  const closeReModal = () => {
    setReModalState(false);
  }
  //DELETE
  const onRemove = useCallback(
    id => {
      closeReModal();
      setMemos(memos.filter(memo => memo.id !== id));
    },
    [memos],
  );

  //UPDATE
  const onUpdate = useCallback(
    (id, change_memo) => {
      closeReModal();
      setMemos(memos.map((memos, index) =>
        index === id ? { id, ...change_memo } : memos
      ))
    }, [memos],
  )

  return (
    <div className="Container">
      <div className='App'>
        <h1>메모장</h1><br /><br />
        <table>
          <tbody>
            <tr className='trList'>
              {memos.map(memo => (
                <td className='cell' onClick={() => openReModal(memo.id)}>
                  <div className='inner'>
                    <h2>{memo.title}</h2>
                    <h5>{memo.author}</h5>
                    <h4>{memo.content}</h4>
                  </div>
                </td>
              ))}
              <td className='cell'>
                <div className="inner" onClick={openModal}>
                  <img src={plus} className='picture' alt='logo' />
                </div>
              </td>
            </tr>
          </tbody>
        </table>
        <main className='App'>
          {/* 모달 연결 */}
          <Modal state={modalState} closeModal={closeModal} onInsert={onInsert} />
          <ReModal state={reModalState} closeModal={closeReModal} data={clickmemo} onRemove={onRemove} onUpdate={onUpdate} />
        </main>
      </div>
    </div>
  );
}

export default App;

▶Modal.js

더보기
import React, { useState, useCallback } from 'react';
import './Modal.scss';

function Modal({ state, closeModal, onInsert }) {
  const [title, setTitle] = useState('');
  const [author, setAuthor] = useState('');
  const [content, setContent] = useState('');

  const onChangeTitle = useCallback(e => {
    setTitle(e.target.value);
  });
  const onChangeAuthor = useCallback(e => {
    setAuthor(e.target.value);
  });
  const onChangeContent = useCallback(e => {
    setContent(e.target.value);
  });
  const onSubmit = useCallback(
    e => {
      onInsert(title, author, content);
      e.preventDefault();
      setTitle('');
      setAuthor('');
      setContent('');

      closeModal();
    },
    [onInsert, title, author, content],
  )

  return (state ?
    <React.Fragment>
      <div className="Modal-overlay" >
        <div className="Modal">
          <h1 className="title">메모를 기록하세요!</h1>
          <form onSubmit={onSubmit}>
            <div className="content">
              <h4>
                <input type="text" placeholder="아이디를 입력하세요"
                  name="author" value={author}
                  onChange={onChangeAuthor}></input>
              </h4>
              <br />
              <h4>
                <input type="text" placeholder="제목을 입력하세요"
                  name="title" value={title}
                  onChange={onChangeTitle}></input>
              </h4>
              <textarea name="content" value={content}
                onChange={onChangeContent}>
              </textarea>
            </div>
            <div className="button-wrap">
              <button type="submit">
                <p>메모 추가하기</p>
              </button>
              <button type="submit" onClick={e => closeModal(e)}>
                <p>닫기</p>
              </button>
            </div>
          </form>
        </div>
      </div>
    </React.Fragment>
    : null);
};

export default Modal;

▶ReModal.js

더보기
import React, { useState, useCallback, useEffect } from 'react';
import './Modal.scss';

function ReModal({ state, data, onRemove, onUpdate, closeModal }) {
  const [title, setTitle] = useState(data.title);
  const [author, setAuthor] = useState(data.author);
  const [content, setContent] = useState(data.content);

  const onChangeTitle = useCallback(e => {
    setTitle(e.target.value);
  });
  const onChangeAuthor = useCallback(e => {
    setAuthor(e.target.value);
  });
  const onChangeContent = useCallback(e => {
    setContent(e.target.value);
  });

  useEffect(() => {
    console.log(title, author, content);
    setTitle(data.title);
    setAuthor(data.author);
    setContent(data.content);
  }, [data]);

  const handleUpdate = useCallback(e => {
    onUpdate(data.id, {
      title,
      author,
      content
    })
    e.preventDefault();

    closeModal();
  })

  return (state ?
    <React.Fragment>
      <div className="Modal-overlay" >
        <div className="Modal">
          <h1 className="title">메모를 기록하세요!:D</h1>
          <form onSubmit={handleUpdate}>
            <div className="content">
              <h4>
                <input type="text" placeholder="아이디를 입력하세요"
                  name="author" value={author}
                  onChange={onChangeAuthor}></input>
              </h4>
              <br />
              <h4>
                <input type="text" placeholder="제목을 입력하세요"
                  name="title" value={title}
                  onChange={onChangeTitle}></input>
              </h4>
              <textarea name="content" value={content}
                onChange={onChangeContent}>
              </textarea>
            </div>
            <div className="button-wrap">
              <button type="submit">
                <p>수정하기</p>
              </button>
              <button type="button" onClick={() => onRemove(data.id)}>
                <p>삭제하기</p>
              </button>
            </div>
          </form>
        </div>
      </div>
    </React.Fragment>
    : null);
};
export default ReModal;

 

728x90

관련글 더보기