[React] custom Hooks 만들기

이동현·2021년 6월 30일
0

React

목록 보기
4/16
post-thumbnail
const Home = () => {
  const [blogs, setBlogs] = useState(null);
  const [isPending, setIsPending] = useState(true);  //Loading메세지 보여주기 위함
  const [error, setError] = useState(null);
  //Error 상황시 메세지 보여주기 위함

  useEffect(() => {
    //setTimeout은 Loading을 명시적으로 보여주기 위해서 1초 무의미하게 쉬는 것임
    setTimeout(() => {
      fetch("http://localhost:8000/blogs")
        .then(res => {
      	//fetch를 통해 받아온 res객체 안에
        //ok 프로퍼티가 있음
          if (!res.ok) {
            throw Error("could not fetch the data that resource");
          }
          return res.json();
        })
        .then(data => {
          setBlogs(data);
          setIsPending(false);
          setError(null);
        //정상적으로 데이터가 오면
        //Loading, error메세지가 출력이 되지 않게
        })
        .catch(err => {
          setIsPending(false);
          setError(err.message);
        //에러시 Loading메세지 사라지고
        //에러메세지만 보이도록 설정
        });
    }, 1000);
  }, []);

  return (
    <div className="home">
      {error && <div>{error}</div>}
      {isPending && <div>Loading...</div>}
      {blogs && <BlogList blogs={blogs} title="All Blogs" />}
    </div>
  );
};

위 코드가 이전에 짰던 코드이다. 그런데 fetch를 통해서 데이터를 가져오고 예외를 처리하는 부분이 다른 컴포넌트 등에서 반복적으로 사용될 것 같아서 재사용가능한 hook으로 만들어 주면 훨씬 코드량도 줄고 유지보수에 좋을 것이다. 어느 컴포넌트에서든 useState, useEffect 등을 사용하는 것처럼 나만의 Hook을 만들어서 어느 컴포넌트에서든 import 해서 사용할 수 있도록 만들 수 있다.

우선 반복적으로 사용될 것 같은 코드를 복사해서 나만의 custom hook 파일을 만들어서 복사를 할 것이다. 그런데 hook처럼 사용하기 위해서는 함수이름 앞이 use로 시작해야 한다.

우선 함수를 하나 만들고 기존에 재사용하고 싶은 코드를 복사해서 붙여넣기를 한다.

//usefetch.js
import {useState, useEffect} from 'react';

const useFetch = () => {
	useEffect(() => {
    //setTimeout은 Loading을 명시적으로 보여주기 위해서 1초 무의미하게 쉬는 것임
    	setTimeout(() => {
      	fetch("http://localhost:8000/blogs")
       	 .then(res => {
      	//fetch를 통해 받아온 res객체 안에
        //ok 프로퍼티가 있음
       	   if (!res.ok) {
            throw Error("could not fetch the data that resource");
          }
          return res.json();
        })
        .then(data => {
          setBlogs(data);
          setIsPending(false);
          setError(null);
        //정상적으로 데이터가 오면
        //Loading, error메세지가 출력이 되지 않게
        })
        .catch(err => {
          setIsPending(false);
          setError(err.message);
        //에러시 Loading메세지 사라지고
        //에러메세지만 보이도록 설정
        });
    }, 1000);
  }, []);
}

export default useFetch;

우선 복제만 해두면 많은 에러가 발생할 것이다. 왜냐하면 그 전에 있던 파일의 state를 지금 여기서 수정하려고 하기 때문에 읽지를 못한다. 그래서 여기서 사용하는 state를 여기서 다시 정의하는 것으로 수정을 한다. 수정을 하면 다음과 같은 코드가 될 것이다.

//usefetch.js
import {useState, useEffect} from 'react';

const useFetch = () => {
  //재사용될 것이기 때문에 이전의 blogs 를 data로 바꿈
   	const [data, setData] = useState(null);
  	const [isPending, setIsPending] = useState(true);
  	const [error, setError] = useState(null);
  
	useEffect(() => {
    //setTimeout은 Loading을 명시적으로 보여주기 위해서 1초 무의미하게 쉬는 것임
    	setTimeout(() => {
      	fetch("http://localhost:8000/blogs")
       	 .then(res => {
      	//fetch를 통해 받아온 res객체 안에
        //ok 프로퍼티가 있음
       	   if (!res.ok) {
            throw Error("could not fetch the data that resource");
          }
          return res.json();
        })
        .then(data => {
          setBlogs(data);
          setIsPending(false);
          setError(null);
        //정상적으로 데이터가 오면
        //Loading, error메세지가 출력이 되지 않게
        })
        .catch(err => {
          setIsPending(false);
          setError(err.message);
        //에러시 Loading메세지 사라지고
        //에러메세지만 보이도록 설정
        });
    }, 1000);
  }, []);
}

export default useFetch;

여기까지 했으면 이제 이 hook을 사용하는 곳에서 여기의 state 데이터들을 사용할 수 있어야 하기 때문에 이 hook 함수를 사용했을 때 반환값을 받을 수 있어야 한다. 그래서 마지막에 return 문을 작성해서 state 데이터들을 반환해줘야 한다.

그리고 추가적으로 url이 hook 내부에 하드코딩돼있는데 이 부분을 매개변수로 받을 수 있으면 다른 endpoint에 대해서도 재사용할 수 있게 되므로 url을 인자로 받아와서 활용하는 것으로 수정할 수 있다.

그리고 url 을 useEffect 함수의 dependencies 로 추가를 해줌으로써 url 이 바뀔 때마다 useEffect 함수가 다시 실행될 수 있도록 해줘야 한다.

위 수정사항을 반영한 코드가 아래에 있다.

//usefetch.js
import { useState, useEffect } from "react";

//url을 인자로 받아온다.
const useFetch = url => {
  const [data, setData] = useState(null);
  const [isPending, setIsPending] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    //여기서는 async await 을 쓸 수 없다.
    setTimeout(() => {
      fetch(url)	//인자로 받아온 url을 활용
        .then(res => {
          if (!res.ok) {
            throw Error("could not fetch the data for that resource");
          }
          return res.json();
        })
        .then(_data => {
          setData(_data);
          setIsPending(false);
          setError(null);
        })
        .catch(err => {
          setIsPending(false);
          setError(err.message);
        });
    }, 1000);
  }, [url]); //url이 바뀔때마다 다시 실행시키도록

  return { data, isPending, error };
};

export default useFetch;

그렇다면 이제 이 custom hook 을 사용해야 하는데 사용할 원래의 Homs.js 파일로 돌아가서 어떻게 활용할 수 있는지 보자.

custom hook 이 훅 안에 있는 state 데이터들을 반환했기 때문에 이 hook을 사용할 Home.js 에서는 객체를 const {data, isPending, error} = useFetch("http://localhost:8000/blogs") 이런 식으로 받아와서 데이터를 활용할 수 있다.

//Home.js
import useFetch from "./useFetch";
//custom hook을 import한다.
import BlogList from "./BlogList";

const Home = () => {
  //data: blogs 는 받아온 data를 blogs 라는 이름으로 쓰겠다는 의미
  const { data: blogs, isPending, error } = useFetch("http://localhost:8000/blogs");
  
  return (
    <div className="home">
      {error && <div>{error}</div>}
      {isPending && <div>Loading...</div>}
      {blogs && <BlogList blogs={blogs} title="All Blogs" />}
    </div>
  );
};

export default Home;

위와 같이 Homs.js 파일에서 custom hook인 useFetch 를 활용할 수가 있는 것이다. 다른 컴포넌트에서도 같은 로직이 필요할 때 import해서 사용하면 된다.

출처: (https://www.youtube.com/watch?v=Jl4q2cccwf0)
profile
Dom Hardy : 멋쟁이 개발자 되기 인생 프로젝트 진행중

0개의 댓글