프로그래밍/React

[React] 초보자를 위한 리액트 강좌 4

라다디 2022. 1. 27. 09:45

 

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>
  );
}

📌 아래 강의의 내용을 정리한 글입니다.