useEffect 잘 사용해서 무한루프 피하기

YUKI KIM·2021년 12월 30일
0

오늘 useEffect를 사용해서 채팅을 개발을 하던 중 무언가 이상한 점이 생겼다. useEffect로 update 해주는 state가 리랜더링 되는 시간이 이상할 정도로 오래 걸리고 심지어 아예 실행 되지도 않아서 엥. 이게 뭐람. 싶었다.


나의 문제 상황

나는 messages 라는 state가 update 될 때마다 리렌더링 하고자 한다. 그래서 useEffect를 사용하며 useEffect(func, [messages])의 형태로 코드를 짰다.

문제 발생 코드

const [message, setMessage] = useState('')
const [messages, setMessages] = useState([])
  
useEffect(() => {
    socket.on('message', (message) => {
        setMessages([...messages, message])
    })
}, [messages])

위 코드를 간략히 설명하자면, message를 받을 때마다, messages 리스트에 push 해주는 코드이다. 그러나 이 코드는 처음에는 빠른 속도로 messages 리스트가 update 되었으나 점점 느려지고 나중에는 아예 작동하지 않았다.

문제의 원인은?

페이스북 짱. 나와 같은 문제는 이미 리액트 공식문서에 나와 있었다. 그리고 아래와 같은 글을 확인할 수 있었다.

그니까 즉, 내가 개발하고 있던 것은 채팅이다. 그리고 나는 채팅 룸의 사용자들의 모든 대화를 messages라는 state에서 관리하고 있다. 그럼... 채팅의 특성 상 messages 배열은 자주 변경되게 될 것이고, 그래서 종속적인 state가 자주 변경되어서 무한루프에 빠지고 만 것이었다.


문제 코드 수정하기

빛이스북... 이런 경우 어떻게 해결해야 하는지도 공식문서에 친절하게 설명되어 있다. 그럼 우선 함수적 갱신을 짚고 넘어가자.

함수 컴포넌트 업데이트 폼

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}

이전 state를 사용해서 새로운 state를 계산하는 경우 함수를 setState 로 전달할 수 있다. 그 함수는 이전 값을 받아 갱신된 값을 반환한다. 위는 여기에 setState의 양쪽 형태를 사용한 카운터 컴포넌트의 예시다.

[]를 사용한 해결 코드

빈 종속성 세트, []는 컴포넌트가 mount 될 때마다 effect가 한 번만 실행되고 매번 렌더링 시에는 실행되지 않음을 의미한다.

useEffect(() => {
    socket.on('message', (message) => {
        setMessages(preMessages => [...preMessages, message])
    })
}, [])

messages state에 의존하는 것이 아니라, 정확한 이전 state를 제공하여 함수 컴포넌트 업데이트 폼으로 state를 update 해주도록 해주면 버그가 싹 사라진다. 편-안. 오늘도 새삼 다시 느끼는 공식 문서의 중요성.



레퍼런스

profile
유키링と 욘데 쿠다사이

0개의 댓글