안녕하세요. 다시 돌아온 웹 배영경입니다😵💫
이번에는 Custom Hook에 대해 알아보았습니다!
React로 게임 만들기를 하면서, useState와 useEffect를 많이 썼는데요, Custom Hook는 어떤것인지 살펴보겠습니다.
Custom Hook 사용 이유
사용자 정의 후크는 재사용 가능한 로직을 캡슐화하여 components를 더 깔끔하고 유지 관리하기 쉽게 만들 수 있다.
사용자 정의 후크를 생성하면 복잡한 로직을 UI에서 분리하여 구성 요소를 더 단순하게 유지하고 렌더링에 집중할 수 있다.
- 다양한 구성 요소에서 상태 저장 논리를 재사용
- 복잡한 로직을 캡슐화하여 components를 더 단순하게 만듦
- 코드를 더 읽기 쉽고 테스트하기 쉽게 만듦
Custom Hook 만들기
사용자 정의 후크는 use 로 시작하도록 명명한다
예를 들어, 게임에서의 타이머 로직은 useTimer라는 사용자 정의 후크로 캡슐화될 수 있다.
이 후크는 다음을 관리한다.
- 타이머의 값(시간이 얼마나 경과했는지).
- 타이머를 시작하고 중지합니다.
- 타이머를 재설정합니다.
이 로직을 'useTimer'에 분리하면, UI 렌더링을 할 때 'Game' 컴포넌트에 집중할 수 있다.
useTimer 설정
useTimer 후크는 다음과 같이 구성할 수 있다.
useState, useEffect, useReducer, useCallback 등 Hooks 를 사용하여 원하는 기능을 구현해주고, 컴포넌트에서 사용하고 싶은 값들을 반환해주면 된다!
import { useState, useEffect, useCallback } from 'react';
const useTimer = () => {
const [timer, setTimer] = useState(0);
const [isRunning, setIsRunning] = useState(false);
// 타이머 시작
const startTimer = useCallback(() => {
setIsRunning(true);
}, []);
// 타이머 중지
const stopTimer = useCallback(() => {
setIsRunning(false);
}, []);
// 타이머 초기화
const resetTimer = useCallback(() => {
setTimer(0);
setIsRunning(false);
}, []);
// 타이머가 작동할 때, 50ms마다 타이머 업데이트
useEffect(() => {
let interval = null;
if (isRunning) {
interval = setInterval(() => {
setTimer((prevTimer) => +(prevTimer + 0.05).toFixed(2)); // 50ms 간격
}, 50);
}
return () => clearInterval(interval);
}, [isRunning]);
return { timer, startTimer, stopTimer, resetTimer, isRunning };
};
export default useTimer;
Game 컴포넌트에서 'useTimer' 후크 사용
정의된 useTimer를 Game 컴포넌트에서 사용할 수 있다.
'Game'이 더 단순화되고, Game 렌더링에 더 집중할 수 있게된다.
import React, { useEffect, useState } from 'react';
import styled from '@emotion/styled';
import useTimer from '../hooks/useTimer';
const Game = ({ nextNumber, setNextNumber, currentSet, setCurrentSet, resetGame }) => {
const { timer, startTimer, stopTimer, resetTimer, isRunning } = useTimer(); // custom hook 사용
const [numbers, setNumbers] = useState([]);
useEffect(() => {
setNumbers(shuffleArray(currentSet));
}, [currentSet]);
const shuffleArray = (array) => array.sort(() => Math.random() - 0.5);
const handleNumberClick = (num) => {
if (num === nextNumber) {
if (num === 1 && !isRunning) {
startTimer(); // 타이머 시작
}
setNextNumber((prev) => prev + 1);
const newNumbers = [...numbers];
const index = newNumbers.indexOf(num);
if (nextNumber <= 9) {
newNumbers[index] = getNextNumberInRange(10, 18);
} else if (nextNumber === 18) {
stopTimer(); // 게임이 끝났을 때 타이머 중지
alert(`게임 끝! 기록: ${timer.toFixed(2)} 초`);
resetGame();
} else {
newNumbers[index] = null;
}
setNumbers(newNumbers);
}
};
const getNextNumberInRange = (start, end) => {
const availableNumbers = Array.from({ length: end - start + 1 }, (_, i) => start + i).filter(n => !numbers.includes(n));
return shuffleArray(availableNumbers)[0];
};
return (
<GameBoard>
<h2>Next Number: {nextNumber}</h2>
<p>Time: {timer.toFixed(2)} seconds</p>
<GameButtonPlace>
{numbers.map((num, index) => (
num !== null ? (
<GameButton key={index} onClick={() => handleNumberClick(num)}>
{num}
</GameButton>
) : (
<EmptyButton key={index} />
)
))}
</GameButtonPlace>
</GameBoard>
);
};
export default Game;
Custom Hook를 사용해보기 위해 예를 든 것이므로, Custom Hook를 사용하기에 완전히 적합한 상황이 아닐 수 있다.
'3주차' 카테고리의 다른 글
[React] StrictMode (0) | 2024.11.01 |
---|---|
[React] - 개발 단계에서 고민해보면 좋은 것들(상태, 컴포넌트) (0) | 2024.11.01 |
[React] useEffect 훅과 의존성 배열 (0) | 2024.11.01 |
리액트 컴포넌트의 Lifecycle (0) | 2024.11.01 |
[3주차] JSX 알아보기 (0) | 2024.10.31 |