커스텀훅으로 useTypingEffect 만들기
import { useState, useEffect } from "react";
function useTypingEffect(text, delay) {
const [typedText, setTypedText] = useState("");
useEffect(() => {
let currentIndex = 0;
const intervalId = setInterval(() => {
const currentLetter = text[currentIndex];
setTypedText((prevTypedText) => prevTypedText + currentLetter);
currentIndex++;
if (currentIndex === text.length) {
clearInterval(intervalId);
}
}, delay);
return () => {
clearInterval(intervalId);
setTypedText("");
};
}, [text, delay]);
return typedText;
}
export default useTypingEffect;
text와 delay를 전달받고 setInterval을 이용해 delay 마다 빈문자열에 text를 한글자씩 붙여주는 방식이다.
setState((prevState) => {
return()
});
useState 세터 함수의 인자로 콜백함수를 넣어주게 되면
useState(() => {
return()
});
useState를 콜백 형식으로 초기값을 지정하면 첫 렌더링 시에만 한 번 콜백을 실행해서 초기값을 만들고, 그 이후에는 콜백함수를 실행하지 않는다.
따라서 useState 초기값 지정시 오래걸리는 작업이 있을때 사용해주면 좋다.
단, 콜백 함수가 리턴할 때까지 React가 렌더링을 하지 않고 기다리게 된다.
const TerminalLine = React.memo(function TerminalLine({ text }) {
let typingText = useTypingEffect(text, 60);
return (
<div>
{typingText}
</div>
);
});
export default TerminalLine;
나의 경우 타이핑을 한번만 하고 끝내는것이 아니라 여러문장을 나타내야했는데 이전 문장이 완료되는 시점에 다음줄의 타이핑이 시작되어야했다.
그렇기 때문에 useTypingEffect을 이용해 첫번째 타이핑이 끝나는 시간을 다음줄의 시작 시간으로 설정하고 그 다음줄은 이전 모든 줄의 타이핑이 끝나는 시간으로 시작 시간을 설정한다.
useEffect(() => {
let timeoutId;
const arrTimeoutId = [];
const startTyping = () => {
let delay = 500;
for (let i = 0; i < text.length; i++) {
const textLine = text[i];
timeoutId = setTimeout(() => {
if (i > 0) {
// 완료된 이전 문장들을 담을 state
setCompleteTyping(text.slice(0, i));
}
// useTypingEffect에 전달할 text state
setTypingText(textLine);
}, delay);
arrTimeoutId.push(timeoutId);
// 이전 문장의 길이 * 타이핑 속도에 여유시간(500ms)를 더해 다음 타이핑 시작 시간을 조절한다.
delay += text[i].length * 60 + 500;
}
};
startTyping();
return () => {
// clearTimeout(timeoutId);
arrTimeoutId.forEach((id) => clearTimeout(id));
};
}, [text]);
참고
setTimeout을 이렇게 여러번 사용해본적이 처음이라 clear를 잘못하고 있었다는걸 몰랐었다. 타이핑 이펙트 주는 화면이 여러개라 전환을 하면 이전 화면 텍스트가 잠깐 나타났다가 사라지곤 했는데 마지막 주석처리 한것처럼 컴포넌트 언마운트 시 타이머를 클리어 하도록 timeoutId 하나만 clearTimeout 함수의 인자로 전달하면 제일 마지막 setTimeout만 클리어된다.(for문에서 setTimeout 호출할때마다 반환하는 id를 timeoudId 변수에 할당하는데 for문이 끝나고 난 다음에는 제일 마지막 id만 할당되어있는 상태다.)
setTimeout을 호출하면 타이머를 식별하는 id값을 반환하는데 실행 대기중인 모든 타이머 id를 clear해줘야한다.