최근 useEffect의 호락호락하지 않음을 경험하고 좀 더 자세히 공부해야겠다 라는 생각이 들었습니다.
무슨일이었냐면 제가 작업 중이던 프로젝트에서useEffect
의존성 배열인자가 계속 호출되어 무한루프가 발생했습니다.
그 결과로파이어베이스
에서 무료로 사용할 수 있도록 할당된 읽기50000건
을 하루만에 사용해 버렸습니다.🧔🏻♀️
=> useGetFeedData.tsx
import { getDoc, doc } from 'firebase/firestore';
import { appFireStore } from '@/firebase/config';
import useAuthContext from '@/hook/useAuthContext';
import useAuthContext from '@/hook/useAuthContext';
export default function useGetFeedData() {
const { user } = useAuthContext();
const getFeedData = async (feedId: string) => {
if (user === null) {
return;
}
try {
const docSnap = await getDoc(doc(appFireStore, 'feed', feedId));
return docSnap.data();
} catch (error) {
console.log(error);
}
};
return getFeedData;
}
문제 발생 이유
해결방법
이 과정을 겪으며 나 너무 useEffect라는 훅을 띄엄띄엄 보고 있었구나 공부해야 겠다는 마음을 많이 느꼈습니다.
useEffct
의 실행순서
는 컴포넌트의 생명주기
와 밀접한 관계가 있습니다. 그래서 먼저 리액트의 생명주기에 대해 이해하고 있는게 중요합니다.
컴포넌트의 생애주기는 크게 세 가지로 나눌 수 있습니다.
컴포넌트는 생성(mounting) -> 업데이트(updating) -> 제거(unmounting)
이미지 출처: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
(1) 마운트 : 처음 컴포넌트가 나타났을 때
처음 state와 props를 가지고 컴포넌트를 생성합니다.
constructor
getDerivedStateFromProps
render
componentDidMount
(2) 업데이트 : 컴포넌트 상태값이 바뀔 때
마운트 완료 후 상태값이나 prop의 변화가 생겼을 때 리액트가 감지하고 컴포넌트에 업데이트해줍니다.
getDerivedStateFromProps
shouldComponentUpdate
render
componentWillUpdate
componentDidUpdate
(3) 언마운트 : 컴포넌트가 사라질 때
언마운트에서는 componentWillUnmount가 실행됩니다. 컴포넌트를 완전히 DOM에서 제거하는 시점
componentWillUnmount : 컴포넌트가 사라지기 바로 직전에 호출
useEffect(setup, dependencies)
useEffect
의 실행 순서에 대해 간단한 예제로 살펴보겠습니다. (예제는 멋사5기 교안을 참고했습니다.)
// 1.컴포넌트가 업데이트 될 때마다 매번 실행
useEffect(()=>{
console.log('hello world');
})
// 2.처음에만 실행
useEffect(()=>{
console.log('hello world');
}, [])
// 3.변수들의 변화가 일어날 때마다 실행
useEffect(()=>{
console.log('hello world');
}, [변수1, 변수2...])
- 성능이슈 발생 코드
import React, { useState, useEffect } from 'react';
function Time(props) {
const [today, setToday] = useState(new Date());
const [hour, setHour] = useState(today.getHours());
const [min, setMin] = useState(today.getMinutes());
const [sec, setSec] = useState(today.getSeconds());
console.log("렌더링이 됩니다..?")//렌더링이 잘 되는지 확인용! 꼭 넣고 진행해주세요.
// 성능이슈가 발생되는 공간
setInterval(() => {
const t = new Date();
setToday(t);
setHour(t.getHours());
setMin(t.getMinutes());
setSec(t.getSeconds());
}, 1000);
return (
<div>
<h1>
시간 : {hour}시 {min}분 {sec}초
</h1>
</div>
);
}
function App() {
return (
<div>
<Time/>
</div>
);
}
export default App;
- 클린업 함수 사용 코드
import { useState, useEffect } from "react";
function Time(props) {
const [today, setToday] = useState(new Date());
const hour = today.getHours();
const min = today.getMinutes();
const sec = today.getSeconds();
console.log("렌더링이 됩니다..?")
useEffect(() => {
let time = setInterval(() => {
const t = new Date();
setToday(t);
}, 1000);
return () => {
clearInterval(time);
};
}, [today]);
return (
<div>
<h1>
시간 : {hour}시 {min}분 {sec}초
</h1>
</div>
);
}
export default Time;