탭 메뉴 만들기: 클릭 시 기능적 에러 해결

문제상황

선택된 탭에만 스타일이 새로 적용되는 것 까지는 잘 구현했지만,
하나가 선택된 상황에서 다른 탭을 선택하려고 할 때 두번 클릭해줘야 하는 현상이 일어났다.

문제의 코드는 다음과 같다.

export const Tab = () => {
  // TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한
  // currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다.

  const menuArr = [
    { name: 'Tab1', content: 'Tab menu ONE' },
    { name: 'Tab2', content: 'Tab menu TWO' },
    { name: 'Tab3', content: 'Tab menu THREE' },
  ];
  const [currentTab, setCurrentTab] = useState(0)
  const [isSelected, setIsSelected] = useState(false)

  const selectMenuHandler = (index) => {
    // TIP: parameter로 현재 선택한 인덱스 값을 전달해야 하며, 이벤트 객체(event)는 쓰지 않습니다
    // TODO : 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성하세요.
    setCurrentTab(index)
    setIsSelected(!isSelected)
    console.log("작동된다~","배열인덱스:",index,"현재선택된인덱스:",currentTab)
  };

  return (
    <>
      <div>
        <TabMenu>
          {/*TODO: 아래 하드코딩된 내용 대신에, map을 이용한 반복으로 코드를 수정합니다.*/}
          {/*TIP: li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며, 
                  나머지 2개의 tab은 'submenu' 가 됩니다. 조건문에서 && selected 같이 썼더니 해제를 해야 다른 탭 선택이 가능했다 */}
          {menuArr.map((ele,idx) => (
            <li 
              className = {idx === currentTab && isSelected? 'submenu focused': 'submenu'} 
              onClick ={() => selectMenuHandler(idx)}
            >
              {ele.name}
            </li>
            ))
            }
          {/* <li className="submenu">{menuArr[0].name}</li>
          <li className="submenu">{menuArr[1].name}</li>
          <li className="submenu">{menuArr[2].name}</li> */}
        </TabMenu>
 ...후략

원인을 찾고 보니, 삼항연산자를 이용한 조건문에서 isSelected를 더 쓴 것이 원인이었다.
이 조건문에서 왜 이런 현상이 일어났는지 분석해보았다.

1) 조건문의 해석
-주어진 배열의 인덱스와, 현재 선택된 인덱스가 동일해야 하며,
-isSelected의 값이 true여야 한다(선택된 상태여야 한다)

2) 선택되어서 파란색으로 표시된 탭은 위 두 조건을 모두 만족하고 있는 상태이다.
다른탭은 클래스가 submenu이기 때문에 파란색으로 표시가 안된다.

3) 초기 상태에서는 탭의초기값이 0이기 때문에 바로 파란색으로 칠해진다.

4) 그런데 그 이후부터는 콘솔에 다음과 같이 찍힌다.

다른 탭을 한번 클릭했을때는 이전에 누른 탭의 파란색이 사라지고,
다시 한번 클릭했을 때서야 파란색이 나타나게 된다.
콘솔에 현재선택된인덱스 라고 나와있는데, 이건 실제로는 현재 선택된 인덱스가 아니고 이전에 선택된 인덱스다.
콘솔 출력 위치가 이벤트 핸들러 함수 안에 있기 때문에 업데이트된 변수의 값을 보지 못한다.
따라서 컴포넌트의 리턴문에서 값을 확인해 봤더니 , 그제서야 일치하는 것을 알 수 있었다.

5) 클릭을 했는데도 선택이 안된 것을 보면 두 조건 중 한가지 조건을 충족하지 못했기 때문이다.
그래서 isSelected와 currentTab값을 컴포넌트 리턴문에서 확인해봤다.

초기에는 0 값으로 잘 나왔는데, 0번 선택된 상태에서 이후 1번 탭을 클릭하니 1 false가 찍혔다.
왜냐하면 불린 값을 가지는 state변수는 같은 li엘리먼트(0번)를 다시 클릭해줘야만 되는데, 여기서는 1번
엘리먼트를 클릭해줬기 때문에 false값이 그대로 남은 것이다. 사실 이 false 값은 1번의 false 값이 아니고 0번의 false 값이기도 하고, 2번의 false 값이기도 하다,
왜냐면 페어분과 처음에는 isSelected 만을 사용하여 코드를 작성했는데 그때는 모든 탭이 다 파란색으로 선택되었기 때문이다. 동시에 세개의 엘리먼트가 상태 변화를 일으킨 것이다.
map을 이용하여 여러 개의 리스트를 만들고, 그 중 특정 요소만의 상태를 변화시키려면 인덱스를 이용하는 것이 맞겠다는 것을 알수있었다.

좋은 웹페이지 즐겨찾기