리액트 컴포넌트의 리렌더링과 state (문제 해결)

Devinix·2024년 5월 19일
0

[문제 해결]

목록 보기
21/29
post-thumbnail

개요

React를 이용하여 네이버 지도에 텃밭들을 표시하고, 텃밭에서 찜하기 버튼을 누르면 해당 텃밭을 찜하는 기능을 구현하고 있었다.

문제 상황

리액트 컴포넌트에서 garden?.gardenLikeId를 기반으로 isGardenLiked 상태가 계산되며, 이는 사용자가 정원을 "좋아요" 했는지 여부를 나타내고 있는 상황이다. 이 값에 따라 '찜하기' 버튼의 상태가 변경되어야 하는데, liked라는 내부 상태가 초기에 한 번 설정된 후 자동으로 업데이트되지 않아 isGardenLikedliked의 값이 일치하지 않는 문제가 발생했다.

const MapGardenDetailBottomSection = ({
  garden,
  refetch,
}: MapGardenDetailBottomSectionProps) => {
  const [loading, setLoading] = useState<boolean | undefined>(undefined);
  
  
  // isGarenLiked 값과 liked 상태값이 다른 이슈가 발생함.
  const isGardenLiked = garden?.gardenLikeId === 0 ? false : true;
  const [liked, setLiked] = useState(isGardenLiked);
  
  
  const { isOpen, onOpen, onClose } = useDisclosure();
  const [copied, setCopied] = useState(false);
  const [isMobile, setIsMobile] = useState<boolean | null>(null);
  const [isClickedCallInWeb, setIsClickedCallInWeb] = useState(false);

  const { mutateLikeGarden } = useLikeGarden(liked, garden?.gardenId, setLiked);
                                             
  // 생략...                                             	

  const handleClickLike = () => {
    setLoading(true);
    if (liked)
      mutateLikeGarden({ type: 'cancel', gardenLikeId: garden?.gardenLikeId });
    else mutateLikeGarden({ type: 'like', gardenLikeId: garden?.gardenLikeId });

    setTimeout(() => {
      refetch();

      setTimeout(() => {
        setLoading(false);
      }, 150);
    }, 250);
  };

  return (
	// 생략...
  );
};

export default MapGardenDetailBottomSection;

원인

이 문제의 원인은 리액트 상태 관리의 특성에서 기인하였다. 리액트에서는 상태가 변경되면 해당 상태에 의존하는 컴포넌트의 리렌더링이 트리거된다. 그러나, 상태 초기화는 컴포넌트가 마운트될 때 단 한 번만 발생하며, 이후에는 상태 업데이트 함수를 호출하지 않는 한 기존 상태가 유지된다. useState로 선언된 liked 상태는 외부에서 gardenLikeId가 변경되어도 자동으로 업데이트되지 않기 때문에, isGardenLiked의 변경이 liked에 반영되지 않는 것이었다.

해결 과정

이 문제를 해결하기 위해 useEffect를 사용하여 gardenLikeId의 변경을 감지하고, 이에 따라 liked 상태를 업데이트하는 방법을 구현했다. useEffect 내에서 gardenLikeId를 의존성 배열로 설정하여, 해당 값이 변경될 때마다 liked 상태를 적절히 업데이트하도록 했다.

  // 추가
  useEffect(() => {
    setLiked(isGardenLiked);
  }, [isGardenLiked]);

결과

결론

리액트 컴포넌트가 리렌더링 될 때 변수와 상수의 값들은 초기화가 된다. 이 초기화된 상수(isGardenLiked)를 liked 상태의 초기값으로 할당해주고 있어서.. 당연히 liked 상태의 값이 초기화된 isGardenLiked값으로 업데이트가 될줄 알았다. 하지만 이것은 나의 큰 오해였다. 해결방안으로, 수동으로 useEffect를 이용해서 isGardenLiked의 값이 변할때마다 liked 상태를 업데이트 해주는 것으로 간단하게 문제를 해결해 줄 수 있었다.

리액트에서 useEffect는 컴포넌트의 생명주기 동안 의존성 배열 값의 변화에 대응하여 내부 상태를 업데이트할 수 있는 강력한 hook이다. 이 사례를 통해 useEffect를 적절히 사용하면 상태 관리 로직을 보다 효율적으로 만들고, UI의 일관성과 반응성을 향상시킬 수 있음을 확인할 수 있었다. 이러한 패턴은 리액트 애플리케이션에서 자주 발생하는 문제에 대한 해결책을 제공하며, 효과적인 사용자 경험을 구현하는 데 중요한 역할을 한다.

profile
프론트엔드 개발

0개의 댓글