useState: 상태 변수를 선언하고 관리. 예시:
const [count, setCount] = useState(0);
useEffect: 컴포넌트의 부수 효과(side effect)를 수행. 예시:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
useContext: 컨텍스트를 구독하여 현재 컨텍스트 값을 가져옴. 예시:
const value = useContext(MyContext);
그 외에도 useReducer, useMemo, useCallback, useRef 등 다양한 훅이 있음.
함수 컴포넌트 최상위 위치에서 호출
const MyComponent = () => {
// 올바른 사용
const [state, setState] = useState(initialState);
// 잘못된 사용(컴포넌트의 최상위 위치 X)
if (someCondition) {
useEffect(() => {
}, []);
}
// 잘못된 사용(컴포넌트의 최상위 위치 X)
const MyFunc = () => {
const [test, setTest] = useState(null);
}
}
커스텀훅 최상위 위치에서 호출
const useCustomHook = () => {
const [state, setState] = useState(initialState);
return {state};
}
DB에서 데이터를 불러오는 모듈을 생성했다.(fetch.js)
const fetch = async () => {
const returnData = await load();
return returnData;
}
해당 모듈은 데이터를 불러오는 함수와 불러온 데이터를 반환(return)하는 구조를 가진다.
이 때, 해당 모듈에서 데이터를 불러오는 함수는 비동기 방식으로 작동한다.
컴포넌트에서 렌더링 시 데이터가 불러와질 수 있도록 아래와 같이 코드를 작성했다.
const Component = () =>{
const loadData = fetch.js() // 데이터 로드
console.log(loadData); // 데이터 콘솔 찍기
return (
<>
<>
)
}
그런데 콘솔에 찍힌 데이터가 promise 객체이다.
이는 fetch에서 데이터를 불러오는 load 함수가 비동기함수이기 때문이다.
이를 해결하기 위해 아래와 같이 코드를 수정하였다.
const Component = () =>{
const [myData, setMyData] = useState(null);
// 데이터 로드 후 myData 상태에 반영
const myFunc = async() => {
const loadData = await fetch.js();
setMyData(loadData);
}
console.log(myData); // 데이터 콘솔 찍기
return (
<>
<>
)
}
단지 데이터를 불러오는 모듈을 콘솔찍기 위함인데도 불구하고 컴포넌트 내부 코드가 많이 복잡해졌다.
가독성 및 재사용성을 위해 커스텀훅으로 변경한다.
// useFetch 커스텀훅으로 변경
const useFetch = () => {
const [loadData, setLoadData] = useState(null);
const fetchFunc = async () => {
const data = await load();
setLoadData(data)
}
return {loadData, fetchFunc };
}
// 변경된 컴포넌트 내부 코드
const Component = () =>{
const {loadData, fetchFunc } = useFetch();
fetchFunc();
console.log(loadData);
return (
<>
<>
)
}
위처럼 커스텀훅을 사용하면 코드가 분리됨으로써 가독성과 재사용성에 도움이 된다.
// usePostUser.js
// DB에 유저 정보 저장
const usePostUser = () => {
const [isError, setIsError] = useState();
// 커스텀훅 가져다 쓸 때 이벤트 핸들러 등에 등록할 수 있도록 함수를 따로 분리 후 반환
// 외부에서 아래 함수를 실행 시 isError에 에러 여부가 반영된다.
const postUser = async (userData) => {
try {
await addDoc(collection(db, "users"), userData);
setIsError(false);
} catch (e) {
console.error("오류 발생 - usePostUser.js \\n", e);
setIsError(true);
}
};
return { postUser, isError };
};
export default usePostUser;