리액트 훅에는 useState, useEffect, useMemo 등이 있는데, 리액트에서는 이러한 기본 훅 이외에도 커스텀 훅을 만들 수 있다.
리액트 공식 문서에서는 커스텀 훅을 사용하면 컴포넌트 로직을 재사용 가능한 함수로 만들 수 있다고 소개하고 있다.
그렇다면 커스텀 훅은 언제 사용되고 어떤 예제가 있는지 알아보자.
useFetch
프론트엔드를 개발할 때 서버로부터 데이터를 받아와 화면에 뿌려주는 것은 누구나 해봤을 것이다.
이 때 가장 흔한 코드 패턴은 useState를 사용해 `data` state를 선언하고 useEffect를 사용해 컴포넌트가 처음 렌더링될 때 fetch를 사용해서 데이터를 받아오고 그 값을 `data` state에 업데이트하는 것이다.
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(resource)
.then((res) => res.json())
.then((data) => setData(data));
}, []);
}
그런데 만약 이런 로직을 여러 컴포넌트 혹은 state에서 반복한다면 재사용 가능한 함수로 분리하는 것이 좋다.
그러면 어떻게 커스텀 훅을 만들 수 있을까?
우선 함수명을 `use`로 시작하는 이름으로 선언한다. 그래야만 리액트가 커스텀 훅이라는 것을 알 수 있기 때문이다.
그런 다음 기존 훅을 배합하여 새로운 훅을 만들 수 있다. useFetch 예제는 아래와 같다.
// useFetch.js
import { useState, useEffect } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => setData(data));
}, [url]);
return [data];
};
export default useFetch;
// App.js
import useFetch from './useFetch';
const App = () => {
const [data] = useFetch(url);
}
이 코드는 위 예제와 같은 로직이지만 커스텀 훅을 사용해 컴포넌트 코드가 좀 더 깔끔해진 것을 볼 수 있다.
useStorage
또 다른 예제를 살펴보자.
내가 개인적으로 fetch만큼 자주 사용하는 로직은 state가 변경될 때마다 session 혹은 local storage에 업데이트하는 로직이다.
예제를 위해 input값을 받아 state가 변경될 때마다 로컬 스토리지에 저장하고, 새로운 페이지를 불러올 때 로컬 스토리지에 저장된 값이 있으면 그 값을 불러오도록 한다고 가정해보자.
리액트 기본 훅을 사용하면 아래와 같이 구현할 수 있다.
import React, { useState, useEffect } from 'react';
function App() {
const [value, setValue] = useState('')
useEffect(() => {
const savedValue = JSON.parse(localStorage.getItem('myKey'))
if (savedValue) setValue(savedValue)
}, [])
useEffect(() => {
localStorage.setItem('myKey', JSON.stringify(value));
}, [value]);
return (
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)}
/>
)
}
이제 위 코드에서 useStorage 커스텀 훅을 만들어 분리해보자.
// useStorage.js
import { useState, useEffect } from 'react'
function getSavedValue(key, initialValue) {
const savedValue = JSON.parse(localStorage.getItem(key))
if (savedValue) return savedValue
if (initialValue instanceof Function) return initialValue()
return initialValue
}
export default function useStorage(key, initialValue) {
const [value, setValue] = useState(() => { // 컴포넌트를 불러올 때만 실행한다.
return getSavedValue(key, initialValue)
})
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value))
}, [value])
return [value, setValue]
}
// App.js
import React from 'react'
import useStorage from './useStorage';
function App() {
const [value, setValue] = useStorage('myValue', '')
return (
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)}
/>
)
}
첫 번째 예제와 마찬가지로 컴포넌트 코드가 훨씬 간결해졌다.
리액트의 기본훅과 마찬가지로 커스텀 훅 또한 같은 훅을 여러 state나 component에서 써도 상태를 공유하지 않는다.
즉 const [data1] = useFetch(url1)과 const [data2] = useFetch(url2)를 사용한다고 해서 data1과 data2가 연동되는 것이 아니라는 뜻이다.
useState로 선언된 모든 state가 각자 다른 상태를 가지듯 커스텀 훅도 마찬가지다.
위 예제 외에도 커스텀 훅의 사용법은 무궁무진하니 여러 번 반복되는 컴포넌트 로직을 발견하면 커스텀 훅을 만들어 분리하도록 하자.
출처
- useFetch : React Custom Hooks - W3Schools
- useStorage: The Power Of Custom Hooks - WebDevSimplified Blog
'개발 > 프론트엔드 (JS & React)' 카테고리의 다른 글
[React + MongoDB + Netlify] Netlify Serverless 함수와 몽고DB를 사용해 리액트 프로젝트 무료로 호스팅하기 1 (0) | 2022.11.08 |
---|---|
[React & Storybook] 3 - 타입스크립트로 스토리 작성하기 (0) | 2022.09.29 |
[React Hooks] useMemo & useCallback (0) | 2022.09.26 |
[React Hooks] useReducer 사용해서 Todo list 구현하기 (0) | 2022.09.25 |
[React Hooks] Context API와 컴포넌트 합성(Component Composition) (0) | 2022.09.25 |