react Hook
을 통해 내가 원하는 인터페이스와 기능을 담은 나만의 커스텀 훅을 직접만들 수 있다. 그럼 간단하게 불러와서 사용할 수 있고, 코드의 중복도 없어 재사용에 있어 효율성이 극대화된다.
그냥 함수를 하나 만드는 것이라고 생각하면된다. 컴포넌트 내부에 있던 로직을 custom Hook이라는 함수로 빼주는 것!
매개변수와 리턴값도 마음대로 지정할 수 있다.
custom Hook 내부에서 다른 useState, useEffect 리액트 훅을 자유롭게 사용할 수 있고, custom Hook을 사용하는 컴포넌트마다 custom Hook이 가지는 useState와 useEffect는 완전히 독립적이기 때문에 custom Hook은 폭발적인 재사용성을 제공한다.
1) 먼저 기본 Input을 하나 만들었다.
2) 확인버튼을 누를때마다 위에 팝업이 뜨게 만드는 함수를 만들어보자.
3) 확인을 누르면 Input을 초기화 시켜주기 위해 setInputValue('') 추가해줬다.
import { useState } from "react";
function App() {
const [inputValue, setInputValue] = useState("");
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = () => {
alert(inputValue);
setInputValue('')
};
return (
<div>
<h1>useInput</h1>
<input value={inputValue} onChange={handleChange} />
<button onClick={handleSubmit}>확인</button>
</div>
);
}
export default App;
여기서 만약 여러개의 컴포넌트가 있고 각 컴포넌트마다 input값을 처리해줘야한다면 위의 로직을 여러 컴포넌트에 계속 복사 붙여넣기를 하기에는 중복코드가 많이 생기는 번거로움이 있다.
이때 input을 처리하는 기능을 담은 custom Hook을 만들어주면 마치 상태관리를 위해 useState 훅을 한 줄로 사용하는 것처럼 모든 기능을 담은 훅을 단 한 줄로 간편하게 사용할 수 있다.
1) src에 커스텀 훅 파일을 하나 만들어 준다.
2) useInput의 매개변수로는 input에 들어갈 초기값을 전달해준다.
3) return 값은 원하는 대로 만들어줄 수 있는데 배열로 만들어보자.
import { useState } from "react";
export function useInput(initialValue) {
const [inputValue, setInputValue] = useState(initialValue);
const handleChange = (e) => {
setInputValue(e.target.value);
};
return [inputValue, handleChange];
}
정리하자면 useInput훅 안에 input을 처리해주는 기능을 넣어줬다. useInput가 반환해주는 inputValue, handleChange만 input 태그의 속성으로 넣어주기만하면서 input을 깔끔하게 처리할 수 있게 되었다.
만약 Input을 하나 더 만들고 싶으면 긴 로직을 쓰는 것이 아니라 그냥 useInput을 가져다가 한 줄로 쓰면 된다.
간편 깔끔 !
이번에는 handleSubmit 확인버튼을 누를때마다 처리해주는 기능도 useInput에 넣어주자.
import { useState } from "react";
export function useInput(initialValue, submitAction) {
const [inputValue, setInputValue] = useState(initialValue);
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = () => {
// input state를 빈 문자열로 초기화
setInputValue("");
// 매개변수로 받아온 submitAction을 호출해준다. 인자로는 inputValue
submitAction(inputValue);
};
return [inputValue, handleChange, handleSubmit];
}
App.js 돌아와서
확인을 누르면 초기화도 잘되는 것을 확인할 수 있다.
커스텀훅의 재사용성이 굉장히 뛰어난 이유는 커스텀 훅이 가지고 있는 state와 effect는 커스텀 훅을 사용하는 컴포넌트마다 독립적이다.
또 한 컴포넌트에 커스텀훅을 여러번 사용해도 독립적인 state와 effect가 생성이 되는 것이며 input 여러개를 처리하기 위해 useInput 훅을 반복해서 사용해도 각각의 input은 독립적인 state를 가질 수 있는 것이다.
fetch api를 통해서 네트워크 상에서 데이터를 가져오는 기능을 해주는 커스텀 훅인 useFetch을 만들어보자.
1) useEffect를 하나 만들어주고 의존성 배열을 넣어준다. 불러온 api 링크를 추가한다. console 에 찍어서 확인해보면 10명의 유저가 불려온 것을 확인할 수 있다.
2) App 컴포넌트 안에다가 fetch를 통해서 받아온 데이터로 기능을 하나 만들어보자.
3) button 3개 추가해주자.
import { useState } from "react";
import { useEffect } from "react";
// 1) api url 바깥으로 빼줌
const baseUrl = "https://jsonplaceholder.typicode.com";
function App() {
//4) state 만들어주기
// data 안에 fetch api를 통해 받아온 데이터를 넣어줄 것이다.
const [data, setData] = useState(null);
// 2) type에는 user 또는 todo 가 올 수 있다.
const fetchUrl = (type) => {
fetch(baseUrl + "/" + type)
.then((res) => res.json())
// 5) setData 를 통해서 data 안에 넣어준다.
.then((res) => setData(res));
};
// 3) useEffect 안에서 fetchUrl 불러주기
useEffect(() => {
fetchUrl("users");
}, []);
// 6) 어떤 data가 들어있는지 확인
console.log(data);
return (
<div>
<h1>useFetch</h1>
{/* 8) 버튼 만들어주기 */}
{/* users 을 클릭하면 fetchUrl 함수가 불려서 type안에는 users가 들어있게되고
users에 대한 데이터를 받아와서 data state안에다가 users 정보를 넣어주고,
받아와진 정보는 pre 태그를 통해 화면에 보여진다.*/}
<button onClick={() => fetchUrl("users")}>Users</button>
<button onClick={() => fetchUrl("posts")}>Posts</button>
<button onClick={() => fetchUrl("todos")}>Todos</button>
{/* 7) 화면에 출력하기*/}
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default App;
버튼을 눌러 확인해보면 각 요구에 맞게 데이터가 불러와지는 것을 확인할 수 있다.
이 기능을 여러개의 컴포넌트에 사용해야하니까 깔끔하게 useFetch 커스텀 훅으로 빼보자.
import { useEffect, useState } from "react";
// 1) 매개변수로 baseUrl, 가장 먼저 사용할 initialType을 넣어주자.
export function useFetch(baseUrl, initialType) {
const [data, setData] = useState(null);
const fetchUrl = (type) => {
fetch(baseUrl + "/" + type)
.then((res) => res.json())
.then((res) => setData(res));
};
// 3) initialType를 맨처음에 불릴 fetchUrl의 인자로 넣어준다.
useEffect(() => {
fetchUrl(initialType);
}, []);
// 2) 객체형태로 반환
return {
data,
fetchUrl,
};
}
import { useFetch } from "./useFetch";
const baseUrl = "https://jsonplaceholder.typicode.com";
function App() {
// useFetch 불러오기
const { data, fetchUrl } = useFetch(baseUrl, "users");
return (
<div>
<h1>useFetch</h1>
<button onClick={() => fetchUrl("users")}>Users</button>
<button onClick={() => fetchUrl("posts")}>Posts</button>
<button onClick={() => fetchUrl("todos")}>Todos</button>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default App;
결과는 똑같다 !
const { data, fetchUrl } = useFetch(baseUrl, "users");
여기서 조금 더 응용을 해보면
1) [App.js]의 버튼을 다 지우고
2) useFetch 받아온 data 변수의 이름을 data: userData로 바꾼다.
3) fetchUrl은 지운다.
4) userData 복사해서 pre에 바인딩해주자.
5) userData 여러명의 유저의 정보를 담은 배열이니 처음 유저정보만 가져오도록하자 [0]
6) 저장하면 화면에 아무것도 안나오네 ? 왜 ?
7) 유저 데이터가 패치를 통해 받아오기 전에는 값이 null이다. pre 태그에서 조건부 렌더링을 걸어주자.
8) 잘 나온다 !
9) 이번에는
const { data:postData, fetchUrl } = useFetch(baseUrl, "users");
추가해주자.
만약에 다른 컴포넌트들이 baseUrl을 통해서 타입의 데이터를 네트워크 상에서 가져오는 기능이 필요하다면 useFetch 훅을 그냥 가져다가 사용만 하면 된다.
이렇게 커스텀 훅이 사용하여 로직의 낭비도 줄일 수 있고, 재사용성을 극대화할 수 있다. 개인 포폴 작업을 하면서 권한 분기를 지정할 때 사용했던 커스텀 훅이 정말 간편했는데 다시 한번 복습을 통해 다시 실습을 하니까 더 쉽게 사용할 수 있을 것 같다.