탭 만들기를 강의 안 듣고 혼자 해봤는데 완전 잘못 생각했다...
탭 3개를 만들어서 각각 눌렀을 때 내용이 보이도록 하는 게 목표였는데,
난 그걸
탭이 보이는 상태와 탭이 안보이는 상태<로 나누어서 저장해야 된다고 생각해서
let [visible, setVisible] = useState(true);
// let [className, setClassName] = useState('tab-invisible');
let tabClassName = 'tab-invisible';
이렇게함
그리고 visible 일때 invisible 일때 CSS 를 이렇게 작성해서
CSS로 조작하려고 했다
.tab-visible{
height: 100px;
background-color: red;
border: 1px solid #000;
color:white;
}
.tab-invisible{
display: none;
}
탭은 컴포넌트로 뺐고...
function Tab(props){
return (
<Nav.Item>
<Nav.Link onClick={()=>{
return !props.tabVisible ? props.setTabVisible(true) : null
}} eventKey="link0">버튼{props.index}</Nav.Link>
<div>내용{props.index}</div>
</Nav.Item>
)
}
map으로 하고 조건문도 주고 그랬다 (;;)
<Nav variant="tabs" defaultActiveKey="link0">
{
tabs.map((a, i)=>{
return <Tab className={tabClassName} tabVisible={tabVisible} setTabVisible={setTabVisible} index={i} />
})
}
{
tabVisible ? tabClassName = 'tab-visible' : null
}
그랬더니,,, 이상함!!!
뭔가 잘못됨을 느낌...ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 겨우 탭인데 뭔가 너무 복잡해졌다.
+ 지금 생각해보니까 굳이 클래스를 써서 CSS로 하지 않으면 되는 문제였던것 같음... 왜이렇게 복잡하게 한거지
내가 바닐라 자바스크립트에 익숙해서 그런듯... 상태 변경에 좀더 익숙해져야해
아래는 간단한 구조로 문제를 해결한 코드입니다:
import { useState } from 'react';
import { Nav } from 'react-bootstrap';
function Tabs() {
const [activeTab, setActiveTab] = useState(0); // 현재 활성화된 탭의 인덱스를 저장
const tabs = ['탭 1 내용', '탭 2 내용', '탭 3 내용']; // 각 탭의 내용을 배열로 관리
return (
<div>
<Nav variant="tabs" defaultActiveKey="link0">
{tabs.map((_, index) => (
<Nav.Item key={index}>
<Nav.Link
eventKey={`link${index}`}
onClick={() => setActiveTab(index)} // 클릭 시 활성화된 탭의 인덱스 업데이트
className={activeTab === index ? 'tab-active' : 'tab-inactive'}
>
버튼 {index + 1}
</Nav.Link>
</Nav.Item>
))}
</Nav>
{/* 활성화된 탭의 내용만 표시 */}
<div style={{ marginTop: '20px' }}>{tabs[activeTab]}</div>
</div>
);
}
export default Tabs;
useState로 activeTab 상태 관리
useState(0)으로 현재 활성화된 탭의 인덱스를 관리합니다.activeTab이 0이고, "버튼 1"을 나타냅니다.setActiveTab이 호출되어 해당 버튼의 인덱스(index)를 activeTab으로 업데이트합니다.탭의 내용을 배열로 관리
tabs 배열에는 각 탭의 내용을 문자열로 저장합니다.activeTab 값에 따라 tabs[activeTab]으로 현재 활성화된 탭의 내용을 가져옵니다.클릭 이벤트로 상태 변경
onClick={() => setActiveTab(index)}는 버튼을 클릭할 때 해당 버튼의 인덱스를 activeTab으로 설정합니다.조건부 렌더링
activeTab)의 내용만 표시됩니다. 나머지 탭의 내용은 렌더링되지 않습니다.CSS 클래스 동적 적용
className={activeTab === index ? 'tab-active' : 'tab-inactive'}로 활성화된 탭과 비활성화된 탭에 다른 스타일을 적용합니다.
- 기존 방식으로 할려면...
tabVisible 하나로 모든 탭의 상태를 관리하려고 하니, 어떤 탭이 활성화되어야 할지 구분하기 어려워졌습니다.true면 모든 탭에 영향을 줄 수 있습니다.<div>내용{props.index}</div>로 렌더링되지만, 항상 보여지고 있습니다. 어떤 탭의 내용이 보이는지를 명확히 구분하기 어렵습니다.tabClassName 값을 직접 변경하면서 조건에 따라 클래스 이름을 바꾸려 했지만, React에서는 상태를 사용하는 것이 더 적합합니다.
React는 DOM 조작 대신 상태 관리로 UI를 업데이트하도록 설계되었습니다.
체크
.tab-visible과 .tab-invisible 클래스를 유지하면서, className을 동적으로 적용해 탭의 가시성을 관리합니다.import { useState } from 'react';
import { Nav } from 'react-bootstrap';
function Tabs() {
const [activeTab, setActiveTab] = useState(0); // 현재 활성화된 탭의 인덱스 관리
const tabs = ['탭 1 내용', '탭 2 내용', '탭 3 내용']; // 각 탭의 내용을 배열로 관리
return (
<div>
<Nav variant="tabs" defaultActiveKey="link0">
{tabs.map((_, index) => (
<Nav.Item key={index}>
<Nav.Link
eventKey={`link${index}`}
onClick={() => setActiveTab(index)} // 클릭 시 활성화된 탭 변경
>
버튼 {index + 1}
</Nav.Link>
</Nav.Item>
))}
</Nav>
{/* 탭 내용 표시 */}
<div>
{tabs.map((content, index) => (
<div
key={index}
className={activeTab === index ? 'tab-visible' : 'tab-invisible'}
>
{content}
</div>
))}
</div>
</div>
);
}
export default Tabs;
activeTab 상태
0은 첫 번째 탭, 1은 두 번째 탭을 의미합니다.map을 이용한 렌더링
tabs.map을 통해 각 탭의 내용과 버튼을 반복적으로 렌더링합니다..tab-visible 클래스를 사용하여 표시되고, 나머지는 .tab-invisible로 숨겨집니다.CSS 클래스 변경
className을 activeTab과 index 비교를 통해 동적으로 설정해 작성한 CSS를 그대로 활용할 수 있습니다.activeTab)와 연결하여 동적으로 적용하는 것이 효율적입니다.useState로 activeTab 관리
activeTab은 현재 활성화된 탭의 인덱스를 저장합니다.setActiveTab으로 해당 탭의 인덱스를 업데이트합니다.탭 내용을 배열로 관리
tabs)에 담아두고, activeTab을 사용해 현재 활성화된 탭의 내용을 표시합니다.className 동적 적용
activeTab과 index를 비교해 활성화된 탭에는 'tab-active', 나머지에는 'tab-inactive' 클래스를 적용합니다. 이를 통해 탭의 스타일을 변경할 수 있습니다.구조 단순화
당연한건데 생각을 못했음...
className={activeTab === index ? 'tab-visible' : 'tab-invisible'
<div>
{tabs.map((content, index) => (
<div
key={index}
className={activeTab === index ? 'tab-visible' : 'tab-invisible'}
>
{content}
</div>
왜 이생각을 못했지?
map에 content 들 돌때마다 자기자신을 출력하도록 하면 됨...
탭 내용을 보여주는 부분과 탭 버튼을 출력하는 부분을 동시에 우겨넣으려고 하니까 혼란이 온 거였다
두개를 나눠서 하면 됨
그리고 좀 차근차근해야 안헷갈린다...
참고해서 다시 만들어볼거임
있던거 다틀렸으니까 그냥 싹다지우고 다시시작함
<Nav variant="tabs" defaultActiveKey="link0">
<Nav.Item>
<Nav.Link eventKey="link0">버튼0</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="link1">버튼1</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="link2">버튼2</Nav.Link>
</Nav.Item>
</Nav>
<div>내용0</div>
<div>내용1</div>
<div>내용2</div>
여기부터 제대로 이해를 했어야했는데
<Nav variant="tabs" defaultActiveKey="link0">
{ 반복문으로 탭 버튼출력하는 곳 }
</Nav>
<div> {탭내용 중에서 몇번째 꺼만 보여주는 곳} </div>
인거임
컴포넌트부터 만들고 그 다음에 변수로 대체할 수 있는 부분들을 찾아봄
<Nav variant="tabs" defaultActiveKey="link0">
{
tabContent.map((_, i)=>{
return <Tab i={i} />
// 자꾸 빼먹는데 여기서 return 빼먹지마 ㅡㅡ
})
}
</Nav>
람다함수쓸때 자꾸 리턴을 빼먹는데
차라리 그럼 중괄호도 생략하던가 자꾸 ()=>{} 이렇게 쓰는게 습관이 됐음 ㅡㅡ
<div>{tabContent[activeTab]}</div>
여기 굳이 중괄호 열 필요 없이 그냥 내용을 꺼내다가 출력하니까 훨씬 간결하고 직관적인 듯.
function Detail(props) {
let [activeTab, setActiveTab] = useState(0);
let tabContent = ['탭1내용','탭2내용','탭3내용','탭4내용'];
// 중략
return( //중략
<Nav variant="tabs" defaultActiveKey="link0">
{
tabContent.map((_, i)=>{
return <Tab i={i} btnIdx={i+1} activeTab={activeTab} setActiveTab={setActiveTab} />
// 자꾸 빼먹는데 여기서 return 빼먹지마 ㅡㅡ
})
}
</Nav>
<div>{tabContent[activeTab]}</div>
)
}
function Tab(props){
return(
<Nav.Item>
<Nav.Link eventKey={`link${props.i}`}
onClick={()=>{
props.setActiveTab(props.i);
}}
>{'버튼'+ props.btnIdx}</Nav.Link>
</Nav.Item>
)
}
ㅜㅜ 드디어 성공함