31일차(MyApp)

Undong·2023년 5월 6일
0

항해구구

목록 보기
31/52

31일차

오늘은 axios에 대한 과제를 페어분과 함께 마무리하고 제출하였다.

그래서 오늘은 과제를 리뷰하는 시간을 가지겠다.

과제

axios를 이용하여 나만의 앱을 만드는 프로젝트이다.

axios를 이용하여 todolist 앱을 만들기로 정하여 만들었다.

파일 구성

이번에 과제를 통하여 커스텀 훅도 만들어보았다. 컴포넌트처럼 훅들을 편리하게 사용할 수 있었다.
또한 axios를 통하여 조회, 추가, 삭제, 수정 기능을 사용하여 todolist를 잘 사용하게 만들었다. 또한 리액트 쿼리를 사용하여 상태관리를 하였다.

pages

check

import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import { Link } from "react-router-dom";
import Button from "../components/allbtn";
import useTodos from "../hooks/customcheck";

function Check() {
  const navigate = useNavigate();
  const { todos, isLoading, error, deleteTodo } = useTodos();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <St>
      <Sthead>
        <div
          onClick={() => {
            navigate("/");
          }}
        >
          🏠
        </div>
        <div>undong</div>
      </Sthead>
      <Stdiv>할 일</Stdiv>
      {todos.map((todo) => (
        <StBox key={todo.id}>
          <Link to={`/work/${todo.id}`}>상세보기</Link>
          <Stdiv>제목 : {todo.title}</Stdiv>
          <Stdiv>작성자 : {todo.writer}</Stdiv>
          <Button onClick={() => deleteTodo.mutate(todo.id)}>삭제하기</Button>
        </StBox>
      ))}
    </St>
  );
}

export default Check;

const Sthead = styled.header`
  // 헤드
  background: #696969;
  align-items: center;
  display: flex;
  height: 50px;
  justify-content: space-between;
  padding: 0 20px;
  font-size: 20px;
  color: white;
  cursor: pointer;
`;

const StBox = styled.div`
  margin: 20px;
  display: flex;
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-pack: justify;
  justify-content: space-between;
  flex-direction: row;
  padding: 0px 20px;
  height: 120px;
  border: 1px solid gray;
  background-color: rgb(255, 255, 255);
  border-radius: 8px;
  margin-top: 30px;
`;

const Stdiv = styled.h2`
  color: #696969;
  margin: 20px;
`;

const St = styled.div`
  background-color: #f5f5f5;
  height: 100vh;
`;

Detail

import React from "react";
import { useState } from "react";
import styled from "styled-components";
import { useNavigate, useParams } from "react-router-dom";
import { Link } from "react-router-dom";
import Button from "../components/allbtn";
import StheadComponent from "../components/header";
import { useQuery, useMutation } from "react-query";
import { getData, patchData } from "../api/todo";

function Detail() {
  const navigate = useNavigate();
  const [editTodo, setEditTodo] = useState({
    title: "",
    body: "",
  });
  // get
  const { id } = useParams();

  const {
    data: todo,
    isLoading,
    error,
  } = useQuery(["todo", id], () => getData(id));

  const editTodoMutation = useMutation((edit) => patchData(todo.id, edit));

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  const onClickEditButtonHandler = async (edit) => {
    try {
      await editTodoMutation.mutateAsync(edit);
    } catch (error) {
      alert(error);
    }
  };

  return (
    <St>
      <div>
        <StheadComponent onClick={() => navigate("/")} />
      </div>
      {todo && (
        <div>
          <StBox>
            <Stdiv>id : {todo.id}</Stdiv>
            <Stdiv>title : {todo.title}</Stdiv>
            <Stdiv>body : {todo.body}</Stdiv>
            <Link to={"/work"}>목록으로</Link>
          </StBox>
          <div>
            <Stform>
              <Stdiv>제목</Stdiv>
              <Stinput
                type="text"
                placeholder="title 입력"
                onChange={(ev) => {
                  setEditTodo({
                    ...editTodo,
                    title: ev.target.value,
                  });
                }}
              />
              <Stdiv>내용</Stdiv>
              <StTextarea
                type="text"
                placeholder="body 수정"
                onChange={(ev) => {
                  setEditTodo({
                    ...editTodo,
                    body: ev.target.value,
                  });
                }}
              />
              <Button
                // type='button' 을 추가해야 form의 영향에서 벗어남
                type="button"
                onClick={() => onClickEditButtonHandler(editTodo)}
              >
                수정하기
              </Button>
            </Stform>
          </div>
        </div>
      )}
    </St>
  );
}

export default Detail;

const StBox = styled.div`
  margin: 20px;
  display: flex;
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-pack: justify;
  justify-content: space-between;
  flex-direction: row;
  padding: 0px 20px;
  height: 120px;
  border: 1px solid gray;
  background-color: rgb(255, 255, 255);
  border-radius: 8px;
  margin-top: 30px;
`;

const Stinput = styled.input`
  box-sizing: border-box;
  height: 46px;
  outline: none;
  border-radius: 8px;
  padding: 0px 12px;
  font-size: 20px;
  border: 1px solid black;
`;

const StTextarea = styled.textarea`
  box-sizing: border-box;
  height: 200px;
  outline: none;
  border-radius: 8px;
  padding: 12px;
  font-size: 20px;
  border: 1px solid black;
  margin-bottom: 200px;
`;

const Stform = styled.form`
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin: 0px 20px 0px;
`;
const Stdiv = styled.h2`
  margin: 20px;
  color: #696969;
`;
const St = styled.div`
  background-color: #f5f5f5;
  height: 100vh;
`;

Home

import React from "react";
import { useNavigate } from "react-router";
import styled from "styled-components";
import StheadComponent from "../components/header";

function Home() {
  const navigate = useNavigate();
  return (
    <St>
      <div onClick={() => navigate("/")}>
        <StheadComponent />
      </div>
      <Stlayout>
        <Stborder
          onClick={() => {
            // 미정
            navigate("/work/write");
          }}
        >
          <h1>Write</h1>
          <h2>👉 Click!</h2>
        </Stborder>
        <Stborder
          onClick={() => {
            // 미정
            navigate("/work");
          }}
        >
          <h1>Check</h1>
          <h2>👉 Click!</h2>
        </Stborder>
      </Stlayout>
    </St>
  );
}

export default Home;

const St = styled.div`
  background-color: #f5f5f5;
  height: 100vh;
`;
const Stlayout = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: center;
  text-align: center;
`;

const Stborder = styled.div`
  border: 2px solid black;
  width: 400px;
  height: 400px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin: 200px;
  border-radius: 40px;
  color: #696969;
`;

Write

import React from "react";
import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import { useState } from "react";
import Button from "../components/allbtn";
import { useMutation } from "react-query";
import StheadComponent from "../components/header";
import { addTodo } from "../api/todo";

function Write() {
  const navigate = useNavigate();
  const [todo, setTodo] = useState({
    title: "",
    body: "",
    writer: "",
  });

  const addTodoMutation = useMutation(addTodo, {
    onSuccess: () => {
      // try
      navigate("/work"); // 이동 가능
    },
    onError: (error) => {
      // catch
      alert(error);
    },
  });

  const onSubmitHandler = (e) => {
    e.preventDefault();
    if (todo.title.length < 10) {
      alert("제목은 10글자 이상 입력해야 합니다.");
      return;
    }
    addTodoMutation.mutate(todo);
  };

  return (
    <St>
      <div onClick={() => navigate("/")}>
        <StheadComponent />
      </div>
      <Stformdiv>
        <Stform onSubmit={onSubmitHandler}>
          <Stdiv>작성자</Stdiv>
          <Stinput
            type="text"
            onChange={(ev) => {
              const { value } = ev.target;
              setTodo((prev) => ({
                ...prev,
                writer: value,
              }));
            }}
          />
          <Stdiv>제목</Stdiv>
          <Stinput
            type="text"
            onChange={(ev) => {
              const { value } = ev.target;
              setTodo((prev) => ({
                ...prev,
                title: value,
              }));
            }}
          />
          <Stdiv>내용</Stdiv>
          <StTextarea
            onChange={(ev) => {
              const { value } = ev.target;
              setTodo((prev) => ({
                ...prev,
                body: value,
              }));
            }}
          />
          <Button
            disabled={
              !todo.title ||
              !todo.body ||
              !todo.writer ||
              addTodoMutation.isLoading
            }
          >
            {addTodoMutation.isLoading ? "추가 중..." : "추가하기"}
          </Button>
        </Stform>
      </Stformdiv>
    </St>
  );
}

export default Write;

const Stformdiv = styled.div`
  margin: 20px;
`;

const Stform = styled.form`
  display: flex;
  flex-direction: column;
  justify-content: center;
`;

const Stinput = styled.input`
  box-sizing: border-box;
  height: 46px;
  width: 100%;
  outline: none;
  border-radius: 8px;
  padding: 0px 12px;
  font-size: 20px;
  border: 1px solid black;
`;

const StTextarea = styled.textarea`
  box-sizing: border-box;
  height: 200px;
  width: 100%;
  outline: none;
  border-radius: 8px;
  padding: 12px;
  font-size: 20px;
  border: 1px solid black;
  margin-bottom: 200px;
`;
const St = styled.div`
  background-color: #f5f5f5;
  height: 100vh;
`;
const Stdiv = styled.h2`
  color: #696969;
`;

마무리

페어 분이랑 과제를 하면서 axios와 리덕스 툴킷에 대해서 좀 더 알아간 거 같다. 앞으로 더 공부해야겠지만, 좋은 시간이 되었던 거 같다.🔥

profile
console.log("Hello")

0개의 댓글