useState 상태값으로 보여지는 내용 분류하기 (Tab / select)
1. 버튼 Tab 방식
React의 useState hook으로 Tab 값을 관리하면서 선택된 Tab에 따라 보여지는 내용이 달라지는 메뉴 기능을 구현하려고 한다.
버튼 Tab 방식은 직관적인 상태 기반 렌더링으로, 구현된 결과물 UI는 다음과 같다.
기본적인 useState와 onClick 속성을 사용할 줄 안다면 간단하게 구현할 수 있다.
import { useState } from 'react'
const [tab, setTab] = useState('mint');
선택된 tab을 관리하기 위한 useState 변수를 생성하고 초기값은 'mint'로 지정한다. setTab은 tab 변수를 변경시키는 데에 사용되는 함수이다.
(간단한 mint와 not mint 메뉴 탭 예시)
<div className="w-full">
<div className="flex justify-start gap-4 mb-8">
<button
onClick={() => setTab('mint')}
className="border p-2 rounded-md flex items-center justify-center cursor-pointer hover:bg-gray-200"
>
mint{' '}
</button>
<button
onClick={() => setTab('not mint')}
className="border p-2 rounded-md flex items-center justify-center cursor-pointer hover:bg-gray-200"
>
not mint{' '}
</button>
</div>
{tab === 'mint' && <div> this is mint </div>}
{tab === 'not mint' && <div> this is not mint </div>}
</div>
각 button 태그는 onClick 이벤트 핸들러를 통해 setTab 함수를 호출하며, 선택된 탭 상태(tab)를 변경한다. setTab(setState)을 호출하면 React는 해당 컴포넌트를 다시 렌더링하며, 이 때 변경된 tab 값에 따라 조건부 렌더링({tab === 'mint' && ...} 등)이 실행되어 화면에 보여지는 내용이 동적으로 바뀌게 된다.
setState의 Shallow Comparison (얕은 비교)
React는 setState가 호출되면 기존 값과 새로운 값을 비교한다. 값이 다르다면 재렌더링이 이루어지고, 값이 동일하다면 생략한다.
객체, 배열, 함수는 참조 비교만 하기 때문에, 내용이 같더라도 주소가 다르면 렌더링이 이루어 진다.
const [user, setUser] = useState({ name: "dy" });
setUser({ name: "dy" });
같은 값이 들어있어도 새로운 객체가 생성되면 무조건 다르다고 판단하기 때문에 주의해야 한다.
2. SELECT option
React useState를 사용해서 유저 인터랙션에 따라 UI를 바꾸는 상태 기반 렌더링임은 동일하지만 Select 방식은 (team + season)과 같이 다중 조건 조합이 가능하다. 또한 팀이나 시즌이 늘어나면 option만 추가하면 되기 때문에 확장성이 좋다.
const { data, loading, error } = useQuery(SbTsDocument, {
variables: {
where: {},
take: 20,
},
});
apollo/client에서 제공하는 useQuery를 이용하여 sbt 리스트를 받아온다. useState 상태로 현재 선택된 team, season을 관리한다.
const [selectedTeam, setSelectedTeam] = useState<string>('');
const [selectedSeason, setSelectedSeason] = useState<string>('');
Array 인스턴스의 filter 메서드
.filter()는 원본 배열(sbts)을 변형하지 않고, 조건에 맞는 요소들을 골라 새로운 배열을 반환한다. (얕은 복사 shallow copy)
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Array.prototype.filter() - JavaScript | MDN
Array 인스턴스의 filter() 메서드는 주어진 배열의 일부에 대한 얕은 복사본을 생성하고, 주어진 배열에서 제공된 함수에 의해 구현된 테스트를 통과한 요소로만 필터링 합니다.
developer.mozilla.org
현재 선택된 team, season 상태값이 없다면 true로 지정하여 filter의 현재 조건을 무시하고 전체를 반환한다. && 연산자를 이용했기 때문에 두 조건을 모두 만족해야 해당된다.
const filteredSBTs = sbts.filter((sbt) => {
return (
(selectedTeam ? sbt.team === selectedTeam : true) &&
(selectedSeason ? sbt.season === selectedSeason : true)
);
});
사용자가 select에서 옵션을 변경하면 onChange 이벤트로 선택된 값이 setState되고, 상태 변경에 따라 컴포넌트가 자동으로 보여지는 UI가 리렌더링된다.
<select
className="border p-2 rounded"
value={selectedTeam}
onChange={(e) => setSelectedTeam(e.target.value)}
>
<option value="">전체 팀</option>
...
<option value="SSG랜더스">SSG</option>
<option value="롯데자이언츠">롯데</option>
</select>
<select
className="border p-2 rounded"
value={selectedSeason}
onChange={(e) => setSelectedSeason(e.target.value)}
>
<option value="">전체 시즌</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
<option value="2025">2025</option>
</select>