๐ Infinite Scroll ?
infinite scroll์ ๋ธ๋ผ์ฐ์ ์ ํ๋จ์ผ๋ก ์คํฌ๋กค์ ํ๋ฉด
์ฌ์ฉ์๊ฐ ์ด๋ ํ ์ก์
(ํด๋ฆญ)์ ํ์ง ์์๋ ์์์ ์ด๋ฏธ์ง๊ฐ ๋์ค๋๋ก ์ฒ๋ฆฌํ๋ ๊ฒ ์
๋๋ค.
์ด๋ฌํ ๊ฒ์ด ๊ฐ๋ฅํ๊ฒ ํ๋ ค๋ฉด
- ์คํฌ๋กค์ด ํ๋จ๋ถ์ ์์์ ๊ฐ์งํ ์ ์์ด์ผํ๊ณ ,
- ๊ฐ์ง ํ์ ๋ ๋ค์ page์ ์ฌ์ง์ ๋ถ๋ฌ์ค๋ ์์ (pagination)์ ์ํํ๋ ํจ์๋ฅผ ์คํํด์ผ ํฉ๋๋ค.
ํฌ๊ฒ ๋๊ฐ์ง ๋ฐฉ์์ด ์์ต๋๋ค.
- (๋ ํจ์จ์ ์ธ) ๊ธฐ์กด ๋ฐฉ์
- ์น ๋ธ๋ผ์ฐ์ ์์ ์ ๊ณตํ๋ api์๋ ์คํฌ๋กค(event)์ ํ ๋ ์ด๋ค ํจ์๋ฅผ ์คํ๋๊ฒํ๋ addEventListener์ ์ด๋ ํ element์ content๊ฐ ์์ง์ผ๋ก ์ผ๋ง๋ ์คํฌ๋กค๋์๋์ง๋ฅผ ํฝ์ ๋จ์์ ์๋ก ๋ํ๋ด์ฃผ๋ Element.scrollTop ํ๋กํผํฐ๊ฐ ์์ต๋๋ค.
- ๊ทธ๋ ๋ค๋ฉด ์คํฌ๋กค์ด ์ผ์ด๋ ๋ ์คํ๋๋ ํจ์ ์์ ์กฐ๊ฑด๋ฌธ์ ๋ฃ์ด์ ์คํฌ๋กค์ ์์น๊ฐ ํน์ ํ ๊ณณ์ ์๋ค๋ฉด ๋ค์ page์ ์ฌ์ง์ ๋ถ๋ฌ์ค๋๋ก ํ๋ ๊ฒ์ด์ฃ .
- ์์๋ ์๋์ ๊ฐ์ต๋๋ค.
document.addEventListener("mousewheel", ()=> console.log(`scrollTop: ${document.children[0].scrollTop}`))โ // console.log()๋์ ์ ์คํฌ๋กค์ด ์ด๋๊น์ง ์๋์ง ํ์ธํ๋๋กํ๊ณ if๋ฌธ์ผ๋ก ์กฐ๊ฑด์ ์ง์ ํด ์ฃผ์ด์ ์คํฌ๋กค์ด ์ด๋ ์์น์ ๋๋ฌํ๋ฉด // ๋ค์ ํ์ด์ง์ ์ฌ์ง์ ๋ถ๋ฌ์ค๋๋ก ํ๋ http ์์ฒญ์ ๋ณด๋ด๋ฉด ๋๋ ๊ฒ์ด์ฃ .
- ๊ถ์ฅํ๋ ๋ฐฉ์
- ์คํฌ๋กค์ ๋์ ์๋ ํน์ element๋ฅผ ์ฃผ์ํ๊ณ ์๋ค๊ฐ ์ด element์ ์ง์ ๋ ๋งํผ์ด ํ๋ฉด์ ๋ํ๋๋ฉด(view port์ ๋ค์ด์์ ๋) ํน์ ํจ์๋ฅผ ์คํํ๊ฒ ํ๋ Intersection Observer API์ Thresholds๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ๋๋ค. ์๋ ์ฌ์ง์ ๋ณด๋ฉด ๋๋๊ณ ํด๋น api๊ฐ infinite scroll์ ํ์ํจ์ ์ ์ด๋์ ๊ฒ์ ์ ์๋ ์์ฃ . ์ ๋ ์ด๋ฒ ํฌ์คํ ์์ ์ด ๋ฐฉ์์ผ๋ก ๋ฆฌ์กํธ์์ infinite scroll์ ๊ตฌํํด ๋ณผ๊น ํฉ๋๋ค.
๐ ๋ฆฌ์กํธ์์ ์ด๋ป๊ฒ ๊ตฌํํ ๊น ?
๊ถ์ฅํ๋ ๋ฐฉ์์ ์ค๋ช
ํ๋ฉฐ ํน์ element๋ฅผ ์ฃผ์ํด์ผํ๋ค๊ณ ํ์ต๋๋ค.
์ฐ์ ๋ชจ๋ ํ๊ทธ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์์ ๊ฐ์ฅ ๋ง์ง๋ง์ ์๋ ํ๊ทธ๋ฅผ ๊ฐ์ํ๊ฒ ํ๋๋ก ํด๋ด
์๋ค.
๋
ธ๋ ๋ฐฐ๊ฒฝ์ ์๋ ๋๋ฌด ์์๋ฅผ ๊ฐ์ํ๊ฒ ๋ง๋ค๊ฑฐ๊ณ ,
์ด ๋๋ฌด์์๊ฐ ํ๋ฉด์ ๋ณด์ด๋ ์๊ฐ ๋ค์ ํ์ด์ง์ ์ฌ์ง์ ์์ฒญํ๋ http ์์ฒญ์ ๋ณด๋ผ ๊ฒ๋๋ค.
- ํด๋น element์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์์ผํ๋๋ฐ ์๋ฐ์คํฌ๋ฆฝํธ์์๋ getElementById๋ฅผ ํตํด์ ํน์ id๊ฐ ์๋ element๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ง๋ง ๋ฆฌ์กํธ์์๋ useRef๋ฅผ ์ด์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
์ ์ฝ๋๋ฅผ ๋ณด์๋ฉด ref={index + 1 === images.length ? elementRef : undefined}๋ผ๋ ์์ฑ ์ค์ ์ ํตํด ๋ง์ง๋ง ์ฌ์ง์ ๊ฐ์ธ๋ Link(a) ํ๊ทธ์๋ง elementRef๋ฅผ ์ง์ ํ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.import React, { useContexet ,useRef } from 'react'; import { ImageContext } from '../context/ImageContext'; function ImageList() { const elementRef = useRef(null); const {images} = useContext(ImageContext); const imgList = images.map((image, index) => ( <Link key={image.key} to={`/images/${image._id}`} ref={index + 1 === myImages.length ? elementRef : undefined} > <img src={`http://localhost:5000/uploads/${image.key}`} alt="" /> </Link> )) return( <div className="imageList">{imgList}</div> ) }โ
- ์ ๊ทธ๋ผ ์ด๋ป๊ฒ elementRef๋ฅผ ๊ฐ์ํ๋ฉด์ ํน์ ํจ์๊ฐ ์คํ๋๊ฒ ํ ์ ์์๊น์?
import React, { useContexet, useRef, useEffect } from 'react'; import { ImageContext } from '../context/ImageContext'; function ImageList() { const elementRef = useRef(null); const {images, loadMoreImages} = useContext(ImageContext); useEffect(() => { if (!elementRef.current) return; //์๋ฌ์ฒ๋ฆฌ const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) loadMoreImages(); }); observer.observe(elementRef.current); return () => observer.disconnect(); }, [loadMoreImages]); // loadMoreImages๋ ํ์ด์ง๋ค์ด์ ์ ํตํด ๋ค์ ํ์ด์ง์ ์ฌ์ง์ ์์ฒญํ๋ ํจ์ ์ ๋๋ค. const imgList = images.map((image, index) => ( <Link key={image.key} to={`/images/${image._id}`} ref={index + 1 === myImages.length ? elementRef : undefined} > <img src={`http://localhost:5000/uploads/${image.key}`} alt="" /> </Link> )) return( <div className="imageList">{imgList}</div> ) }โโ
-
useEffect๋ฅผ ํตํด loadMoreImages ํจ์๊ฐ ๋ฐ๋๋ ๋ค์ ํ์ด์ง์ ์ฌ์ง์ ์์ฒญํ๋ ํจ์์ธ loadMoreImages๊ฐ ํธ์ถ๋๊ณ , ํธ์ถํ ํ์ ๊ฐ์ํ๋ element๋ฅผ ์์ ๋๋ก ํ์์ต๋๋ค.
-
const observer = new IntersectionObserver(([entry]) => {
-
new IntersectionObserver๋ ์ฝ๋ฐฑ๊ณผ ์ต์ ์ ์ธ์๋ก ๋ฐ์ต๋๋ค.
- ์ฝ๋ฐฑ ์ ๋ฌ์ธ์์๋ ๊ตฌ์กฐ๋ถํด ํ ๋น์ผ๋ก entry๋ฅผ ๋ฝ์์ ๋ฃ์ด์ฃผ์๊ณ
- new IntersectionObserver์ ์ฝ๋ฐฑํจ์์ ์ ๋ฌ์ธ์๋ก ์ฌ๋ฌ๊ฐ์ entry๋ค์ ๋ฃ์ด์ฃผ๋ฉด ์ฌ๋ฌ element๋ฅผ ์ฃผ์ํ ์ ์์ง๋ง
์ ๋ ๋ง์ง๋ง ์ฌ์ง์ ๊ฐ์ธ๋ Link(a)ํ๊ทธ๋ฅผ ์ฃผ์ํ ๊ฒ์ด๊ธฐ์ ํ๋๋ง ๊ฐ์ ธ์์ต๋๋ค.
- (์ฐธ๊ณ ๋ก ์ต์ ์ ์ค์ ํ์ง ์์๊ธฐ์ thresholds๋ 0์ผ๋ก ๋์ด์์ต๋๋ค.)
-
- if (entry.isIntersecting) loadMoreImages();
- entry.isIntersecting์ ํด๋น ์์๊ฐ ํ๋ฉด์ ๋ณด์ด์๋ง์ true๊ฐ ๋๊ณ ๋ค์ ํ์ด์ง์ ์ฌ์ง์ ์์ฒญํ๋ loadMoreImages ํจ์๋ฅผ ์คํํด์ฃผ๋ ๊ฒ์ด์ฃ .
- ์ฆ ํด๋น ์์๊ฐ ํ๋ฉด์ ๋ณด์ด์๋ง์ new IntersectionObserver์ ์ฝ๋ฐฑํจ์๊ฐ ์คํ๋๋ ๊ฒ์ ๋๋ค.
- observer.observe(elementRef.current);
- ์ฃผ์ํ element๋ฅผ ๋ฃ์ด์ฃผ๋ ์ฝ๋์ ๋๋ค.
- return () => observer.disconnect();
- ๊ธฐ์กด์ ์๋ elementRef๋ฅผ ์์ ์ฃผ๋ ์ญํ ์ ๋๋ค.
-
์ด๋ ๊ฒ ํด์ ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ๋ ๋ฌดํ ์คํฌ๋กค์ ๋ํด์ ์์๋ณด์์ต๋๋ค.
์ฐธ๊ณ ๋ก ๋ง์ง๋ง ์ฌ์ง์ ๊ฐ๊ธฐ ์ ์กฐ๊ธ ๋ ์ผ์ฐ ์ฌ์ง์ ๋ถ๋ฌ์ค๊ณ ์ถ๋ค๋ฉด...
const imgList =
images.map((image, index) => (
<Link
key={image.key}
to={`/images/${image._id}`}
ref={index + 1 === myImages.length ? elementRef : undefined}
>
<img
src={`http://localhost:5000/uploads/${image.key}`}
alt=""
/>
</Link>
))
์ ์ฝ๋์์ index + 1์ index + 3, index + 5, index + 10๊ณผ ๊ฐ์ด ์ฌ๋ ค์ฃผ๋ฉด ์กฐ๊ธ๋ ๋น ๋ฅธ ๋ก๋ฉ์ ํตํด ์ข์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ค ์๋ ์๋ต๋๋ค!
'React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[๋ฒ์ญ] Why Not To Modify React State Directly ์ state๋ฅผ ์ง์ ์์ ํ์ง ์๋ ์ด์ (0) | 2021.09.19 |
---|---|
[React ๊ธฐ์ด ๋ฌธ๋ฒ] #4 event handling(์ด๋ฒคํธ ํธ๋ค๋ง) (0) | 2021.06.14 |
[React ๊ธฐ์ด ๋ฌธ๋ฒ] #3 state์ props (0) | 2021.06.13 |
[React ๊ธฐ์ด ๋ฌธ๋ฒ] #2 ํด๋์คํ ์ปดํฌ๋ํธ + props (0) | 2021.06.12 |
[React ๊ธฐ์ด ๋ฌธ๋ฒ] #1 intro + JSX ๋ช ๊ฐ์ง ๊ท์น (0) | 2021.06.03 |