리액트에는 useState, useReducer, useEffect 등 많은 상태관리를 위한 Hooks들이 있다.
하지만 개발자가 직접 hooks들을 조합해서 새로운 hook을 만들수있는데, 이러한 것을 Custom Hook 이라고 한다.
import { useState, useEffect } from 'react';
import Card from './Card';
const BackwardCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter - 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <Card>{counter}</Card>;
};
export default BackwardCounter;
import { useState, useEffect } from 'react';
import Card from './Card';
const ForwardCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <Card>{counter}</Card>;
};
export default ForwardCounter;
두개의 컴포넌트를 보면 한 컴포넌트는 단순히 +1, 다른 컴포넌트는 -1을 꾸준히 해준다.
두 컴포넌트의 차이점은 -와 + 밖에 없는데, 비슷한 코드를 컴포넌트에 중복적으로 작성했다.
이렇게 되면 코드의 중복이 늘어나게 되어서 가독성이 안좋아지고 재사용성이 줄어든다.
이럴때 Custom Hook을 직접 만들어주어서, 코드의 중복을 줄이고 재사용성을 늘리는 것이다.
import { useState, useEffect } from "react";
const useCounter = (plusOrMinus) => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
if(plusOrMinus){ //plusOrMinus가 true라면
setCounter((prevCounter) => prevCounter + 1);
} else{ //false 라면
setCounter((prevCounter) => prevCounter - 1);
}
}, 1000);
return () => clearInterval(interval);
}, [plusOrMinus]);
return counter;
};
export default useCounter;
Custom hook을 만들때 항상 앞에 use를 사용해서 작명해주어야한다.
그래야만 리액트내에서 Custom Hook으로 인식한다.
매개변수로 plusOrMinus 라는 true or false 값을 받았고, 매개변수 값이 true라면 +를, false라면 - 계산을 해주도록 하였다.
import useCounter from '../hooks/use-counter';
import Card from './Card';
const ForwardCounter = () => {
const counter = useCounter(true);
return(<Card>{counter}</Card>);
};
export default ForwardCounter;
import useCounter from '../hooks/use-counter';
import Card from './Card';
const BackwardCounter = () => {
const counter = useCounter(false);
return (<Card>{counter}</Card>);
};
export default BackwardCounter;
코드의 가독성이 더 좋아졌고, 컴포넌트도 정상적으로 동작한다.
import { useState } from "react";
function App() {
const [inputValue, setInputValue] = useState('');
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = () =>{
alert(inputValue);
setInputValue('');
}
return (
<>
<h1>useInput</h1>
<input value={inputValue} onChange ={handleChange}></input>
<button onClick={handleSubmit}>확인</button>
</>
);
}
export default App;
인풋값을 받아서 버튼을 누르면, 받은 인풋값을 alert창에서 띄어주는 창이 있는데,
입력값을 받는 인풋창이 여러개가 있다고 가정해보자.
인풋창 갯수에 맞게 useState 갯수도 늘어날것이고, 인풋창 핸들러함수도 늘어날것이다.
이럴때 useInput 라는 Custom Hook을 만들어주면 상태관리가 깔끔해진다.
import { useState } from "react";
const useInput = (initailValue, submitAction) => {
const [inputValue, setInputValue] = useState(initailValue);
const handleChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = () => {
submitAction(inputValue);
setInputValue('');
}
return [inputValue, handleChange, handleSubmit];
}
export default useInput
import useInput from "./hooks/useInput";
const displayMsg = (msg) => {
alert(msg); // alert 창 띄우기
}
function App() {
const [inputValue, handleChange, handleSubmit] = useInput('', displayMsg);
// useInput의 리턴값을 받아옴
return (
<>
<h1>useInput</h1>
<input value={inputValue} onChange ={handleChange}></input>
<button onClick={handleSubmit}>확인</button>
</>
);
}
export default App;
위의 코드처럼 useInput 이라는 Custom Hook을 작성해주니 훨씬 코드의 가독성이 올라갔다.