요즘 리액트를 열심히 공부했는데, 포트폴리오도 리액트로 만들면 좋겠다 해서 리액트로 리메이크하고 있습니다.
원래 포트폴리오 main 페이지에서 사용하는 타이핑 이벤트를 적용하고자 해요.
일단 먼저, 어떻게 작용하는지 확인해봅시다!
main.js
// 타이핑 이벤트
const firstContent = "안녕하세요! 저는 프론트엔드 개발자 전유빈입니다.";
const firstText = document.querySelector(".text-1");
let i = 0;
function FirstTyping() {
if (i < firstContent.length) {
let txt = firstContent.charAt(i);
firstText.innerHTML += txt;
i++;
}
}
setInterval(FirstTyping, 120);
const secondContent = "성실하고 창의적인 개발자가 되는 것이 목표입니다!";
const secondText = document.querySelector(".text-2");
let j = 0;
function SecondTyping() {
if (j < secondContent.length) {
let txt = secondContent.charAt(j);
secondText.innerHTML += txt;
j++;
}
}
setInterval(SecondTyping, 100);
// 피아노 건반 사운드 이벤트
function playSound(soundFile) {
var audio = document.getElementById("audio");
audio.src = soundFile;
audio.play();
}
결과
다 되었으면, 이것을 이제 리액트로 변환해 봅시다!
Typing.jsx
import React from "react";
// 타이핑 이벤트
const firstContent = "안녕하세요! 저는 프론트엔드 개발자 전유빈입니다.";
const firstText = document.getElementById("text-1");x
let i = 0;
function TypingText() {
if(i < firstContent.length) {
let text = firstContent.charAt(i);
firstText.innerHTML += text;
console.log(text);
i++;
}
}
setInterval(TypingText, 100);
const Typing = () => {
return (
<>
<div className="mt-12 font-DNFForgedBladeNormal text-2xl text-mainColor text-center select-none">
<span id="text-1"></span>
</div>
<div>
<span id="text-2"></span>
</div>
</>
);
};
export default Typing;
결과
처음엔 잘 나오다가 새로고침 하니까 사진처럼 에러가 계속 발생하네요... 왜지..?
오류 메시지를 보니 Cannot read properties of null (reading 'innerHTML') 즉, 객체 or 변수가 null이거나 undefined라는 것이라고 합니다.
자세히 알아보니 React 생명주기와 관련된 문제라고 하네요.
※ React 생명주기 : 컴포넌트가 실행되거나 업데이트, 제거될 때, 특정 이벤트들이 발생하는 것
컴포넌트가 렌더링될 때, DOM 요소가 생성되지 않을 수 있으므로, DOM 요소에 접근하지 않는 것이 좋다고 합니다.
그래서 DOM을 직접 조작하는 것이 아닌, 가상 DOM을 사용하는 것이라고 하네요!
그래서 저는 useState와 useEffect를 통해 타이핑 이벤트를 실행하였어요.
Typing.jsx
import React, { useEffect, useState } from "react";
// 타이핑 이벤트
const Typing = () => {
const [firstText, setFirstText] = useState("");
const [secondText, setSecondText] = useState("");
const firstContent = "안녕하세요! 저는 프론트엔드 개발자 전유빈입니다.";
const secondContent = "성실하고 창의적인 개발자가 되는 것이 목표입니다!";
const [i, setI] = useState(0);
const [j, setJ] = useState(0);
useEffect(() => {
const interVal = setInterval(() => {
// 글자 하나 씩 넣기
if (i < firstContent.length) {
setFirstText((firstText) => firstText + firstContent.charAt(i));
setI((i) => i + 1);
} else {
// 전부 출력 시, interval 중지
clearInterval(interVal);
}
}, 120);
return () => clearInterval(interVal);
}, [i, firstContent.length]);
useEffect(() => {
const interVal = setInterval(() => {
// 글자 하나 씩 넣기
if (j < secondContent.length) {
setSecondText((secondText) => secondText + secondContent.charAt(i));
setJ((j) => j + 1);
} else {
// 전부 출력 시, interval 중지
clearInterval(interVal);
}
}, 120);
return () => clearInterval(interVal);
}, [j, secondContent.length]);
return (
<div className="mt-12 font-DNFForgedBladeNormal text-2xl text-mainColor text-center select-none">
<div>
<span id="text-1">{firstText}</span>
</div>
<div className="mt-4">
<span id="text-2">{secondText}</span>
</div>
</div>
);
};
export default Typing;
결과
정상적으로 작동이 되었습니다!
리액트로 코딩을 할 시, useState와 useEffect 같은 훅을 자주 사용해야 겠어요.
'Front-End Study > React' 카테고리의 다른 글
BrowserRouter과 HashRouter의 차이 (0) | 2024.06.16 |
---|---|
Material UI 설치하기 (0) | 2024.06.16 |
Vercel로 React 배포하기 (0) | 2024.05.05 |
Prop Drilling과 Contect API (0) | 2024.04.29 |
public 폴더에서 이미지 가져오기 (0) | 2024.04.22 |