[React] 조건부 스타일링을 활용한 탭 활성화 기능 구현
경로와 매칭되었을 때 특정 스타일이나 클래스를 적용할 수 있는 기능을 사용하고 싶은 경우는 [NavLink↗️]를 사용하도록 하자!
1. Styled Components로 tap 활성화 구현하기
어떤 탭이 클릭되었는지 아래처럼 style-components의 조건부 스타일링을 적용하여 구현해보자!
1.1 적용 전 초기 코드
import styled from "styled-components";
function Tabs() {
const tabItems = ["토토로", "키키", "포뇨", "치히로", "소피", "가오나시"];
return (
<TabsList>
<TabsTitle>누구에게 보내실 건가요?</TabsTitle>
{tabItems.map((item, index) => {
return <TabsItem key={index}>{item}</TabsItem>;
})}
</TabsList>
);
}
const TabsList = styled.ul`
background-color: #ffffff;
width: 390px;
border-radius: 13px;
padding: 45px;
margin-right: 30px;
`;
const TabsTitle = styled.h1`
font-size: 18px;
font-weight: bold;
margin-bottom: 30px;
`;
const TabsItem = styled.li`
border-radius: 8px;
border: 2px solid #bababa;
height: 65px;
margin-bottom: 15px;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
`;
export default Tabs;
1.2 state 생성
useState 훅을 사용하여 현재 활성화된 탭을 관리하자.
const [activeTab, setActiveTab] = useState("토토로");
1.3 tap 렌더링
- tabItems 배열을 순회하며 각 탭을 렌더링하자. 각 탭이 클릭되면 setActiveTab을 호출하여 상태를 업데이트한다.
- 그리고 props로 조건부 스타일을 주자. 스타일 컴포넌트 특성상 카멜 케이스 사용시 $를 앞에 붙여줘야한다!
<TabsList>
<TabsTitle>누구에게 보내실 건가요?</TabsTitle>
{tabItems.map((item, index) => (
<TabsItem
key={index}
$isActive={item === activeTab}
onClick={() => setActiveTab(item)}
>
{item}
</TabsItem>
))}
</TabsList>
1.3 조건부 스타일링
props.$isActive를 사용하여 활성화된 탭과 비활성화된 탭에 대해 서로 다른 스타일을 적용하자
import styled, { css } from "styled-components"; // import(retrun에 css치고 tap누르면 자동 import)
const TabsItem = styled.li`
border-radius: 8px;
border: 2px solid #bababa;
height: 65px;
margin-bottom: 15px;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
${(props) =>
props.$isActive
? css`
border: 2px solid black;
font-weight: bold;
`
: css`
border: 2px solid #bababa;
font-weight: normal;
`}
`;
1.4 전체코드
import { useState } from "react";
import styled, { css } from "styled-components";
const Tabs = () => {
const [activeTab, setActiveTab] = useState("토토로");
const tabItems = ["토토로", "키키", "포뇨", "치히로"];
return (
<TabsList>
<TabsTitle>누구에게 보내실 건가요?</TabsTitle>
{tabItems.map((item, index) => (
<TabsItem
key={index}
$isActive={item === activeTab}
onClick={() => setActiveTab(item)}
>
{item}
</TabsItem>
))}
</TabsList>
);
};
const TabsList = styled.ul`
background-color: #ffffff;
width: 390px;
border-radius: 13px;
padding: 45px;
margin-right: 30px;
`;
const TabsTitle = styled.h1`
font-size: 18px;
font-weight: bold;
margin-bottom: 30px;
`;
const TabsItem = styled.li`
border-radius: 8px;
border: 2px solid #bababa;
height: 65px;
margin-bottom: 15px;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
${(props) =>
props.$isActive
? css`
border: 2px solid black;
font-weight: bold;
`
: css`
border: 2px solid #bababa;
font-weight: normal;
`}
`;
export default Tabs;
2. Context API 사용하기
2.1 TabsContext 생성
로컬스토리지와 세션 스토리지의 차이는 [여기↗️] 서 확인하자!
로컬스토리지와 세션 스토리지를 사용해서 새로고침 시에도 데이터가 유지되도록 해보자!
2.1.1 [방법1] sessionStorage 사용
import { createContext, useContext, useState, useEffect } from "react";
const TabsContext = createContext();
export function TabsProvider({ children }) {
// sessionStorage에서 초기 상태를 가져오거나 "토토로"로 초기화
const [activeTab, setActiveTab] = useState(
() => sessionStorage.getItem("activeTab") || "토토로"
);
useEffect(() => {
// activeTab이 변경될 때마다 sessionStorage에 저장
sessionStorage.setItem("activeTab", activeTab);
}, [activeTab]);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
);
}
export function useTabs() {
return useContext(TabsContext);
}
2.2.2 [방법2] localStorage 사용
import { createContext, useContext, useState, useEffect } from "react";
const TabsContext = createContext();
export function TabsProvider({ children }) {
// localStorage에서 초기 상태를 가져오거나 "토토로"로 초기화
const [activeTab, setActiveTab] = useState(
() => localStorage.getItem("activeTab") || "토토로"
);
useEffect(() => {
// activeTab이 변경될 때마다 localStorage에 저장
localStorage.setItem("activeTab", activeTab);
}, [activeTab]);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
);
}
export function useTabs() {
return useContext(TabsContext);
}
2.3 Context Provider로 감싸기
import Tabs from "components/ContextPractice/Tabs";
import { TabsProvider } from "../context/TabsContext"; // Import the provider
const HomePage = () => {
return (
<div>
<TabsProvider>
<Tabs />
</TabsProvider>
</div>
);
};
export default HomePage;
2.4 Tabs 컴포넌트에서 Context 사용하기
import styled, { css } from "styled-components";
import { useTabs } from "../../context/TabsContext";
function Tabs() {
const tabItems = ["토토로", "키키", "포뇨", "치히로", "소피", "가오나시"];
const { activeTab, setActiveTab } = useTabs();
return (
<TabsList>
<TabsTitle>누구에게 보내실 건가요?</TabsTitle>
{tabItems.map((item, index) => (
<TabsItem
onClick={() => setActiveTab(item)}
$isActive={item === activeTab}
key={index}
>
{item}
</TabsItem>
))}
</TabsList>
);
}
const TabsList = styled.ul`
background-color: #ffffff;
width: 390px;
border-radius: 13px;
padding: 45px;
margin-right: 30px;
`;
const TabsTitle = styled.h1`
font-size: 18px;
font-weight: bold;
margin-bottom: 30px;
`;
const TabsItem = styled.li`
border-radius: 8px;
border: 2px solid #bababa;
height: 65px;
margin-bottom: 15px;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
${(props) =>
props.$isActive
? css`
border: 2px solid black;
font-weight: bold;
`
: css`
border: 2px solid #bababa;
font-weight: normal;
`}
`;
export default Tabs;
아래와 같이 구현할 수도 있다!
font-weight: ${(props) => (props.$isActive ? 'bold' : '')};
border: ${(props) => (props.$isActive ? '2px solid black' : '')};
3. 부족한점😫
- textContent와 같은 DOM API 개념 및 활용
- e.target, e.target.value, e.currentTarget의 역할과 차이점 구분
《 e.target, e.target.value, e.currentTarget의 역할과 차이점 》에 대해 정리 해놨다!
댓글남기기