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해서 사용하면 된다.