타이머를 만들면서 정말 많은 내용을 배우고 있습니다.
useState를 왜 써야 하는지, useRef는 무엇인지, useEffect는 언제 사용하는 지에 대해서 정말 하나도 몰랐구나라는 생각이 들었습니다.
도대체 나는 이것들을 프로젝트때 어떻게 썼을까..
이번 포스팅은 단순히 useState로 관리했던 내용들을 useEffect를 통해 렌더링 이후에 관리하는 방식으로 보다 편리하고 눈에 보이도록 상태관리를 할 수 있다는 것을 경험한 내용을 담은 포스팅이 되겠습니다!
목차
0. useEffect란?
useEffect는 각종 Side Effect를 처리하기 위해서 만들어진 Hook입니다.
과거 class 생명주기에서는 componentDidMount, componentDidUpdate, componentWillUnmount와 연관이 깊습니다.
조금 더 쉽게 말하면, React가 DOM을 업데이트한 뒤에 추가로 코드를 실행해야 하는 경우가 있을 수 있습니다. 그런 경우에 useEffect를 사용할 수 있습니다.
// 간단 예시 - 리렌더링 되면 count가 바뀌었다면 화면도 count에 맞게 바뀌게 됩니다! useEffect(() => { document.title = `You clicked ${count} times`; });
useEffect 안에 있는 요소들은 컴포넌트가 리렌더링될 때마다 작동하게 됩니다.
만약 특정 요소가 변경되었을 때만 useEffect 내부 함수를 동작시키고 싶다면 두 번째 매개변수로 [] 안에 바라보는 요소를 넣으면 됩니다.
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // count가 바뀔 때만 effect를 재실행합니다.
그리고 useEffect의 반환값(return)에 들어가는 값이나 함수는 컴포넌트의 생명주기가 끝나게 되면 작동해서 메모리 누수가 발생하지 않도록 정리하는 역할을 담당합니다.
function FriendStatus(props) { // ... useEffect(() => { // ... ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
더 자세한 내용이나 예시는 아래 공식 문서를 통해 확인하실 수 있습니다!
1. useEffect를 만나기 전..
저는 단순히 타이머를 만들고 있었습니다.
재생 버튼을 눌렀을 때 second의 state값이 1초에 1씩 감소하고, 0이라면 minute의 값이 1 감소하고 second는 59가 되고... 와 같은 방식으로 작동하도록 만들었죠.
setState의 안에서 setState를 하고..를 반복하다보니 굉장히 불편해졌습니다.
그리고 가장 큰 문제는 저는 분명히 1씩 감소시키려고 했는데, 자꾸 2씩 감소가 되는겁니다.
console.log()로 찍어봐도 한 번씩만 작동하고, 다른 곳에서는 setMinute나 setHour를 쓰지도 않는데 말이죠.
임시 방편으로 prev2 - 0.5 처럼 만드니 잘 작동했습니다.
하지만 2번씩 작동하는 이유는 전혀 알 수 없었습니다.
그리고 그 원인을 해결하지 못해서 0.5로 넣어두는 것은 뭔가 찜찜했습니다.
이대로 완료처리를 할 수가 없었죠.
(테스트를 통해 짐작하기로는 setState안에 또 다시 setState가 들어가게 되면 함께 처리되는 것이 아니라 따로 처리되는 것 같습니다. depth가 3개로 늘어나면 3번씩 동작하더라구요!!)
2. useEffect를 만난 후
그래서 저는 useEffect를 사용해보기로 결정했습니다.
기존에 위와 같은 방법을 사용하게 된다면 기존 state 환경이 그대로 캡쳐되서 다음 Interval로 넘어가기 때문에 state를 console.log로 찍어볼 수 없습니다. (이것을 해결하기 위해서 prev => 형태로 넘겨주는 방식으로 처리하고 있었습니다.)
그래서 아예 useEffect에서 status가 play상태라면 1초마다 setTimeout을 통해 초가 줄어드는 동작을 실행하도록 코드를 짰습니다.
그리고 테스트 용도로 second를 찍어봤죠.
그랬더니 이제 state에 직접 접근해도 정상적으로 상태값을 console.log로 찍어볼 수 있게 되었습니다!
굳이 prev를 사용하면서 depth를 추가로 들어가지 않고도 간결하게 구현할 수 있었습니다!
추가로 시간이 바뀔 때와 상태가 바뀔 때만 useEffect가 작동하게 하고, 그 외의 상황에서는 작동하지 않도록 했습니다.
또한 컴포넌트의 생명주기가 끝났을 때 Timeout을 clear처리해줌으로써 브라우저의 부하를 줄이고 조금이나마 성능을 최적화할 수 있었습니다.
최근댓글