[React] 초보자를 위한 리액트 강좌 4
PUT(수정)
아는 단어인지 체크하는 부분은 상태를 유지하는 게 좋음
-> PUT메소드를 이용하여 단어의 isDone필드를 수정하자
// Words.js
...
function toggleDone() {
fetch(`http://localhost:3001/words/${word.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json", // 보내는 리소스의 타입 (html, 이미지 등)
},
body: JSON.stringify({ // body에는 수정을 위한 정보들, JSON문자열로 변환
...word,
isDone: !isDone,
}),
}).then((res) => {
if (res.ok) {
setIsDone(!isDone);
}
});
}
...
fetch의 두 번째 인자는 객체, 객체는 요청의 옵션들을 의미
DELETE(삭제)
실수로 버튼을 누를 수 있으니 confirm을 띄워서 확인 후 삭제
function del() {
if (window.confirm("삭제 하시겠습니까?")) {
fetch(`http://localhost:3001/words/${word.id}`, {
method: "DELETE",
});
}
}
삭제는 특별히 어떤 정보를 넘겨줄 필요가 없기 때문에 이렇게만 작성하면 됨
삭제를 누른 후 새로 고침 전까지 아무 반응도 없음
-> 삭제된 이후 단어 리스트를 다시 그려주지 않았기 때문
삭제를 하고 ok 응답을 받으면 컴포넌트를 다시 렌더링 하자
이때 null을 리턴해주면 아무것도 표현하지 않음
...
function del() {
if (window.confirm("삭제 하시겠습니까?")) {
fetch(`http://localhost:3001/words/${word.id}`, {
method: "DELETE",
}).then((res) => {
if (res.ok) {
setWord({ id: 0 });
}
});
}
}
if (word.id === 0) {
return null;
}
...
전체 코드
import { useState } from "react";
export default function Word({ word: w }) { // 혹은 props
const [word, setWord] = useState(w); // props.word
const [isShow, setIsShow] = useState(false);
const [isDone, setIsDone] = useState(word.isDone);
function toggleShow() {
setIsShow(!isShow);
}
function toggleDone() {
fetch(`http://localhost:3001/words/${word.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
...word,
isDone: !isDone,
}),
}).then((res) => {
if (res.ok) {
setIsDone(!isDone);
}
});
}
function del() {
if (window.confirm("삭제 하시겠습니까?")) {
fetch(`http://localhost:3001/words/${word.id}`, {
method: "DELETE",
}).then((res) => {
if (res.ok) {
setWord({ id: 0 });
}
});
}
}
if (word.id === 0) {
return null;
}
return (
<tr className={isDone ? "off" : ""}>
<td>
<input type="checkbox" checked={isDone} onChange={toggleDone} />
</td>
<td>{word.eng}</td>
<td>{isShow && word.kor}</td>
<td>
<button onClick={toggleShow}>뜻 {isShow ? "숨기기" : "보기"}</button>
<button onClick={del} className="btn_del">
삭제
</button>
</td>
</tr>
);
}
POST(생성)
단어추가를 위해 CreateWord 컴포넌트 생성
초기 코드
export default function CreateWord() {
return (
<form>
<div className="input_area">
<label>Eng</label>
<input type="text" placeholder="computer" />
</div>
<div className="input_area">
<label>Kor</label>
<input type="text" placeholder="컴퓨터" />
</div>
<div className="input_area">
<label>Day</label>
<select>
<option>1</option>
<option>2</option>
</select>
</div>
<button>저장</button>
</form>
);
}
현재 Day는 하드코딩
실제 DayList를 가져와서 넣기 (만들어둔 커스텀 훅 활용)
import useFetch from "../hooks/useFetch";
export default function CreateWord() {
const days = useFetch("http://localhost:3001/days");
return (
<form>
<div className="input_area">
<label>Eng</label>
<input type="text" placeholder="computer" />
</div>
<div className="input_area">
<label>Kor</label>
<input type="text" placeholder="컴퓨터" />
</div>
<div className="input_area">
<label>Day</label>
<select>
{days.map((day) => (
<option key={day.id} value={day.day}>
{day.day}
</option>
))}
</select>
</div>
<button>저장</button>
</form>
);
}
저장을 누르면 새로고침이 되는데 이건 form태그로 감싸져있기 때문
...
function onSubmit(e) {
e.preventDefault();
}
return (
<form onSubmit={onSubmit}>
...
이벤트를 받은 뒤 preventDefault를 이용해서 기본 기능을 막아줌
@ 저장 버튼을 눌렀을 때 단어와 뜻에 대한 정보를 찍어보기
input창에 적힌 값들을 얻기 위해 useRef라는 훅을 사용
useRef는 돔에 접근할 수 있게 해줌 (ex. 스크롤 위치를 확인하거나 포커스를 주거나 할 때 사용할 수 있음)
import { useRef } from "react";
import useFetch from "../hooks/useFetch";
export default function CreateWord() {
const days = useFetch("http://localhost:3001/days");
function onSubmit(e) {
e.preventDefault();
console.log(engRef.current.value);
console.log(korRef.current.value);
console.log(dayRef.current.value);
}
const engRef = useRef(null);
const korRef = useRef(null);
const dayRef = useRef(null);
return (
<form onSubmit={onSubmit}>
<div className="input_area">
<label>Eng</label>
<input type="text" placeholder="computer" ref={engRef} />
</div>
<div className="input_area">
<label>Kor</label>
<input type="text" placeholder="컴퓨터" ref={korRef} />
</div>
<div className="input_area">
<label>Day</label>
<select ref={dayRef}>
{days.map((day) => (
<option key={day.id} value={day.day}>
{day.day}
</option>
))}
</select>
</div>
<button>저장</button>
</form>
);
}
세 개의 ref객체를 만들어 주고 초기값을 null로 지정
각 태그에 ref를 연결
이렇게 연결하면 돔 요소가 생성된 후 접근 가능
current 속성을 이용하면 해당 요소에 접근할 수 있고 value는 input에 입력된 값을 얻을 수 있음
...
function onSubmit(e) {
e.preventDefault();
console.log(engRef.current.value);
console.log(korRef.current.value);
console.log(dayRef.current.value);
fetch(`http://localhost:3001/words/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
day: dayRef.current.value,
eng: engRef.current.value,
kor: korRef.current.value,
isDone: false,
}),
}).then((res) => {
if (res.ok) {
alert("생성이 완료 되었습니다");
}
});
}
...
잘 생성이 된 것을 볼 수 있음
그런데 매번 생성될 때마다 직접 가보는 방식 말고 생성이 완료 되면 단어 리스트 페이지로 이동하게 해보자
useHistory()는 리액트 라우터에서 지원하는 기능
...
const history = useHistory();
function onSubmit(e) {
e.preventDefault();
fetch(`http://localhost:3001/words/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
day: dayRef.current.value,
eng: engRef.current.value,
kor: korRef.current.value,
isDone: false,
}),
}).then((res) => {
if (res.ok) {
alert("생성이 완료 되었습니다");
history.push(`/day/${dayRef.current.value}`) // 보내고 싶은 주소
}
});
}
...
useHistory로 생성한 history에 주소를 push해주면 해당 페이지로 이동
Link to처럼 a태그를 사용하지 않고 페이지 전환시킬 때 유용
날짜 추가를 위해 CreateDay 컴포넌트 생성
import Day from "./component/Day";
import DayList from "./component/DayList";
import Header from "./component/Header";
import EmptyPage from "./component/EmptyPage";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import CreateWord from "./component/CreateWord";
import CreateDay from "./component/CreateDay";
function App() {
return (
<BrowserRouter>
<div className="App">
<Header />
<Switch>
<Route exact path="/">
<DayList />
</Route>
<Route path="/day/:day">
<Day />
</Route>
<Route path="/create_word">
<CreateWord />
</Route>
<Route path="/create_day">
<CreateDay />
</Route>
<Route>
<EmptyPage />
</Route>
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
import { Link } from "react-router-dom";
export default function Header() {
return (
<div className="header">
<h1>
<Link to="/">토익 영단어(고급)</Link>
</h1>
<div className="menu">
<Link to="/create_word" className="link">
단어 추가
</Link>
<Link to="/create_day" className="link">
Day 추가
</Link>
</div>
</div>
);
}
import { useHistory } from "react-router-dom";
import useFetch from "../hooks/useFetch";
export default function CreateDay() {
const days = useFetch("http://localhost:3001/days");
const history = useHistory();
function addDay(e) {
fetch(`http://localhost:3001/days/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
day: days.length + 1,
}),
}).then((res) => {
if (res.ok) {
alert("생성이 완료 되었습니다");
history.push(`/`);
}
});
}
return (
<div>
<h3>현재 일수 : {days.length}일</h3>
<button onClick={addDay}>Day 추가</button>
</div>
);
}
📌 아래 강의의 내용을 정리한 글입니다.