진행하고 있는 빵덕 프로젝트의 테마상세 페이지에서 스크롤시 네비게이션바의 색이 변환하는 디자인이 나왔다. 스크롤 위치를 어떻게확인할까 고민을 하다가 IntersectionObserver를 사용하기로 하였다.
const option = {};
const isRef = useRef<HTMLElement | null>(null);
useEffect(() => {
const observer = new IntersectionObserver((entry) => {
if (entry[0].isIntersecting) {
console.log("여깁니다")
}
}, option);
if (isRef.current) {
observer.observe(isRef.current);
}
return () => observer.disconnect();
}, []);
return (
<section>
<ThemeHeader />
<ThemeNav />
<ThemeExplain ref={isRef}/>
<ThemeAnalysis />
<ThemeEvent />
<ThemeRevie />
<ReviewBottom />
</section>
);
1.eEffect안에 IntersectionObserver로 ref를 감지하도록 하고 태그가 화면에 나옴과 동시에 isIntersecting의 true라면 console을 찍을수 있게하여 확인하였다.
2.섹션별로 IntersectionObserver가 필요해보여서 여러개를 만드려니 코드가 지저분해질것같아 customHook으로 만들어야곘다는 생각을 하였다.
// useThemeObserver.ts
import { useEffect, useRef } from 'react';
export const useThemeObserver = (
setState: React.Dispatch<React.SetStateAction<number>>,
stateNumber: number,
): React.MutableRefObject<HTMLElement | null>[] => {
const isRef = useRef<HTMLElement | null>(null);
const option = {};
useEffect(() => {
const observer = new IntersectionObserver((entry) => {
if (entry[0].isIntersecting) {
setState(stateNumber);
}
}, option);
if (isRef.current) {
observer.observe(isRef.current);
}
return () => observer.disconnect();
}, []);
return [isRef];
};
커스텀훅안에 state번호를 지정하여 번호의 값으로 네비게이션바를 구분하기로 하였다.
const ThemeDetailTemplate = (): ReactElement => {
const [navNumber, setNavNumber] = useState(1);
const [explainRef] = useThemeObserver(setNavNumber, 1);
const [analysisRef] = useThemeObserver(setNavNumber, 2);
const [eventRef] = useThemeObserver(setNavNumber, 3);
const [reviewRef] = useThemeObserver(setNavNumber, 4);
return (
<section>
<ThemeHeader />
<ThemeNav position={navNumber} />
<ThemeExplain isRef={explainRef} />
<ThemeAnalysis isRef={analysisRef} />
<ThemeEventisRef={eventRef}/>
<ThemeReview isRef={reviewRef} />
<ReviewBtn/>
</section>
);
};
export default ThemeDetailTemplate;
//usethemeObserver.ts
const option = { threshold: 0, rootMargin: `-${document.body.scrollHeight / 2 - 1}px 0px` };
IntersectionObserver의 option에서 rootMargin값으로 화면높이의 반보다 약간작게하여 중앙을 지나갈때 state번호가 바뀔수 있게하여 1에서 3으로 넘어가는 불상사를 막을수있게 하였다.
//ThemeNav.tsx
interface Props {
position: number;
}
const ThemeNav = ({ position }: Props): ReactElement => {
const arr = [
{ id: 1, content: '테마' },
{ id: 2, content: '분석' },
{ id: 3, content: '이벤트' },
{ id: 4, content: '리뷰' },
];
return (
<S.Nav>
{arr.map((ar) => (
<S.Box key={ar.id} border={position === ar.id}>
<S.PTag border={position === ar.id}>{ar.content}</S.PTag>
</S.Box>
))}
</S.Nav>
);
};
네비게이션바와 props로 받아온 state값이 같을때의 boolean값을 styled-component로 넘겨 state값이 변경될때마다 css가 변환되게 구현해보았다.
훅이 정말 깔끔하네요!
덕분에 많은 도움 됐습니다 :)