infinite scroll을 예전에 구현했을 땐 직접 구현했었는데 회사에서 라이브러리를 사용했기 때문에 이것에 대한 사용방법을 익히고 다시한번 예전에 구현했던 코드를 상기시키는 시간을 가져보려 한다
// 공식 문서 예시 1
import React from "react";
import { render } from "react-dom";
import InfiniteScroll from "react-infinite-scroll-component";
const style = {
height: 30,
border: "1px solid green",
margin: 6,
padding: 8
};
class App extends React.Component {
state = {
items: Array.from({ length: 20 })
};
fetchMoreData = () => {
// a fake async api call like which sends
// 20 more records in 1.5 secs
setTimeout(() => {
this.setState({
items: this.state.items.concat(Array.from({ length: 20 }))
});
}, 1500);
};
render() {
return (
<div>
<h1>demo: react-infinite-scroll-component</h1>
<hr />
<InfiniteScroll
dataLength={this.state.items.length} // 반복되는 컴포넌트의 개수
next={this.fetchMoreData} // 스크롤이 바닥에 닿으면 데이터를 더 불러오는 함수
hasMore={true} // 추가 데이터 유무
loader={<h4>Loading...</h4>} // 로딩스피너
>
{this.state.items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
);
}
}
render(<App />, document.getElementById("root"));
// 공식 문서 예시 2
<div
id="scrollableDiv"
style={{
height: 300,
overflow: 'auto',
display: 'flex',
flexDirection: 'column-reverse',
}}
>
{/*Put the scroll bar always on the bottom*/}
<InfiniteScroll
dataLength={this.state.items.length}
next={this.fetchMoreData}
style={{ display: 'flex', flexDirection: 'column-reverse' }} //To put endMessage and loader to the top.
inverse={true} //
hasMore={true}
loader={<h4>Loading...</h4>}
scrollableTarget="scrollableDiv"
>
{this.state.items.map((_, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
scrollableTarget : 여러 컨텐츠 리스트에 의해 스크롤이 생기는 엘리먼츠
동작 원리 : height prop을 제공하지 않으면 기본적으로 scrollableTarget(document.body)의 맨 끝에 닿으면 next함수가 실행되면서 추가 데이터를 요청하는 구조
// useInfiniteScroll.ts
export function useInfiniteScroll() {
const [page, setPage] = useState(1);
function handleScroll() {
if (
document.documentElement.scrollTop + window.innerHeight + 300 >=
document.documentElement.scrollHeight
) {
setPage((p) => p + 1);
}
}
useEffect(() => {
window.addEventListener("scroll", handleScroll);
}, []);
return page;
}
// Movie.tsx
import { movieApi } from "../api/axiosApi";
const Movie = () => {
const [movies, setMovies] = useState<Movie[]>([]);
const page = useInfiniteScroll();
useEffect(() => {
movieApi
.nowPlaying(page)
.then((res) => {
const {
data: { results },
} = res;
setMovies((prev) => [...prev, ...results]);
})
.catch((err) => console.error(err));
}, [page]);
return (
<main>
{movies.map((movie) => {
return (
<div key={movie.id}>
<h3>{movie.title}</h3>
<p>{movie.overview}</p>
</div>
);
})}
</main>
);
};
export default Movie;