๋ชจ๋ฅด๋๊ฑด ๋ฐฐ์ฐ๊ณ ๋ฐฐ์ ์ผ๋ฉด ์จ๋ด์ผ ๋ญ๊ฐ ๋ญ์ง ์..
๊ทธ๋์ ์ด์ ํฌ์คํ
ํ Intersection Observer
๋ฅผ ์ฌ์ฉํด ๋ฌดํ ์คํฌ๋กค์ ๊ตฌํํด ๋ณด๊ธฐ๋ก ํจ.
์ด๋ถ๊ฐ์๋ฅผ ๋ฃ๊ณ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ํฌ์คํ ํ์. (์ด๋ฏธ์ง ํด๋ฆญ ์ ์ ํ๋ธ ์ด๋ํจ.)
unsplash์์ ์ ๊ณตํ๋ ๋ฌด๋ฃ API์ฌ์ฉํจ. ํ์๊ฐ์ ํ๋ฉด ๋ฌด๋ฃ๋ก API ๋ฐ์ ์ ์์!
https://unsplash.com/developers
ํ์๊ฐ์
ํ๊ณ ๋ก๊ทธ์ธํด์ ๋
ธ๋๋ถ๋ถ์ ํด๋ฆญํ๋ฉด
์๋ ๊ฒ API ํค๋ฅผ ๋ฐ์ ์ ์์
Documentation์ ๋ ธ๋๋ถ๋ถ์ ํด๋ฆญํ๋ฉด API์ฌ์ฉ ๋ฐฉ๋ฒ์ ์ ์ ์์. ๊ทธ์ธ API ๋ฉ์๋ ๋ค๋ ์ฌ์ฉ ํ ์ ์๋๋ฐ
ํ์ด์ง์ ๋ช๊ฐ์ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ๋์ง ์ปจํธ๋กค ํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด 10๊ฐ๋ง ๋ณด์ฌ์ค ๊บผ์.
- public
- src
- index.js
- styles.css
- App.js
- PhotoItem.js
CSS ์นดํผํจ. ์ด ๋ถ github์ ์ ํ๋ธ ์ฌ์ดํธ์ ์์.
import axios from "axios"; import { useEffect, useState } from "react"; import "./styles.css"; export default function App() { const [photos, setPhotos] = useState([]); //๋ฐ์ดํฐ ๊ณ์ํด์ ๋ด์ state const [pageNumber, setPageNumber] = useState(1); //์คํฌ๋กค์ด ๋ฟ์์ ๋ 2=>3=>4 ์๋กญ๊ฒ ๋ฐ์ดํฐํ์ด์ง๋ฅผ ๋ฐ๊ฟ state const [loading, setLoading] = useState(false); //๋ก๋ฉ ์ฑ๊ณต, ์คํจ ๋ด์ state const fetchPhotos = async () => { const API_KEY = "mEbcH0pSm70nidrKQS43hkgPtYQeFj1GI1txLml1tmk"; await axios .get( `https://api.unsplash.com/photos/?client_id=${API_KEY}&page=${pageNumber}&per_page=10` ) .then((res) => setPhotos([...photos, ...res.data])) //๋ฐ์ดํฐ๋ฅผ ์ฒ์๋ถํฐ ๋ณด์ฌ์ค์ผ ํ๊ธฐ ๋๋ฌธ์, operator ์ฐ์ฐ์๋ก ๋ณต์ ํด์ ์ถ์ ์ํด. .then(() => setLoading(true)) }; console.log(photos); useEffect(() => fetchPhotos(), [pageNumber]); //ํ์ด์ง ๋๋ฒ๊ฐ ๋ฐ๋๋๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์์ผ ํ๋๊น ๋ฐฐ์ด ๊ฐ์ผ๋ก pageNumber๋ฅผ ๋ฃ์. return ( <div className="App"> <h1>Infinite Scrolling</h1> <button>Load More</button> </div> ); }
10๊ฐ์ ๋ฐ์ดํฐ๊ฐ ์์๊ฒ ์ ๋ด๊ฒจ์์.
//App.js {...์๋ต} <div className="App"> <h1>Infinite Scrolling</h1> { photos.map((photo,index) => <PhotoItem key={index} photo={photo} />) } <button>Load More</button> </div>
//PhotoItem.js export default function PhotoItem({ photo }) { return ( <div className="photos"> <img src={photo.urls.small} /> <p>Photo By : {photo.user.username}</p> </div> ); }
//App.js const loadMore= () => setPageNumber(prev => prev+1); // setState๋ parameter๋ก state๋ก ๋ฐ์.
## 6. Target ์ค์
๋ฌดํ ์คํฌ๋กค์ ์ด๋ก ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ง์ง๋ง ์๋ฆฌ๋จผํธ์ ๋ฟ์์ ๋, ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๋ถ๋ฌ์ค๋ ๊ฒ์ด๋ค. ๋ด๊ฐ ๋ง๋ ํ์ด์ง์์ ๋ง์ง๋ง ์๋ฆฌ๋จผํธ๋ `LoadMore`๋ฒํผ์ด๋ฏ๋ก, ์ด ๋ฒํผ์ ref ๋ฅผ ์ง์ ํด๋๊ณ ํ์ ํ๊ฒ์ผ๋ก ์ ํ๋ฉด,
**์ด ์๋ฆฌ๋จผํธ์ ๋๋ฌํ์ ๋ ๋ญ๊ฐ๋ฅผ ํ ์ ์๋ค. (๋ฐ์ดํฐ ํจ์นญ)**
### โช ref ์ง์
> ```javascript
import {useRef } from "react";
const target= useRef();
{...์๋ต}
<button ref={target}>Load More</button>
//App.js {...์๋ต} useEffect(() => { if(loading){ //๋ก๋ฉ๋์์ ๋๋ง ์คํ const observer= new IntersectionObserver((entries) => { console.log(entries[0]) }) //์ต์ ธ๋ฒ ํ์ ์์ observer.observe(target.current); } },[]) {...์๋ต}
viewport
ํด๋น ๋ด์์ ์คํฌ๋กค์ ํ ๋ ์ต์ ธ๋ฒ๊ฐ ์คํ๋๋ฉด์ ์ฝ์์ด ์ฐํ.
target
์ ๋๋ฌํ๊ธฐ ์ : isIntersection:false
target
์ ๋๋ฌํ ํ : isIntersection:true
//App.js useEffect(() => { if (loading) { const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { loadMore(); //๋ฒํผ์ ๋๋ฌํ์ ๋ pageNumber๋ฅผ 1์ฉ ์ฆ๊ฐ์์ผ ๋ฐ์ดํฐ๋ฅผ 10๊ฐ์ฉ ๋ ๋ณด์ฌ์ค. } }); observer.observe(target.current); } }, []);
60 ๊ฐ์ด์์ ์ฝํ ์ธ ๋ฅผ ๋ณด์ด์ง ์๊ฒ ํ๊ณ ์ถ์. ๊ทธ๋ฌ๋ฉด 6๋ฒ ์ด์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ฉด ์๋จ.
//App.js {...์๋ต} let num = 1; //๋ณ์ ์ง์ useEffect(() => { if (loading) { const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { num++; //๋ณ์ ์ฆ๊ฐ์ํด loadMore(); if (num >= 6) { //6์ด์์ด๊ฑฐ๋ ๊ฐ์ผ๋ฉด ํ์์ค์ง observer.unobserve(target.current); } } }); observer.observe(target.current); } }, []); {...์๋ต}