React

React Hooks 적용, Custom Hook과 React.lazy()와 Suspense를 이용하기

mellomello.made 2022. 7. 30. 01:24

React Hooks 

 

1. react.lazy(), suspense 를 사용해서 동적으로 리소스 받아오기

react.lazy()와 suspense 는 같이 사용해야 한다. (앱의 볼륨이 작으면 속도감이 안 올 수 있다, 대형 프로젝트에서는 빠른 속도 체감할 수 있다.)

동적 import를 사용해서 받아오고, 받아온 것들을 suspens 컴포넌트 안에서 실행시킨다. 

 

App.js

/* react 메소드 사용할 때 import React, { Suspense } from "react" 불러오기*/
import React, { Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import useFetch from "./util/useFetch";

/* react.lazy()와 suspense를 사용해 보세요. */

const Home = React.lazy(() => import("./Home"));
const Navbar = React.lazy(() => import("./component/Navbar"));
const CreateBlog = React.lazy(() => import("./blogComponent/CreateBlog"));
const BlogDetails = React.lazy(() => import("./blogComponent/BlogDetail"));
const NotFound = React.lazy(() => import("./component/NotFound"));
const Loading = React.lazy(() => import("./component/Loading"));
const Footer = React.lazy(() => import("./component/Footer"));


function App() {
  const {
    error,
    isPending,
    data: blogs,
  } = useFetch("http://localhost:3001/blogs");


return (
    <BrowserRouter>
      {error && <div>{error}</div>}
      {/*Suspense fallback에 로딩 컴포넌트 만들어서 넣어주기*/}
      <Suspense fallback={<Loading />}>
        <div className="app">
          <Navbar />
          <div className="content">
            <Routes>
              <Route
                exact
                path="/"
                element={<Home blogs={blogs} isPending={isPending} />}
              />
              <Route path="/create" element={<CreateBlog />} />
              <Route path="/blogs/:id" element={<BlogDetails blog={blogs} />} />
              <Route path="/blogs/:id" element={<NotFound />} />
            </Routes>
          </div>
          <Footer />
        </div>
      </Suspense>
    </BrowserRouter>
  );
}

export default App;

root component App이 동적 import를 받아오고 컴포넌트를 할 때 마다 추적해서 다운 받는다.

App 컴포넌트 밖에서(최상단) 불렀을 때 suspens가 fallback 안에서 실행 시켜준다. 

 


 

2. id를 이용하여 개별 블로그의 내용이 보일 수 있게하기.

react-router hook: useParams

=> 현재 url에서 동적 매개 변수 key/value 값 객체를 반환하는 hook이다.
BlogDetails.js

const { id } = useParams();
 
구조분해할당을 사용해서 동적으로 변하는 id의 id 가져온다.
 

 

3. useNavigate()를 이용하여 delete 버튼을 누르면 다시 home으로 리다이렉트 하기

BlogDetails.js

const handleClick = () => {
    /* delete 버튼을 누르면 다시 home으로 리다이렉트 되어야 합니다. */
    /* useNavigate()를 이용하여 로직을 작성해주세요. */

    fetch("http://localhost:3001/blogs/" + blog.id, {
      method: "DELETE",
    }).then(() => {
      navigate("/");
    });
  };

 


 

4. isLike와 blog.likes를 이용하여 하트를 누르면 home에서 새로고침을 했을 때 숫자가 올라가게 하기

BlogDetails.js

const handleLikeClick = () => {
    /* 하트를 누르면 home에서 새로고침을 했을 때 숫자가 올라가야 합니다. */
    /* isLike와 blog.likes를 이용하여 handleLikeClick의 로직을 작성해주세요. */

    setIsLike(!isLike);
    let result = blog.Likes;

    if (isLike === false) {
    //하트가 0일때는 마이너스가 나올 수 없기 때문에 -조건은 blog.likes가 0보다 클때로 지정한다.
      if (blog.likes > 0) {
        result = blog.likes - 1;
      }
      result = blog.likes;
    } else {
      result = blog.likes + 1;
    }

  //기존에 있던 값도 같이 보내줘야한다. 
    let putData = {
      id: blog.id,
      title: blog.title,
      body: blog.body,
      author: blog.author,
      //바꾸고자 하는 부분에 result 넣기
      likes: result,
    };
    
    fetch(`http://localhost:3001/blogs/` + blog.id, {
    //put 
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(putData),
    }).then(() => {
      navigate(`/blog/${id}`);
    });
    
    
  return (
    <div className="blog-details">
      {isPending && <div>Loading...</div>}
      {error && <div>{error}</div>}
      {blog && (
        <article>
          <h2>{blog.title}</h2>
          <p>Written by {blog.author}</p>
          <div>{blog.body}</div>
          <button onClick={handleLikeClick}>
            {/* isLike에 의해 조건부 렌더링으로 빨간 하트(❤️)와 하얀 하트(🤍)가 번갈아 보여야 합니다. */}
            {isLike === false ? "❤️" : "🤍"}
          </button>

          <button onClick={handleClick}>delete</button>
        </article>
      )}
    </div>
  );
};

export default BlogDetails;

 

전체코드

BlogDetails.js

import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import useFetch from "../util/useFetch";

const BlogDetails = ({ blogs }) => {
  const { id } = useParams();
  const {
    data: blog,
    error,
    isPending,
  } = useFetch("http://localhost:3001/blogs" + id);
  const [isLike, setIsLike] = useState(true);
  const navigate = useNavigate();

  /* 현재는 개별 블로그 내용으로 진입해도 내용이 보이지 않습니다. */
  /* id를 이용하여 개별 블로그의 내용이 보일 수 있게 해봅시다.
  useParams => 현재 url에서 동적 매개 변수 key/value 값 객체를반환
  */

  const handleClick = () => {
    /* delete 버튼을 누르면 다시 home으로 리다이렉트 되어야 합니다. */
    /* useNavigate()를 이용하여 로직을 작성해주세요. */

    fetch("http://localhost:3001/blogs/" + blog.id, {
      method: "DELETE",
    }).then(() => {
      navigate("/");
    });
  };

  const handleLikeClick = () => {
    /* 하트를 누르면 home에서 새로고침을 했을 때 숫자가 올라가야 합니다. */
    /* isLike와 blog.likes를 이용하여 handleLikeClick의 로직을 작성해주세요. */

    setIsLike(!isLike);
    let result = blog.Likes;

    if (isLike === false) {
      if (blog.likes > 0) {
        result = blog.likes - 1;
      }
      result = blog.likes;
    } else {
      result = blog.likes + 1;
    }

    let putData = {
      id: blog.id,
      title: blog.title,
      body: blog.body,
      author: blog.author,
      likes: result,
    };

    fetch(`http://localhost:3001/blogs/` + blog.id, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(putData),
    }).then(() => {
      navigate(`/blog/${id}`);
    });
  };

  return (
    <div className="blog-details">
      {isPending && <div>Loading...</div>}
      {error && <div>{error}</div>}
      {blog && (
        <article>
          <h2>{blog.title}</h2>
          <p>Written by {blog.author}</p>
          <div>{blog.body}</div>
          <button onClick={handleLikeClick}>
            {/* isLike에 의해 조건부 렌더링으로 빨간 하트(❤️)와 하얀 하트(🤍)가 번갈아 보여야 합니다. */}
            {isLike === false ? "❤️" : "🤍"}
          </button>

          <button onClick={handleClick}>delete</button>
        </article>
      )}
    </div>
  );
};

export default BlogDetails;

 

5. 등록 버튼을 누르면 게시물이 등록하고, home으로 리다이렉트하기.

CreateBlog.js

import { useState } from "react";
import { useNavigate } from "react-router-dom";

const CreateBlog = () => {
  const [title, setTitle] = useState("");
  const [body, setBody] = useState("");
  const [author, setAuthor] = useState("김코딩");
  const navigate = useNavigate();

const handleSubmit = (e) => {
    e.preventDefault();
    
    /*라이브러리 기능으로 id는 자동으로 들어가기 때문에 안 넣어도 됨, likes: 0 으로 정해줘야함*/
    const blog = { title, body, author, likes: 0 };

    fetch("http://localhost:3000/blogs/", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(blog),
    }).then(() => {
      navigate("/");
    });
  };
  
  return (
    <div className="create">
      <h2>Add a New Blog</h2>
      <form onSubmit={handleSubmit}>
        <label>제목</label>
        <input
          type="text"
          required
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="제목을 입력해주세요."
        />
        <label>내용</label>
        <textarea
          required
          value={body}
          onChange={(e) => setBody(e.target.value)}
          placeholder="내용을 입력해주세요."
        ></textarea>
        <label>작성자</label>
        <select value={author} onChange={(e) => setAuthor(e.target.value)}>
          <option value="kimcoding">김코딩</option>
          <option value="parkhacker">박해커</option>
        </select>
        <button>등록</button>
      </form>
    </div>
  );
};

export default CreateBlog;

 

1. 어떤 데이터를 보낼 것인지 request body를 통해서 정해야한다.

 
2. JSON 파일안에 넣어줘야 하기 때문에 파일을 컨버팅 해줘야할 필요가 있다.
=>  JSON.stringify(blog) 메서드를 사용해 바꿔준다. 
 
3. .then()에서 리다이렉트하여 홈으로 갈 때,  useNavigate() 훅을 사용한다.

4. const navigate = useNavigate(); 넣어주고

5. .then()안에  navigate("/") 넣어준다.


6. useState를 이용하여 data, isPending, error를 정의하기.

 
useFetch.js

import { useState, useEffect } from "react";

const useFetch = (url) => {
  /* useState를 이용하여 data, isPending, error를 정의하세요. */
  const [data, setData] = useState(null);
  const [isPending, setIsPending] = useState(true);
  const [error, setError] = useState(null);

  /* useFetch 안의 중심 로직을 작성해주세요. */
  useEffect(() => {
    const abortCont = new AbortController();

    setTimeout(() => {
      fetch(url, { signal: abortCont.signal })
        .then((res) => {
          if (!res.ok) {
            throw Error("could not fetch the data for that resource");
          }
          return res.json();
        })
        .then((data) => {
          setIsPending(false);
          setData(data);
          setError(null);
        })
        .catch((err) => {
          if (err.name === "AbortError") {
            console.log("fetch aborted");
          } else {
            setIsPending(false);
            setError(err.message);
          }
        });
    }, 1000);
//http://localhost:3001/blogs" + id); url이 계속 바뀌는 것을 따라가야할 필요성이있다.
//url에 따라 실행시켜 리렌더링이 된다. 

    return () => abortCont.abort();
  }, [url]);

  return { data, isPending, error };
};

export default useFetch;

 

'React' 카테고리의 다른 글

리액트 리덕스 useMemo 경고  (0) 2022.08.31
리액트 동적으로 CSS 클래스 설정하기  (0) 2022.08.11
React Hooks useMemo  (0) 2022.07.28
React Diffing Algorithm 비교 알고리즘  (0) 2022.07.28
React 심화 Virtual DOM  (0) 2022.07.27