useState 사용해서 메모장 구현하기
최근 실시간 강의때 민태강사님께 배웠던 예전 리액트 라우팅 방식도 사용해보고 싶어서 아직은 단일 페이지인 메모장에 도입해보았다.
import "./App.css"
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
import { useState } from "react"
import { Header } from "./components/Header"
import { List } from "./components/List"
import { AddItem } from "./components/AddItem"
const App = () => {
const date = new Date()
const currentDate = date.toISOString().substring(0, 10)
const [todays, setTodays] = useState(currentDate)
const [addItem, setAddItem] = useState(false)
const [title, setTitle] = useState("")
const [content, setContent] = useState("")
const [label, setLabel] = useState("Memo")
const [notes, setNotes] = useState([
{
id: 1,
label: "Memo",
title: "벨로그 내용",
content: "하루에 공부한 내용 정리하면서 복습하고 업로드하기",
todays: "2024.07.14",
}
])
const handleToday = () => { // 오늘 날짜 출력 함수
setTodays(currentDate)
}
const deleteItem = (id) => { // 메모 삭제 함수
let newNotes = notes.filter((item) => item.id !== id)
setNotes(newNotes)
}
const addItemModal = () => { // 메모 추가하는 모달 띄우기 함수
addItem ? setAddItem(false) : setAddItem(true)
if (addItem) { // 초기화
setTitle("")
setContent("")
setLabel("Memo")
}
}
const handleTitle = (e) => {
setTitle(e.target.value)
}
const handleContent = (e) => {
setContent(e.target.value)
}
const handleLabel = (e) => {
setLabel(e.target.value)
}
const addNote = (e) => { // 메모 추가 함수
e.preventDefault()
if (title !== "" && content !== "") {
const newNote = { id: crypto.randomUUID(), label, title, content, todays }
const newNotes = [...notes, newNote]
setNotes(newNotes)
addItemModal()
}
}
return (
<main>
{addItem ? (
<AddItem
addNote={addNote}
title={title}
content={content}
handleContent={handleContent}
handleTitle={handleTitle}
handleLabel={handleLabel}
addItemModal={addItemModal}
todays={todays}
handleToday={handleToday}
/>
) : null}
<Router>
<Routes>
<Route path="/" element={<Header addItemModal={addItemModal} />}>
<Route index element={<List notes={notes} deleteItem={deleteItem} addItemModal={addItemModal} />} />
</Route>
</Routes>
</Router>
</main>
)
}
export default App
Route path="/" 인 부분에 Header컴포넌트가 들어가고 List컴포넌트를 감쌌다. Header컴포넌트에 있는 Outlet
부분엔 감싸진 element 요소(List)가 자리하게 된다.
import { ReactComponent as AddIcons } from "../assets/add-icon.svg"
import { ReactComponent as SearchIcon } from "../assets/search-icon.svg"
import { Outlet } from "react-router-dom"
import "./Header.css"
export const Header = ({ addItemModal }) => {
return (
<>
<header>
<div>
<SearchIcon />
<input placeholder="Search" />
</div>
<button onClick={() => addItemModal()}>
<AddIcons />
<span>Add</span>
</button>
</header>
<h1>Your notes</h1>
<Outlet /> // Route의 element 공간
</>
)
}
메모장 리스트 생성 컴포넌트
import "./List.css"
import { Item } from "./Item"
export const List = ({ notes, deleteItem, addItemModal }) => {
return (
<ul>
{notes.map((item) => {
return <Item
item={item}
key={item.id}
deleteItem={deleteItem}
addItemModal={addItemModal}
/>
})}
</ul>
)
}
메모 생성 컴포넌트
import "./Item.css"
import { ReactComponent as DeleteIcon } from "../assets/delete-icon.svg"
import { ReactComponent as EditIcon } from "../assets/edit-icon.svg"
export const Item = ({ item, deleteItem, addItemModal }) => {
return (
<li>
<div>
<span className={item.label}>{item.label}</span>
<div>
<button>
<EditIcon onClick={() => addItemModal()} />
</button>
<button>
<DeleteIcon onClick={() => deleteItem(item.id)} />
</button>
</div>
</div>
<p>{item.title}</p>
<p>{item.content}</p>
<span className="today">{item.todays}</span>
</li>
)
}
메모 추가 컴포넌트
import "./AddItem.css"
export const AddItem = ({
todays,
handleToday,
addNote,
title,
content,
handleContent,
handleTitle,
handleLabel,
addItemModal,
}) => {
return (
<form onSubmit={addNote}>
<div>
<p onChange={handleToday}>{todays}</p>
<div>
<label htmlFor="Memo">
<input type="radio" name="label" id="Memo" value="Memo" onChange={handleLabel} defaultChecked />
<span>Memo</span>
</label>
<label htmlFor="Home">
<input type="radio" name="label" id="Home" value="Home" onChange={handleLabel} />
<span>Home</span>
</label>
</div>
<div className="titleWrap">
<p>제목</p>
<input type="text" value={title} onChange={handleTitle} />
</div>
<div className="contentWrap">
<p>내용</p>
<textarea type="text" value={content} onChange={handleContent} />
</div>
<div>
<button type="submit">추가</button>
<button onClick={() => addItemModal()}>취소</button>
</div>
</div>
</form>
)
}
추가 버튼에 submit타입을 넣고 폼태그에 onSubmit이벤트를 준다. 각 value는 label, title, content이고 onChange 핸들러는 각각의 state를 업데이트한다.