custom hook이란?
- 리액트에서 제공하는 기본 Hook들(useState, useEffect, useContext 등)을 조합해 특정 기능을 구현하고, 이를 여러 컴포넌트에서 재사용할 수 있도록 만든 것.
- 함수 이름은 반드시 use로 시작해야 한다.
- 리액트는 함수 이름이 use로 시작하면 해당 함수가 Hook이라는 것을 인식하고, 그 안에서 Hook을 사용할 수 있도록 허용한다.
이렇게 생긴 좋아요 버튼과 기능을 만들어 보며 알아보자.
function Detail(){
let [like, setLike] = useState(0)
return (
(생략)
<h4>{like}</h4>
<button onClick={()=>{ setLike((a) => { return a + 1 }) }}>❤</button>
)
}
state를 만들고 +1 버튼도 만들었다.
(참고)
state 변경 함수 사용할 때 안에 콜백함수를 넣을 수 있다.
콜백함수 안에 파라미터를 하나 작명할 수 있는데(위에서는 a라고 작명함), 거기에 기존 state 값이 들어있다.
그래서 위처럼 코드를 짜도 like + 1이 된다. state 이름 기억하기 싫으면 이렇게 써도 된다.
중괄호와 return은 생략 가능하므로 setLike( (a) => a + 1 ) 이렇게 작성해도 된다.
function Detail(){
let [like, setLike] = useState(0)
function addLike(){
setLike((a) => { return a + 1 })
}
return (
(생략)
<h4>{like}</h4>
<button onClick={()=>{ addLike() }}>❤</button>
)
}
길어서 addLike()라는 함수로 만들어서 사용해 봤다.
그런데 여기에서 문제가 하나 발생했다고 해 보자. 위에 작성한 4줄의 자바스크립트 코드가 다른 컴포넌트에도 자주 필요해진다면? 페이지마다 4줄의 코드를 복사 붙여넣어도 되지만, 마찬가지로 함수로 ㅁ나들어서 재사용해도 된다.
(hooks/useLike.js)
export function useLike(){
let [like, setLike] = useState(0)
function addLike(){
setLike((a) => { return a + 1 })
}
}
그래서 hooks라는 폴더를 생성한 후, useLike.js 파일을 생성하여 위의 4줄 코드를 담았다.
이제 4줄의 코드가 필요한 곳마다 useLike()라고 간단하게 쓰면 된다.
(Detail.js)
import { useLike } from "./../hooks/useLike.js";
function Detail(){
useLike()
return (
(생략)
<h4>{like}</h4>
<button onClick={()=>{ addLike() }}>❤</button>
)
}
상단에 useLike()를 import한 후 사용해 봤다.
그런데 like와 addLike라는 변수 함수가 정의되지 않았다고 에러가 발생한다.
like와 addLike는 useLike() 안에 있는데 왜 정의되지 않았다는 걸까?
왜냐하면 원래 함수 안에 있던 변수는 함수 바깥에서 사용하지 못한다. 변수의 범위 때문이다.
함수 안에 있던 변수들을 함수 바깥에서도 쓰고 싶으면 그 변수들을 함수 바깥으로 배출하면 되는데, 배출하려면 return 키워드 이용하면 된다.
(hooks/useLike.js)
export function useLike(){
let [like, setLike] = useState(0)
function addLike(){
setLike((a) => { return a + 1 })
}
return [like, setLike]
}
return 키워드 코드를 사용하면 함수를 실행하고 난 자리에 키워드를 뱉어 준다.
return은 여러개 쓸 수 없으므로 바깥으로 꺼낼 변수가 여러개라면 배열 형식을 사용하면 된다.
(Detail.js)
function Detail(){
let [a, b] = useLike()
return (
(생략)
<h4>{a}</h4>
<button onClick={()=>{ b() }}>❤</button>
)
}
함수를 실행하면 그 자리에 [like, setLike]를 뱉고 있기 때문에 그걸 destructuring 문법으로 각각 변수에 저장해서 사용했다.
(Detail.js)
function Detail(){
let [like, setLike] = useLike()
return (
(생략)
<h4>{a}</h4>
<button onClick={()=>{ setLike() }}>❤</button>
)
}
a, b라고 작명한 변수들을 더 적절한 이름으로 변경했다.
아무튼 이러면 함수 안에 있던 변수들을 함수 바깥에서도 사용 가능하다.
🌱 custom hook
(hooks/useLike.js)
export function useLike(){
let [like, setLike] = useState(0)
function addLike(){
setLike((a) => { return a + 1 })
}
return [like, setLike]
}
1. 위처럼 useState, useEffect 등의 코드를 담고 있는 함수를 작명할 때는 use를 붙이는 것이 좋다.
- useStatea 이런 코드들은 항상 컴포넌트 함수 안에 적어야 하는데, 실수로 html 안에 넣거나 if문 안에 넣거나 하는 상황을 방지하기 위해서이다. useState 같은 코드가 담겨 있는 함수들도 use로 시작하게 짓는 걸 권장한다.
2. useState, useEffect 등을 hook이라고 부르는데, useState, useEffect등을 담고 있는 함수를 custom hook이라고 한다.
+ 참고로 react query 같은 것들도 코드가 길고 재사용이 잦기 때문에 custom hook으로 만들어 놓고 재사용하는 경우들이 많다.
또 다른 예제를 통해 custom hook을 만들어 보자.
서버에서 이름을 가져와서 html에 보여주는 코드를 작성해 보자. 다른 페이지에서도 자주 사용할 것 같으니 함수로 빼서 사용해 보자.
하지만 서버가 없기 때문에 [public]-[username.json] 파일을 만들고, 그 파일에 "kim"이라는 단어 하나를 저장해 두면 /username.json으로 GET 요청시 "kim"을 가져올 수 있다.
(username.json)
kim
(username.js)
import { useEffect, useState } from "react";
import axios from 'axios';
export function useUsername(){
let [username, setUserName] = useState('');
useEffect(()=>{
axios.get('/username.json').then((result) => {
setUserName(result.data);
})
.catch( () => {
console.log('Error');
});
}, [])
return username;
}
(Detail.js)
import {useUsername} from "./../hooks/username.js";
function Detail(props){
let username = useUsername();
return(
<div>
{username ? <p>{username}</p> : <p>Loading...</p>}
</div>
)
}
export default Detail;
* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.