리액트 훅을 공부하면서, class component 기반 코드와 hook을 이용한 코드를 비교하던 중에 풀리지 않는 문제가 생겼다.
처음에는 "Loading..." 을 렌더링하다가 3초 후에 "we are ready" 를 렌더링 하는 (비교적 매우 간단한...) 예제이다.
import React, { Component } from "react";
class App extends React.Component {
state = {
isLoading: true,
};
componentDidMount() {
setTimeout(() => {
this.setState({
isLoading: false,
});
}, 3000);
}
render() {
return
<div>
{this.state.isLoading ? "Loading..." : "we are ready"}
</div>;
}
}
export default App;
이렇게 class로 작업을 하면, render()
함수가 실행 된 뒤에 그대로 값을 유지하기 위해서 componentDidMount()
를 쓰는 것 까지는 이해가 됐는데, 이 코드를 hooks 코드로 변형을 해보니 (👇 아래 코드)
import React, { useState } from "react";
const App = () => {
const [isLoading, setIsLoading] = useState(true);
setTimeout(() => {
setIsLoading(!isLoading);
}, 3000);
return <h1>{isLoading ? "Loading" : "we are ready"}</h1>;
};
export default App;
잘 되는 것 같이 보였지만 Loading → we are ready → Loading → we are ready → Loading ... 이렇게 계속 렌더링이 반복 되었다.
상태가 바뀌어서 그런가? 하고
setTimeout()
부분을
useEffect(() => {
setTimeout(() => {
setIsLoading(!isLoading);
}, 3000);
});
useEffect()
로 감싸주었는데도 계속 반복...
처음으로 stackoverflow 에 질문을 해봤는데 1시간도 지나지 않아 댓글이 달렸다! 😂
답변
함수형 컴포넌트는 계속해서 렌더링이 된다고 한다. useEffect()
를 쓰는 접근은 맞았지만 끝에 ,[]
를 추가해 줘야 한다는 것.
위 링크의 리액트 document 를 참고해보니,
효과종속성이 너무 자주 변경되는 경우
빈 종속성 집합은 []구성 요소가 마운트 될 때마다 효과가 한 번만 실행되고 모든 다시 렌더링시 실행되지 않음을 의미합니다.
처음 클래스 컴포넌트에서, componentDidMount()
는 rendering이 모두 끝난 후 실행 되는 함수이므로 문제가 발생하지 않는다.
하지만 함수형 컴포넌트에서는 위 예제처럼 계속해서 redering이 되는 경우, 반복되는 life cycle을 막기 위해 state가 변경된 후 빈 배열로 mount를 지정해주어야 한다.
useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 3000);
});
setIsLoading
값을 아예 boolean으로 주면 re-rendering이 발생하지 않음.
최근 class형으로 componentDidMount() 코드 구성을 연습해보다가 hooks로 바꿔보고 싶어 자료를 찾아보던 중이었는데 덕분에 도움이 되었습니다. 감사합니다!! 😁