Skip to content

Commit 54949f2

Browse files
committed
feat: 반복요소 컴포넌트화
1 parent 95c3cf9 commit 54949f2

File tree

15 files changed

+179
-83
lines changed

15 files changed

+179
-83
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@
55
화면에 실제로 보여지는 부분만 DOM으로 렌더링하도록 연산하여 시스템 부하와 과도하게 낭비되는 리소스를 방지 하여 더 나은 서비스를 제공할 수 있습니다.
66

77
## 일반적인 목록 vs 가상화된 목록 비교
8+
89
일반적으로 목록을 많이 보여주는 화면과 react-virtualized를 사용하여 최적화 한 차이를 비교 제공합니다.
10+
11+
## 공식문서
12+
13+
- https://github.com/bvaughn/react-virtualized

src/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
2-
import { ChakraProvider, Flex, Container } from '@chakra-ui/react';
2+
import { ChakraProvider, Flex, Container, Heading } from '@chakra-ui/react';
33

44
import Navigation from './components/Navigation';
55
import TextList from './pages/TextList';
@@ -13,7 +13,12 @@ const App = () => {
1313
<Flex>
1414
<Container width="700px" padding={`20px 15px`}>
1515
<Router>
16-
<Navigation />
16+
<Container padding={`0 0 20px 0`} marginBottom={5} borderBottom={'solid 1px #bbb'}>
17+
<Heading mb={5} textAlign="center">
18+
React virtualized examples
19+
</Heading>
20+
<Navigation />
21+
</Container>
1722
<Switch>
1823
<Route exact path="/" component={TextList} />
1924
<Route path="/text-list" component={TextList} />
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import './index.scss';
2+
3+
interface Props {
4+
index: number;
5+
imageUrl: string;
6+
title: string;
7+
onLoad?: () => void;
8+
}
9+
10+
const ImageListItem = ({ index, imageUrl, title, onLoad }: Props) => {
11+
return (
12+
<div className="image-list-item">
13+
<section className="thumb-wrap">
14+
<img src={imageUrl} alt="" onLoad={() => onLoad?.()} />
15+
</section>
16+
<section>
17+
<p>index: {index}</p>
18+
<p>
19+
{title} {title} {title} {title} {title} {title}
20+
</p>
21+
</section>
22+
</div>
23+
);
24+
};
25+
26+
export default ImageListItem;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.navigation {
2+
a {
3+
padding: 10px;
4+
background-color: #bbb;
5+
text-align: center;
6+
7+
&.selected {
8+
background-color: #61dafb;
9+
font-weight: bold;
10+
}
11+
}
12+
}

src/components/Navigation/index.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
11
import { NavLink } from 'react-router-dom';
2-
import { Container, Heading, SimpleGrid } from '@chakra-ui/react';
2+
import { SimpleGrid } from '@chakra-ui/react';
3+
import './index.scss';
34

45
const Naviagation = () => {
56
return (
6-
<Container padding={`0 0 20px 0`} marginBottom={5} borderBottom={'solid 1px #bbb'}>
7-
<Heading mb={5} textAlign="center">
8-
React virtualized examples
9-
</Heading>
10-
<SimpleGrid className="navigation" columns={2} spacingX="5px" spacingY="20px">
11-
<NavLink to="/text-list" activeClassName="selected">
12-
텍스트 목록
13-
</NavLink>
14-
<NavLink to="/text-list-virtualized" activeClassName="selected">
15-
텍스트 목록 (with virtualized)
16-
</NavLink>
17-
<NavLink to="/image-list" activeClassName="selected">
18-
이미지 목록
19-
</NavLink>
20-
<NavLink to="/image-list-virtualized" activeClassName="selected">
21-
이미지 목록 (with virtualized)
22-
</NavLink>
23-
</SimpleGrid>
24-
</Container>
7+
<SimpleGrid className="navigation" columns={2} spacingX="5px" spacingY="20px">
8+
<NavLink to="/text-list" activeClassName="selected">
9+
텍스트 목록
10+
</NavLink>
11+
<NavLink to="/text-list-virtualized" activeClassName="selected">
12+
텍스트 목록 (with virtualized)
13+
</NavLink>
14+
<NavLink to="/image-list" activeClassName="selected">
15+
이미지 목록
16+
</NavLink>
17+
<NavLink to="/image-list-virtualized" activeClassName="selected">
18+
이미지 목록 (with virtualized)
19+
</NavLink>
20+
</SimpleGrid>
2521
);
2622
};
2723

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import './index.scss';
2+
3+
interface Props {
4+
index: number;
5+
email: string;
6+
name: string;
7+
body: string;
8+
}
9+
10+
const TextListItem = ({ index, email, name, body }: Props) => {
11+
return (
12+
<div className="text-list-item">
13+
<p>index: {index}</p>
14+
<p>email: {email}</p>
15+
<p>name: {name}</p>
16+
<p>
17+
body: {body} {body}
18+
</p>
19+
</div>
20+
);
21+
};
22+
23+
export default TextListItem;

src/pages/ImageList/index.scss

Whitespace-only changes.

src/pages/ImageList/index.tsx

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,63 @@
11
import { useEffect, useState, useCallback } from 'react';
2+
import { checkInfiniteScrollPosition } from '../../helpers/scroll';
3+
import { throttle } from 'lodash-es';
4+
25
import { Container, Heading, Button, Text } from '@chakra-ui/react';
36
import StackSkleton from '../../components/StackSkeleton';
4-
import './index.scss';
7+
import ImageListItem from '../../components/ImageListItem';
58

6-
export interface ImageListItem {
9+
export interface ImageListItemState {
710
id: number;
811
title: string;
12+
url: string;
913
thumbnailUrl: string;
1014
}
1115

16+
export const SPLICE_SIZE = 500;
17+
18+
let totalList: ImageListItemState[] = [];
19+
1220
const ImageList = () => {
13-
const [list, setList] = useState<ImageListItem[]>([]);
21+
const [list, setList] = useState<ImageListItemState[]>([]);
1422

15-
const addList = useCallback(() => {
23+
const fetchData = useCallback(async () => {
24+
// const res = await fetch('https://jsonplaceholder.typicode.com/photos');
25+
// console.log('res :>> ', res);
1626
fetch('https://jsonplaceholder.typicode.com/photos').then((res) => {
1727
const data = res.json();
1828

1929
data.then((newList) => {
20-
setList([...list, ...newList]);
30+
totalList = newList;
31+
addList();
2132
});
2233
});
23-
}, [list, setList]);
34+
}, []);
35+
36+
const addList = useCallback(() => {
37+
if (!totalList.length) {
38+
return;
39+
}
40+
41+
const data = totalList.splice(0, SPLICE_SIZE);
42+
setList([...list, ...data]);
43+
}, [list]);
44+
45+
const onScroll = useCallback(() => {
46+
const isNeedFetching = checkInfiniteScrollPosition({ bottom: 600 });
47+
if (isNeedFetching) {
48+
addList();
49+
}
50+
}, [addList]);
2451

2552
useEffect(() => {
26-
addList();
27-
}, []);
53+
fetchData();
54+
}, [fetchData]);
55+
56+
useEffect(() => {
57+
const onScrollTrottle = throttle(onScroll, 100);
58+
window.addEventListener('scroll', onScrollTrottle);
59+
return () => window.removeEventListener('scroll', onScrollTrottle);
60+
}, [onScroll]);
2861

2962
return (
3063
<>
@@ -43,11 +76,7 @@ const ImageList = () => {
4376
<section>
4477
{list.length ? (
4578
list.map(({ title, thumbnailUrl }, index) => (
46-
<div className="text-list-item">
47-
<div>index: {index}</div>
48-
<div>title: {title}</div>
49-
<div>body: {thumbnailUrl}</div>
50-
</div>
79+
<ImageListItem index={index} imageUrl={thumbnailUrl} title={title} />
5180
))
5281
) : (
5382
<StackSkleton count={5} />

0 commit comments

Comments
 (0)