import {useEffect, useRef, useState } from 'react';
import Content from './Content';
import Navigate from './Navigate';
const pages = Array.from({length:8}).map((_,i) => i + 1);
const App = () => {
// 현재 보고 있는 page를 알 수 있게 해주는 viewIndex
const [viewIndex,setViewIndex] = useState(0)
// 관찰 대상은 Div elements이기에 배열로 타입 지정
// ref를 사용한 이유는 observing에 사용하기 위함이다.
const pageRef = useRef<HTMLDivElement[] | null>([])
//scrollIntoView는 js에 내장된 element 인터페이스의 method다.
// 해당 element로 이동하게 만들어준다.
// navigation에서 사용할 함수로,
// index를 받아 원하는 페이지로 이동하게 한다.
// 인자를 받기 위해 고차함수로 적용.
const moveToTargetPage = (index:number) => () => {
if(pageRef.current){
pageRef.current[index].scrollIntoView({
block:'start',
behavior:"smooth",
inline:'center'
})
}
}
const options:IntersectionObserverInit = {
root: null,
threshold:0.5
}
const callback:IntersectionObserverCallback = (entries:IntersectionObserverEntry[],observer) => {
entries.forEach((entry) => {
const {isIntersecting,boundingClientRect} = entry;
// pageYOffset 문서가 수직으로 얼마나 스크롤 됐는지 픽셀 단위로 반환한다.
const scrollTop = window.pageYOffset;
// 이벤트가 발생된 target의 height
const {height} = boundingClientRect;
// 관찰 요소와 교차될때마다 index를 계산하여 viewIndex의 값을 바꿔준다.
if(isIntersecting) {
const index = Math.round(scrollTop / height);
setViewIndex(index);
}
})
}
const io = new IntersectionObserver(callback,options);
useEffect(() => {
// 첫 랜더링때에만 io를 부착한다
pageRef.current!.forEach((item) => io.observe(item))
// clean-up function. componentDidMount 이후 실행된다.
return () => {
pageRef.current!.forEach((item) => io.unobserve(item))
}
},[])
return (
<div>
<Navigate
pages={pages}
viewIndex={viewIndex}
moveToTargetPage={moveToTargetPage} />
{pages.map((page,index) =>
<Content
key={index}
page={page}
// type narrowing
ref={(r) =>{ if(r && pageRef.current)
return pageRef.current[index] = r}} />)}
</div>
)};
export default App;
navigation 내부의 버튼을 클릭할 때마다moveToTargetPage
함수가 실행되어 해당 element로 이동하게 만들어 준다
import React from 'react';
interface NavigateProps {
moveToTargetPage: (index: number) => () => void
viewIndex: number;
pages: number[]
}
const Navigate = ({moveToTargetPage,viewIndex,pages}:NavigateProps) => {
return (
<nav className='fixed left-0 right-0 top-0 p-0 m-0 bg-blue-300 overflow-hidden'>
<ul className='h-16 w-full flex flex-row justify-center items-center p-1'>
{pages.map((page,index) => <li className={`relative flex-1 text-center py-2 rounded-lg
${viewIndex === index ? "bg-white text-rose-500 font-bold text-xl" : ""}
`}><button className='w-full h-full' onClick={moveToTargetPage(index)}>{page}</button></li> )}
</ul>
</nav>
);
};
export default Navigate;
import React,{ComponentPropsWithRef, forwardRef} from 'react';
interface ContentProps extends ComponentPropsWithRef<'div'> {
page:number
}
const Content = forwardRef<HTMLDivElement,ContentProps>(({page},ref) => {
return (
<div className='h-[100vh] leading-[100vh] text-center text-[15rem]' ref={ref}>
{page}
</div>
);
});
export default Content;
여러개의 ref를 사용할 때 type narrowing까지 생각하는게 어려웠다.
Nowadays, infidelity has caused many relationships to end. I was able to get information on how to determine whether or not someone is cheating on you. Everything is on this website signs someone is hiding something on their phone . For instance, this website helped me learn a lot about treason. The website is highly useful, as are the spy software applications. I suggest you test this material out because it will be useful to you in the future.