본문 바로가기
STUDY/react.js

[React] custom hook으로 코드 재사용하기

by 히컵 2024. 9. 11.

(

 

 

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;

 

 

 

 

 

 

* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.