반응형
이 녀석은 반응형 탭에 대한 고민의 흔적이 담긴 꽤 오래 묵혀놓은 포스팅이다.
재작년에 짰던 코드라 부족할 수 있지만 최대한 고쳐 올려보겠다.
상품 판매 페이지의 중간에 메뉴 탭이 붙게 되는데,
이 메뉴를 클릭시 해당 내용이 있는 곳으로 스크롤이 이동한다.
또한 그냥 스크롤 다운 하다가 해당 영역이 있는 지점에 들어서면 이번엔 반대로 탭이 해당 메뉴로 이동한다.
그럼 구현하러 가보자 !
React(91) 탭 클릭시 지정영역으로 스크롤 이동 / 스크롤시 해당 탭 자동선택
아래 상품 상세 페이지에는 포트폴리오라는 탭이 보인다. 이 탭을 클릭해보자
요렇게 스크롤이 딱 포트폴리오를 설명하는 부분으로 이동한다.
반대의 경우도 마찬가지이다.
포트폴리오를 설명하는 부분으로 스크롤을 내리다보면 해당 영역으로 진입하면 탭이 눌리게 된다.
import { ReactElement, useEffect, useState, useRef } from 'react';
import Tabs from 'components/Tabs';
import Portfolio from 'components/Portfolio';
import DetailInfo from 'components/DetailInfo';
const menu = [
{ id: 0, name: '포트폴리오' },
{ id: 1, name: '상세정보' }
];
const ProductDetail = (): ReactElement => {
const [menuID, setMenuID] = useState(0);
const portfolioRef = useRef<HTMLDivElement>(null);
const detailInfoRef = useRef<HTMLDivElement>(null);
const scrollTarget = useRef<number | null>(null);
const scrolling = useRef(false);
const offset = 72; // 탭의 높이
// 스크롤 이벤트 감지
useEffect(() => {
window.addEventListener('scroll', handleScroll, { capture: true });
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
// 스크롤하면 탭 자동선택 함수
function handleScroll (){
if (!detailInfoRef.current || !document.getElementById('app')) {
return;
}
// 현재 스크롤 위치를 담은 변수
const scrollTop: number | undefined = document.getElementById('app')?.scrollTop;
if (scrollTop) {
// 스크롤 하는 중일 경우 → 초기화 해버리자
if (scrolling.current) {
if (!scrollTarget.current || scrollTop === scrollTarget.current) {
scrollTarget.current = null;
scrolling.current = false;
}
return;
}
// 상세정보 영역인 경우 → 메뉴ID를 '상세정보'로 변경
if (scrollTop >= detailInfoRef.current.offsetTop - offset) {
setMenuID(1);
// 포트폴리오 영역인 경우 → 메뉴ID를 '포트폴리오'로 변경
} else if (scrollTop < detailInfoRef.current.offsetTop - offset) {
setMenuID(0);
}
}
}
// 클릭시 이동
function setMenu (index) {
if (menu.length <= index) return; // 예외처리
let top = 0;
setMenutID(index); // (현재클릭된) 메뉴ID를 변경
// '포트폴리오' 영역을 클릭했을 때 (스크롤 위치 설정)
if (index === 0) {
if (!portfolioRef.current) return;
top = portfolioRef.current.offsetTop - offset;
}
// '상세정보' 영역을 클릭했을 때 (스크롤 위치 설정)
if (index === 1) {
if (!detailInfoRef.current) return;
top = detailInfoRef.current.offsetTop - offset;
}
scrollTarget.current = top;
scrolling.current = true;
document.getElementById('app')?.scrollTo({ top: top, behavior: 'smooth' });
}
return (
<div className="product-detail">
<Tabs // 메뉴탭 컴포넌트이다
items={menu} // 메뉴 배열을 받아 보여주고
activeID={menuID} // 클릭된 탭의 메뉴ID 를 내려보내주기도 하고
setActiveID={setMenuID} // 클릭할 탭의 메뉴ID 를 여기서 조작하게 해주기도 한다.
/>
<div className="product-content">
<MarketPortfolio ref={portfolioRef} /> // 각각의 컴포넌트에 useRef 로 DOM 에 접근하자
<MarketDetailInfo ref={detailInfoRef} />
</div>
</div>
);
};
export default ProductDetail;
반응형
'React' 카테고리의 다른 글
React(93) TypeScript - 유사객체배열에서 원하는 값 사용하기 (0) | 2023.02.16 |
---|---|
React(92) useRef 알아보기 (공식문서 정리) (0) | 2023.02.01 |
React(90) useEffect 알아보기 (공식문서 정리) (0) | 2023.01.19 |
React(89) 글 작성시간 계산함수 만들기 (몇분전/몇시간전/몇일전) (0) | 2022.12.28 |
React 실행오류 - rendered fewer hooks than expected (feat. Hook의 규칙) (0) | 2022.11.30 |
댓글