setState 비동기 해결 (useEffect 사용 안함)

Chaeeun Lee·2023년 7월 18일

setState 밀리는 현상😡

코인네버다이 프로젝트 때에도 그러더니 이번에도 날 힘들게 했다.
프로젝트 때는 useEffect 를 두개를 만들어서 dependency에 각각의 state 값을 넣어서 해결했다.
하지만 이번에는 useEffect 만으로는 완전히 해결할 수 없어서 다른 방법을 찾아야했다.

1. 문제 상황 🤔

탭 컴포넌트를 구현해야 하는데, 탭을 선택할 때마다 이벤트 아이디가 변경되어야 한다.

예들 들어,

  • 0번 탭을 클릭하면 이벤트아이디가 0번으로 업데이트 되야한다.
  • 1번 탭을 클릭하면 이벤트아이디가 1번 탭으로 업데이트 해야 된다.

setState의 비동기적 작동을 전혀 생각하지 못하고 단지 setState를 연달아 작성해줬다. 연달아 작성하면 동기적으로 작동하겠지...라는 말도 안되는 생각을 무의식적으로 하고 있었다. 저번에도 당해놓고 정신을 못차렸다.

버튼의 onClick 주목❗️
setActiveTab 실행 이후에 업데이이트 된 activeTab 값을 바탕으로 setEventId가 실행될 줄 알았다.
0번 탭을 클릭하면 activeTab 값이 0번으로 업데이트 되고 그에 맞춰서 eventId 값이 0으로 업데이트 될 줄 알았지만, 0번 탭 클릭 시점의 activeTab 값이 0번이 아니었기 때문에 eventId 가 1이 되었다...

문제의 코드

const [activeTab, setActiveTab] = useState(0);
const [eventId, setEventId] = useState(null);

const Tab = forwardRef(({ tabs, activeTab, setActiveTab, setEventId }, ref = '') => {
  return (
    <div ref={ref}>
      <div>
        {tabs.map((tab, index) => {
          const isActiveTab = activeTab === index;
          return (
            <button
              key={tab.key}
              active={`${isActiveTab}`}
              onClick={() => {
                setActiveTab(index)
				activeTab === 0 ? setEventId(0) : setEventId(1) 
				}
            >
              <span>{tab.text}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
});

2. 문제 해결 🥳

setState 비동기 해결을 구글에 검색하면 과장해서 열에 아홉은 useEffet를 사용하라고 한다. 하지만 useEffet로도 해결할 수 없는 상황이 간혹 있다!!!!!!

나도 처음에는 useEffet 를 사용했다. 의존성 배열에 activeTab을 넣고, useEffect 안에서 setEventId를 실행했다. 하지만 activeTab 값이 변하지 않더라도 이벤트아이디는 변경되어야 할 때를 다룰 수 없어서 새로운 해결방법을 찾아야 했다.

그 결과, functional argument (한국어로 뭐라고 하는지 모르겠다) 를 사용하여 해결할 수 있었다. 이렇게 해서 문제도 완벽하게 해결하고 복잡했던 useEffet 코드도 싹 다 날려버릴 수 있었다!!!! 유후

버튼의 onClick 주목❗️
업데이트 된 상태값을 직접적으로 받아서 setSelectedEventId 를 실행해줬다.
어떻게 더 설명해야할지 모르겠다. 그냥 코드로 보자

현재 코드

const [activeTab, setActiveTab] = useState(0);
const [eventId, setEventId] = useState(null);

const Tab = forwardRef(({ tabs, activeTab, setActiveTab, setEventId }, ref = '') => {
  return (
    <div ref={ref}>
      <div>
        {tabs.map((tab, index) => {
          const isActiveTab = activeTab === index;
          return (
            <button
              key={tab.key}
              active={`${isActiveTab}`}
              onClick={() => {
                setActiveTab((prev) => {
                  const updatedIndex = index;
                  if (updatedIndex === 0) setSelectedEventId(0);
                  else setSelectedEventId(1);
                  return updatedIndex;
                });
              }}
            >
              <span>{tab.text}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
});

3. 참고

스택 오버플로우를 다 뒤졌는데 이 블로그가 가장 유용했다. 사랑해요...❤️

profile
나는야 뚝딱이 개발자야

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

정말 잘 읽었습니다, 고맙습니다!

답글 달기