[TIL] React Hook ๊ณผ์ œ

ใ…œใ…œยท2022๋…„ 11์›” 29์ผ
1

Today I learn

๋ชฉ๋ก ๋ณด๊ธฐ
62/77
post-thumbnail

๐Ÿฉ ๊ณผ์ œ ๋ชฉํ‘œ

  • json-server ์ด์šฉํ•ด ์„œ๋ฒ„๋ฅผ ์—ด๊ณ , CRUD์— ๋งž์ถฐ fetch๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ค๊ฑฐ๋‚˜, ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์ˆ˜์ •ํ•˜๊ธฐ
  • React.lazy(), Suspense ์ ์šฉํ•˜๊ธฐ
  • useNavigate, useParams ์ด์šฉ
  • Custom Hook ๋งŒ๋“ค์–ด ์ ์šฉํ•ด๋ณด๊ธฐ : useFetch, useScroll


๐Ÿƒ ๊ณผ์ œ ์ง„ํ–‰

โ“ json-server

Get a full fake REST API with zero coding in less than 30 seconds (seriously) ๊ณต์‹๋ฌธ์„œ

json-server๋Š” ์ง์ ‘ DB๋ฅผ ๋งŒ๋“ค๊ณ  ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ํ•„์š” ์—†์ด json ํŒŒ์ผ์„ ์ด์šฉํ•˜์—ฌ REST API ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

REST API์˜ ๊ธฐ๋ณธ์ ์ธ ์„ฑ๋Šฅ์€ ์ „๋ถ€ ๊ฐ–์ถ”๊ณ  ์žˆ์œผ๋‚˜ ์‹ค์ œ ์•ฑ์— ์‚ฌ์šฉ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ ์•ฑ์— json-server ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค๊ณ  ํ•จ.
json-server๋Š” ์•ฑ์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๊ณต๋ถ€๋ฅผ ์œ„ํ•ด ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์šฉ๋„๋กœ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.

์„ค์น˜

//ํ„ฐ๋ฏธ๋„์—์„œ ์ „์—ญ ์„ค์น˜ 
npm i -g json-server

db.json ๋ฐ์ดํ„ฐ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ

//db.json ๋ฐ์ดํ„ฐ ์˜ˆ์‹œ 
{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

//๊ณผ์ œ data.json์€ ์ด๋Ÿฐ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€ ๋ฐ์ดํ„ฐ ํ˜•ํƒœ์˜€์Œ. 
{
  "blogs": [
    {
      "id": 2,
      "title": "๋ฐ•ํ•ด์ปค์˜ ์‚ถ",
      "author": "๋ฐ•ํ•ด์ปค",
      "body": "์˜ค๋Š˜๋„ ๋ฐ•ํ•ด์ปค๋Š” ์—ด์‹ฌํžˆ ์ฝ”๋”ฉ์„ ํ•˜๋„ค",
      "likes": 26
    },
    {
      "id": 3,
      "title": "์•ˆ๋…•ํ•˜์„ธ์š”?",
      "body": "๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค",
      "author": "๊น€์ฝ”๋”ฉ",
      "likes": 0
    }
  ]
}

๊ณผ์ œ์—์„œ๋Š” data.json ์ด ์ด๋ฏธ ์ž‘์„ฑ๋˜์–ด ์žˆ์—ˆ์ง€๋งŒ, ๋งŒ์•ฝ ํ˜ผ์ž ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ง์ ‘ ์ž‘์„ฑํ•ด์ค€๋‹ค.

json-server ์‹œ์ž‘ํ•ด์ฃผ๊ธฐ

json-server --watch db.json

๊ณผ์ œ์—์„œ๋Š” โ€”port 3001์ด๋ผ๋Š” ์˜ต์…˜์„ ๋ถ™์—ฌ์ฃผ์—ˆ๋Š”๋ฐ, ์˜ต์…˜์ด ์—†์œผ๋ฉด json-server๋Š” ์ €์ ˆ๋กœ 3000๋ฒˆ ํฌํŠธ๋ฅผ ์ ์œ ํ•˜๊ณ  ์„œ๋ฒ„๋ฅผ ์—ฐ๋‹ค.

์„œ๋ฒ„๊ฐ€ ๋ฌธ์ œ ์—†์ด ์—ด๋ฆฌ๋ฉด ํ„ฐ๋ฏธ๋„์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋œฌ๋‹ค ~~

localhost:3001 ๋กœ ์ ‘์†ํ•ด๋ณด๋ฉด



postman์œผ๋กœ Get ์š”์ฒญ ๋ณด๋‚ด๊ธฐ

ํฌ์ŠคํŠธ๋งจ์˜ Workspaces์—์„œ HTTP request๋ฅผ ์ƒˆ๋กœ์ด ์ƒ์„ฑํ•˜์—ฌ, json-server๊ฐ€ ๋งŒ๋“ค์–ด์ค€ API์— GET ์š”์ฒญ์„ ๋ณด๋‚ด๋ณด๋ฉด, json ํŒŒ์ผ์— ๋“ค์–ด์žˆ๋Š” ๋‚ด์šฉ ๊ทธ๋Œ€๋กœ ์‘๋‹ต์„ ํ•˜๊ณ  ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. (์„œ๋ฒ„๊ฐ€ ์ž˜ ๋งŒ๋“ค์–ด์กŒ๊ตฌ๋‚˜๐Ÿคญ)





๐Ÿ’ซ React.lazy(), Suspense ์‚ฌ์šฉํ•ด ๋กœ๋”ฉ์ฐฝ ๊ตฌํ˜„

๊ทธ๋ƒฅ lazy()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ , React.lazy()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
lazy๋ฅผ import ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š๋ƒ, React ๋ฅผ import ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š๋ƒ์˜ ์ฐจ์ด

์›๋ž˜๋Š” App.js ์ตœ์ƒ๋‹จ์— import๋ฌธ์œผ๋กœ ๋ถˆ๋Ÿฌ์™€์ฃผ์—ˆ๋˜ Home, CreateBlog, BlogDetails, NotFound ๋“ฑ์˜ ํŽ˜์ด์ง€๋“ค์„ ์•„๋ž˜์™€ ๊ฐ™์ด lazy๋ฅผ ์‚ฌ์šฉํ•ด import ํ•ด์ฃผ์—ˆ๋‹ค.

import { Suspense, lazy } from "react";
const Home = lazy(() => import("./Home"));
const CreateBlog = lazy(() => import("./blogComponent/CreateBlog"));
const BlogDetails = lazy(() => import("./blogComponent/BlogDetail"));
const NotFound = lazy(() => import("./component/NotFound"));

//๊ณผ์ œ์‹œ์—๋Š” ํŽ˜์ด์ง€์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ๋“ค์—๋งŒ lazy๋ฅผ ์ ์šฉํ–ˆ๋Š”๋ฐ, 
//๋ž˜ํผ๋Ÿฐ์Šค์—์„œ๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์— lazy๋ฅผ ์ ์šฉํ•ด์คŒ. 

//์•„๋ž˜๊ฐ€ ๋ž˜ํผ๋Ÿฐ์Šค ์ฝ”๋“œ 
import React, { Suspense } from 'react';

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 Footer = React.lazy(() => import('./component/Footer'));
const Loading = React.lazy(() => import('./component/Loading'));

๋กœ๋”ฉ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ด๋ฏธ ๊ตฌํ˜„์ด ๋˜์–ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๋”ฉ ์ปดํฌ๋„ŒํŠธ๋ฅผ Suspense์˜ fallback์œผ๋กœ ์ง€์ •ํ•ด์ค„ ๊ฒƒ์ด๋‹ค.

์ง€์—ฐ ์‹œ ๋กœ๋”ฉ ํ™”๋ฉด์— ๋–ด์œผ๋ฉด ํ•˜๋Š” ๊ณณ์— Suspense๋กœ ๋ฌถ์–ด์ค€๋‹ค.

//๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ž˜ํผ๋Ÿฐ์Šค ์ฝ”๋“œ์—์„œ๋Š” Navbar, Footer๊นŒ์ง€ ํฌํ•จํ•ด์ฃผ๊ณ  ์žˆ์ง€๋งŒ
//๋‚˜์™€ ํŽ˜์–ด๋Š” ํŽ˜์ด์ง€์— ๋Œ€ํ•ด์„œ๋งŒ Suspense๋ฅผ ์ ์šฉ์‹œ์ผฐ๋‹ค. 
return (
    <BrowserRouter>
      {error && <div>{error}</div>}
      <div className="app">
        <Navbar />
        <div className="content">
          <Suspense fallback={<Loading />}>
            <Routes>
              <Route
                exact
                path="/"
                element={<Home blogs={blogs} isPending={isPending} />}
              />
              <Route path="/create" element={<CreateBlog />} />
              <Route
                path="/blogs/:id"
                element={<BlogDetails blogs={blogs} />}
              />
              <Route path="/blogs/:id" element={<NotFound />} />
            </Routes>
          </Suspense>
        </div>
        <Footer />
      </div>
    </BrowserRouter>
  );




โ˜๏ธ useParams ์ด์šฉํ•ด ๊ฐœ๋ณ„ ๋ธ”๋กœ๊ทธ ๋‚ด์šฉ ๋ณด์—ฌ์ฃผ๊ธฐ

useParams๋Š” ๋ฆฌ์•กํŠธ์—์„œ ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ํ™œ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Hook์ด๋‹ค. (ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์•„๋‹Œ Pathname์„ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” useLocation์ด๋ผ๋Š” Hook๋„ ์žˆ๋‹ค.)

BlogDetail์ด๋ผ๋Š” ๋ธ”๋กœ๊ทธ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฉ”์ธ ํ™”๋ฉด์—์„œ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ๋ธ”๋กœ๊ทธ ์ƒ์„ธํ™”๋ฉด์„ ๋„์šฐ๋Š” ์ปดํฌ๋„ŒํŠธ์ธ๋ฐ, ์ด ์ƒ์„ธํ™”๋ฉด์„ ๊ตฌํ˜„ํ•  ๋•Œ fetch๋กœ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์•ผํ•œ๋‹ค. App.js์—์„œ ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์ค„ ๋•Œ, ์ƒ์„ธ ํ™”๋ฉด์œผ๋กœ ๊ฐ€๋Š” path๋ฅผ "/blogs/:id"๋ผ๊ณ  ์ง€์ •ํ•ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— fetch์—๋„ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์˜ id๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํฌํ•จ๋œ Url์„ ์ „๋‹ฌํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ด๋•Œ id ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด useParams์ด๋‹ค.

const BlogDetails = ()=>{
 
 let { id } = useParams();

 //์ค‘๊ฐ„์ƒ๋žต  
  
  useEffect(() => {
    setTimeout(() => {
      fetch(`http://localhost:3001/blogs/${id}`)
      //ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•ด์„œ id ๊ฐ’์„ url์— ํฌํ•จ์‹œ์ผœ์คŒ 
        .then((res) => {
          if (!res.ok) {
            throw Error("could not fetch the data for that resource");
          }
          return res.json();
        })
        .then((data) => {
          setIsPending(false);
          setBlogs(data);
          console.log(data);
          setError(null);
        })
        .catch((err) => {
          setIsPending(false);
          setError(err.message);
        });
    }, 1000);
  }, []);
 
  //์ƒ๋žต 
}

์ด๋ ‡๊ฒŒ useParams๋ฅผ ์ด์šฉํ•ด ๋ฐ›์€ id๋Š” fetch๋กœ ๊ฒŒ์‹œ๊ธ€์—๋Œ€ํ•œ Delete ์š”์ฒญ์„ ๋ณด๋‚ผ๋•Œ๋‚˜ patch ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ธ”๋กœ๊ทธ ์ƒ์„ธํ™”๋ฉด์— ์žˆ๋Š” ํ•˜ํŠธ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ์ƒ์„ธํ™”๋ฉด์—์„œ ํ•˜ํŠธ ์ƒ‰์ด ๋ฐ”๋€Œ๊ณ , ์‹ค์ œ๋กœ ๋ฉ”์ธ ํ™”๋ฉด์—์„œ๋„ ํ•˜ํŠธ ์ˆ˜๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๋ถ€๋ถ„์— ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ fetch ์š”์ฒญ์„ ๋ณด๋‚ด์ค„ ๋•Œ PATCH๋กœ ๋ณด๋‚ด์ค„ ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ PUT์œผ๋กœ ๋ณด๋‚ด์ฃผ์—ˆ๋‹ค.

PUT์€ ๋ฆฌ์†Œ์Šค์˜ ๋ชจ๋“  ๊ฒƒ์„ ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ž˜ํผ๋Ÿฐ์Šค ์ฝ”๋“œ์™€ ๋น„๊ตํ•ด๋ณด๋‹ˆ... ์ž‘์„ฑํ•œ ๊ธธ์ด์— ๊ฝค ์ฐจ์ด๊ฐ€ ๋‚ฌ๋‹ค. ๊ธธ์ด๊ฐ€ ์ „๋ถ€๋Š” ์•„๋‹ˆ๊ฒ ์ง€๋งŒ, ๊ทธ๋ž˜๋„ ํŽ˜์–ด๊ฐ€ ์ด์•ผ๊ธฐํ•  ๋•Œ ๋‚ด๊ฐ€ PATCH๋ฅผ ์“ฐ๋Š” ๊ฑด ์–ด๋–จ๊นŒ์š”~ ์ด์•ผ๊ธฐํ•ด๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค๋ฉด ์ข‹์•˜์„ ๊ฑฐ ๊ฐ™๋‹จ ์ƒ๊ฐ์ด ๋“ ๋‹ค.

//patch ์‚ฌ์šฉํ•œ ๋ž˜ํผ๋Ÿฐ์Šค ์ฝ”๋“œ 
const handleLikeClick = () => {
        /* ํ•˜ํŠธ๋ฅผ ๋ˆ„๋ฅด๋ฉด home์—์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ–ˆ์„ ๋•Œ ์ˆซ์ž๊ฐ€ ์˜ฌ๋ผ๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค. */
        /* isLike์™€ blog.likes๋ฅผ ์ด์šฉํ•˜์—ฌ handleLikeClick์˜ ๋กœ์ง์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. */
        setIsLike(!isLike);
        let patchData = {"likes" : blog.likes + 1};
        fetchPatch('http://localhost:3001/blogs/', id, patchData);
    }


//Put์„ ์‚ฌ์šฉํ•œ ๋‚ด ์ฝ”๋“œ
const handleLikeClick = () => {
    setIsLike(!isLike);
    let boll = !isLike;
    let likeData = blog.likes;
    boll ? likeData++ : likeData--;

    const putData = {
      id: blog.id,
      title: blog.title,
      author: blog.author,
      body: blog.body,
      likes: likeData,
    };

    fetch(`http://localhost:3001/blogs/${id}`, {
      method: "PUT",
      body: JSON.stringify(putData),
      headers: {
        "Content-Type": "application/json",
      },
    });
    /* ํ•˜ํŠธ๋ฅผ ๋ˆ„๋ฅด๋ฉด home์—์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ–ˆ์„ ๋•Œ ์ˆซ์ž๊ฐ€ ์˜ฌ๋ผ๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค. */
    /* isLike์™€ blog.likes๋ฅผ ์ด์šฉํ•˜์—ฌ handleLikeClick์˜ ๋กœ์ง์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. */
    console.log("like!");
  };

์ฐธ๊ณ ๋กœ ์•„์˜ˆ ์œ„ ๋ž˜ํผ๋Ÿฐ์Šค ์ฝ”๋“œ์—์„œ๋Š” fetchPatch์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” API๋ฅผ ๋งค์„œ๋“œํ™”(?) ํ•ด์„œ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์ €๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด util ํด๋”์— api.js ๋ผ๋Š” api ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

//์ด๋Ÿฐ ์‹์œผ๋กœ api๋ฅผ ๋ฌธ์„œํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


const BASE_URL = 'http://localhost:3000/';
const BLOG_URL = 'http://localhost:3000/blogs/';

export const fetchCreate = (url, data) => {
    fetch(url, {
        method: "POST",
        headers: {"Content-Type" : "application/json"},
        body: JSON.stringify(data)
    })
    .then(() => {
        window.location.href = BASE_URL;
    })
    .catch((error) => {
        console.error('Error', error);
    })
}

export const fetchDelete = (url, id) => {
    fetch(`${url}${id}`, {
      method: "DELETE",
    })
    .then(() => {
      window.location.href = BASE_URL;
    })
    .catch((error) => {
      console.error('Error', error);
    })
}

export const fetchPatch = (url, id, data) => {
    fetch(`${url}${id}`, {
      method : "PATCH",
      headers: {"Content-Type" : "Application/json"},
      body: JSON.stringify(data)
    })
    .then(() => {
      window.location.href = `${BLOG_URL}${id}`;
    })
    .catch((error) => {
      console.error('Error', error);
    })
}




๐Ÿน useNavigate ์ด์šฉํ•ด ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ•ด์ฃผ๊ธฐ

์œ„ ๋ž˜ํผ๋Ÿฐ์Šค ์ฝ”๋“œ์˜ api.js ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ์—ญ์‹œ BASE_URL์„ ํ†ตํ•ด์„œ ํ•ด์คฌ์ง€๋งŒ, ๊ณผ์ œ๋ฅผ ํ•  ๋•Œ๋Š” react-router-dom์˜ useNavigate๋ฅผ ์‚ฌ์šฉํ•ด์ฃผ์—ˆ๋‹ค. (๋น„์Šทํ•˜๊ฒŒ Link ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋Š”๋ฐ Link ์ปดํฌ๋„ŒํŠธ๋„ ์ง€์ •ํ•œ ๊ฒฝ๋กœ๋กœ ์ด๋™์‹œ์ผœ ์ค€๋‹ค. ํด๋ฆญํ•  ๊ฒฝ์šฐ ๋ฐ”๋กœ ์ด๋™ํ•˜๋Š” ๊ฒฝ์šฐ Link ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , useNavigate๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ํŠน์ • ํ–‰๋™์ด๋‚˜ ์ถ”๊ฐ€๋กœ ์ฒ˜๋ฆฌํ•ด์•ผํ•˜๋Š” ๋กœ์ง์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ)

useNavigate๋Š” ํŠน์ • ํ–‰๋™์„ ํ–ˆ์„ ๋•Œ ์ง€์ •ํ•œ ์ฃผ์†Œ๋กœ ์ด๋™ํ•˜๊ฒŒ ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

๊ณผ์ œ์—์„œ๋Š” ์ƒˆ๋กœ์šด ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€์„ ์ƒ์„ฑํ•˜๊ณ  ๋‚˜์„œ ํ™ˆ ํ™”๋ฉด์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•˜๋„๋ก ํ–ˆ๋‹ค.

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

const CreateBlog = () => {
  //์ƒ๋žต 
const navigate = useNavigate();
  
const handleSubmit = (e) => {
    e.preventDefault();
    /* ๋“ฑ๋ก ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ฒŒ์‹œ๋ฌผ์ด ๋“ฑ๋ก์ด ๋˜๋ฉฐ home์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. */
    //ํ™ˆ ์ฃผ์†Œ๋ฅผ ๊ฐ–๋„๋ก useNavigate ์‚ฌ์šฉํ•˜๊ธฐ
    const postData = {
      title,
      body,
      author,
      likes: 0,
    };

    fetch(`http://localhost:3001/blogs`, {
      method: "POST",
      body: JSON.stringify(postData),
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then(() => {
        navigate("/");
        window.location.reload(); //๋ฐ”๋กœ ์ƒˆ๋กœ๊ณ ์นจ ํ•ด์ฃผ๋Š” ์ฝ”๋“œ
      })
      .catch((err) => console.log(err));
    console.log(e.type);
  };
  
  //์ƒ๋žต
}




๐Ÿ›  Custom Hook ๋งŒ๋“ค์–ด ์ ์šฉํ•ด๋ณด๊ธฐ : useFetch, useScroll

useFetch

GET ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” useEffect hook์€ ์ปดํฌ๋„ŒํŠธ ๋‚ด ์—ฌ๊ธฐ์ €๊ธฐ์„œ ๋ฐ˜๋ณต์ด ๋˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ custom hook์œผ๋กœ ๋งŒ๋“ค์–ด ๋ณด์•˜๋‹ค.


import { useState, useEffect } from "react";

const useFetch = (url) => {
  const [data, setData] = useState();
  const [isPending, setIsPending] = useState();
  const [error, setError] = useState();

  useEffect(() => {
    setTimeout(() => {
      fetch(url)
        .then((res) => {
          if (!res.ok) {
            throw Error("could not fetch the data for that resource");
          }
          return res.json();
        })
        .then((data) => {
          setIsPending(false);
          setData(data);
          console.log(data);
          setError(null);
        })
        .catch((err) => {
          setIsPending(false);
          setError(err.message);
        });
    }, 1000);
  }, []);
  return [data, isPending, error]; /* return ๋ฌธ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. */
};

export default useFetch;

์‚ฌ์šฉํ•ด๋ณด๊ธฐ

import useFetch from "../util/useFetch";

const BlogDetails = () => {
//BlogDetail.js ํŒŒ์ผ ์†์—์„œ get์„ ์ด๋ ‡๊ฒŒ ์งง๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค!
const [blog, isPending, error] = useFetch(
    `http://localhost:3001/blogs/${id}`
  );
}


useScroll

์ ์šฉ๋œ ์ปดํฌ๋„ŒํŠธ ์ง„์ž…์‹œ ํŽ˜์ด์ง€ ๋งจ ์œ„๋กœ ์Šคํฌ๋กคํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

import { useEffect } from "react";

const useScroll = () => {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, []);

  return null;
};

export default useScroll;

์‚ฌ์šฉํ•ด๋ณด๊ธฐ

์‚ฌ์šฉํ•  ์ปดํฌ๋„ŒํŠธ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๋„ฃ์–ด์ฃผ๊ธฐ

import useScroll from "../util/useScroll";
//์ƒ๋žต
const BlogDetails = () => {
 const scrollTop = useScroll();
//์ƒ๋žต 
}
profile
๋‹ค์‹œ ์ผ์–ด๋‚˜๋Š” ์ค‘

0๊ฐœ์˜ ๋Œ“๊ธ€