스크롤을 내릴 때, 마치 엔딩 크레딧처럼 평면적으로 스크롤이 내려가는 것보다, 브라우저 영역에 들어가거나 나갈 때 transition이 일어나도록 수정하려 한다.
intersection-observer라는 브라우저 내장 기능을 통해 구현할 수 있다.
우선 테스트 뼈대를 잡았다.
참고로, 아래 Div와 Image 태그는 margin-top: 1000px; 값을 가져, 충분히 스크롤해야 나타나도록 설정해두었다.
import s from '../stores/styling'
import image from '../assets/images/artistregister-background.jpg'
export default function ScrollTest() {
return (
<>
<s.Div className='test'>테스트</s.Div>
<s.Image className='test' src={image} alt="photo"/>
</>
)
}
그리고 useRef를 사용해 DOM element에 직접 접근한다.
import { useRef } from 'react'
import s from '../stores/styling'
import image from '../assets/images/artistregister-background.jpg'
export default function ScrollTest() {
const myref = useRef<HTMLDivElement>(null);
return (
<>
<s.Div className='test' ref={myref}>테스트</s.Div>
<s.Image className='test' src={image} alt="photo"/>
</>
)
}
useEffect 훅으로 IntersectionObserver을 설정한다. 이를 통해 특정 요소가 뷰포트에 들어오거나 나가는 것을 감지한다.
import { useRef, useEffect } from 'react'
import s from '../stores/styling'
import image from '../assets/images/artistregister-background.jpg'
export default function ScrollTest() {
const myref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => { //entry: observed item의 리스트.
const entry = entries[0];
console.log('entry:', entry);
})
observer.observe(myref.current)
}, [])
return (
<>
<s.Div className='test' ref={myref}>테스트</s.Div>
<s.Image className='test' src={image} alt="photo"/>
</>
)
}
그러나 위처럼 작성하면 Argument of type 'HTMLDivElement | null' is not assignable to parameter of type 'Element'. 에러가 나타난다.
myref.current 이 null인지 확인하고, null이 아닐 때만 observe할 수 있게 처리한다.
import { useRef, useEffect } from 'react'
import s from '../stores/styling'
import image from '../assets/images/artistregister-background.jpg'
export default function ScrollTest() {
const myref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => { //entry: observed item의 리스트.
const entry = entries[0];
console.log('entry:', entry);
})
if (myref.current) {
observer.observe(myref.current)
}
return () => {
if (myref.current) {
observer.unobserve(myref.current)
}
}
}, [])
return (
<>
<s.Div className='test' ref={myref}>테스트</s.Div>
<s.Image className='test' src={image} alt="photo"/>
</>
)
}
사실 여기에서 조금 의문이 들었다.
if 문을 처리할 때, myref.current 가 null 인 경우를 else 처리하면 되지 않나?
결론적으로는 안된다.
if (myref.current) {
observer.observe(myref.current)
}
else {
oberserver.unobserve(myref.current)
}
if (myref.current === null) {
observer.unobserve(myref.current)
}
else {
observer.observe(myref.current)
}
위 두 코드는 myref.current 가 null 인지 확인하는 것처럼 보이지만,
첫번째 코드는 null일 때 else 문이 반드시 실행된다.
두번째 코드도 비슷하게 직접 null인지 확인하고 unobserve를 실행한다.
observer.unobserve는 관찰 중인 실제 DOM 요소가 있을 때만 호출되어야 하기 때문에, 위 두 코드는 잘못된 접근이다.
따라서
import { useRef, useEffect } from 'react'
import s from '../stores/styling'
import image from '../assets/images/artistregister-background.jpg'
export default function ScrollTest() {
const myref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => { //entry: observed item의 리스트.
const entry = entries[0];
console.log('entry:', entry);
})
if (myref.current) { // myref.current 가 true 일때만 실행 = null이 아닌 경우 실행
observer.observe(myref.current)
}
return () => { // cleanup 함수
if (myref.current) {
observer.unobserve(myref.current)
}
}
}, [])
return (
<>
<s.Div className='test' ref={myref}>테스트</s.Div>
<s.Image className='test' src={image} alt="photo"/>
</>
)
}
위처럼 작성하니, 콘솔에서도 뷰포트에 요소가 들어오면 true, 나가면 false를 반환하는 것을 확인할 수 있었다.
'React.ts' 카테고리의 다른 글
React TypeScript - OpenAI DALL.E API로 이미지 생성하기 (0) | 2024.07.17 |
---|---|
React TypeScript - Custom Hook으로 모달창 관리하기 (0) | 2024.07.07 |
React TypeScript - 카카오맵 API 호출 : TypeError: Cannot read properties of null (reading 'currentStyle') (0) | 2024.06.12 |
React TypeScript - 카카오맵 API 호출 : TypeError: window.kakao.maps.LatLng is not a constructor 해결하기 (0) | 2024.06.11 |
React 캐러셀 구현 (1) | 2024.06.04 |