Skip to content

Commit b20aba9

Browse files
committed
feat: implemented react intersection observer
1 parent 08b05ef commit b20aba9

File tree

6 files changed

+97
-18
lines changed

6 files changed

+97
-18
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@
3737
"react-ga": "^3.1.2",
3838
"react-ga4": "^2.1.0",
3939
"react-i18next": "^11.7.2",
40+
"react-intersection-observer": "^10.0.0",
4041
"react-markdown": "^7.1.0",
42+
"react-notion": "^0.10.0",
4143
"react-redux": "^7.2.1",
4244
"react-router": "^6.15.0",
4345
"react-router-dom": "^6.15.0",
4446
"react-scrollbars-custom": "^4.0.21",
4547
"react-slick": "^0.29.0",
4648
"redux": "^4.0.0",
4749
"slick-carousel": "^1.8.1",
48-
"zabo-embed": "^0.0.7",
49-
"react-notion": "^0.10.0"
50+
"zabo-embed": "^0.0.7"
5051
},
5152
"devDependencies": {
5253
"@babel/plugin-proposal-decorators": "^7.24.1",

src/components/sections/dictionary/courselist/CourseListSection.jsx

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import courseListsShape from '../../../../shapes/state/dictionary/CourseListsSha
2424
import userShape from '../../../../shapes/model/session/UserShape';
2525
import courseLastSearchOptionShape from '../../../../shapes/state/dictionary/CourseLastSearchOptionShape';
2626

27+
import { InView } from 'react-intersection-observer';
28+
2729
import {
2830
getLabelOfValue,
2931
getDepartmentOptions,
@@ -32,19 +34,30 @@ import {
3234
getTermOptions,
3335
} from '../../../../common/searchOptions';
3436

35-
const REFRESH_LIMIT = 20;
37+
const REFRESH_LIMIT = 10;
3638

3739
class CourseListSection extends Component {
3840
constructor(props) {
3941
super(props);
42+
this.inViewRef = React.createRef();
4043
}
4144

42-
onScrollChange = (e) => {
43-
const { scrollTop, scrollHeight, clientHeight } = e;
44-
45-
if (scrollTop + clientHeight >= scrollHeight) this._addCourseToList();
45+
state = {
46+
isLoading: false,
47+
hasMore: true,
4648
};
4749

50+
componentDidUpdate(prevProps, prevState) {
51+
if (
52+
this.inViewRef?.current &&
53+
this.state.hasMore &&
54+
prevState.isLoading &&
55+
!this.state.isLoading
56+
) {
57+
this._tryLoadMore();
58+
}
59+
}
60+
4861
showSearch = () => {
4962
const { openSearchDispatch } = this.props;
5063
openSearchDispatch();
@@ -98,6 +111,14 @@ class CourseListSection extends Component {
98111
return lists[selectedListCode].courses;
99112
};
100113

114+
_tryLoadMore = () => {
115+
const { isLoading, hasMore } = this.state;
116+
117+
if (isLoading || !hasMore) return;
118+
119+
this._addCourseToList();
120+
};
121+
101122
_addCourseToList = () => {
102123
const {
103124
setListCoursesDispatch,
@@ -106,23 +127,28 @@ class CourseListSection extends Component {
106127
setLastSearchOptionDispatch,
107128
} = this.props;
108129

130+
this.setState({ isLoading: true });
131+
109132
const courses = this._getCourses(selectedListCode);
110133
const offset = (lastSearchOption?.offset ?? 0) + REFRESH_LIMIT;
111134

112135
const option = {
113136
...lastSearchOption,
114137
offset,
115-
limit: offset + REFRESH_LIMIT,
138+
limit: REFRESH_LIMIT,
116139
};
117140

118141
const beforeRequest = () => {
119142
setLastSearchOptionDispatch(option);
120143
};
121144

122145
const afterResponse = async (newCourses) => {
123-
if (newCourses.length > 0) {
124-
await setListCoursesDispatch(CourseListCode.SEARCH, [...courses, ...newCourses]);
146+
await setListCoursesDispatch(CourseListCode.SEARCH, [...courses, ...newCourses]);
147+
if (newCourses.length < REFRESH_LIMIT) {
148+
this.setState({ hasMore: false, isLoading: false });
149+
return;
125150
}
151+
this.setState({ isLoading: false });
126152
};
127153

128154
performSearchCourses(option, option.limit, beforeRequest, afterResponse);
@@ -202,7 +228,7 @@ class CourseListSection extends Component {
202228
);
203229
}
204230
return (
205-
<Scroller key={selectedListCode} onScroll={this.onScrollChange}>
231+
<Scroller key={selectedListCode}>
206232
<div className={classNames('block-list')}>
207233
{courses.map((c, idx) => (
208234
<CourseBlock
@@ -215,6 +241,17 @@ class CourseListSection extends Component {
215241
onClick={this.focusCourseWithClick}
216242
/>
217243
))}
244+
245+
<InView
246+
threshold={0}
247+
onChange={(inView) => {
248+
this.inViewRef.current = inView;
249+
if (inView) {
250+
this._tryLoadMore();
251+
}
252+
}}>
253+
{({ ref }) => <div ref={ref}></div>}
254+
</InView>
218255
</div>
219256
</Scroller>
220257
);

src/components/sections/dictionary/courselist/CourseSearchSubSection.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class CourseSearchSubSection extends Component {
4848
};
4949

5050
searchStart = () => {
51-
const LIMIT = 20;
51+
const LIMIT = 10;
5252

5353
const { t } = this.props;
5454
const { selectedTypes, selectedDepartments, selectedLevels, selectedTerms, keyword } =

src/components/sections/timetable/lecturelist/LectureListSection.jsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,36 @@ import {
6262
import LectureGroupBlockRow from '../../../blocks/LectureGroupBlockRow';
6363
import { TIMETABLE_START_HOUR } from '../../../../common/constants';
6464

65+
import { InView } from 'react-intersection-observer';
66+
6567
const REFRESH_LIMIT = 50;
6668

6769
class LectureListSection extends Component {
6870
constructor(props) {
6971
super(props);
7072
this.arrowRef = React.createRef();
73+
this.inViewRef = React.createRef();
7174
}
7275

73-
onScrollChange = (e) => {
74-
const { scrollTop, scrollHeight, clientHeight } = e;
75-
76-
if (scrollTop + clientHeight >= scrollHeight) this._addLectureGroups();
76+
state = {
77+
isLoading: false,
78+
hasMore: true,
7779
};
7880

7981
componentDidMount() {
8082
window.addEventListener('resize', this.selectWithArrow);
8183
}
8284

8385
componentDidUpdate(prevProps, prevState, snapshot) {
86+
if (
87+
this.inViewRef?.current &&
88+
this.state.hasMore &&
89+
prevState.isLoading &&
90+
!this.state.isLoading
91+
) {
92+
this._tryLoadMore();
93+
}
94+
8495
const { lists, selectedListCode, lectureFocus, isLectureListOpenOnMobile } = this.props;
8596

8697
if (selectedListCode !== prevProps.selectedListCode) {
@@ -297,6 +308,14 @@ class LectureListSection extends Component {
297308
clearLectureFocusDispatch();
298309
};
299310

311+
_tryLoadMore = () => {
312+
const { isLoading, hasMore } = this.state;
313+
314+
if (isLoading || !hasMore) return;
315+
316+
this._addLectureGroups();
317+
};
318+
300319
_getLectureGroups = (selectedListCode, lists) => {
301320
if (!lists[selectedListCode]) {
302321
return null;
@@ -314,6 +333,8 @@ class LectureListSection extends Component {
314333
lists,
315334
} = this.props;
316335

336+
this.setState({ isLoading: true });
337+
317338
const lectures = this._getLectureGroups(LectureListCode.SEARCH, lists);
318339

319340
let offset = 0;
@@ -357,6 +378,12 @@ class LectureListSection extends Component {
357378
spreadedLectures = spreadedLectures.concat(response.data);
358379

359380
setListLecturesDispatch(LectureListCode.SEARCH, spreadedLectures);
381+
382+
if (response.data.length < REFRESH_LIMIT) {
383+
this.setState({ hasMore: false, isLoading: false });
384+
return;
385+
}
386+
this.setState({ isLoading: false });
360387
}
361388
})
362389
.catch((error) => {});
@@ -448,7 +475,6 @@ class LectureListSection extends Component {
448475
<Scroller
449476
onScroll={(e) => {
450477
this.selectWithArrow();
451-
this.onScrollChange(e);
452478
}}
453479
key={selectedListCode}>
454480
<div className={classNames('block-list')}>
@@ -480,6 +506,16 @@ class LectureListSection extends Component {
480506
))}
481507
</LectureGroupBlock>
482508
))}
509+
<InView
510+
threshold={0}
511+
onChange={(inView) => {
512+
this.inViewRef.current = inView;
513+
if (inView) {
514+
this._tryLoadMore();
515+
}
516+
}}>
517+
{({ ref }) => <div ref={ref}></div>}
518+
</InView>
483519
</div>
484520
</Scroller>
485521
);

src/pages/DictionaryPage.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class DictionaryPage extends Component {
5656
}
5757

5858
if (startSearchKeyword && startSearchKeyword.toString().trim()) {
59-
const LIMIT = 20;
59+
const LIMIT = 10;
6060

6161
const option = {
6262
keyword: startSearchKeyword.toString().trim(),

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6033,6 +6033,11 @@ react-i18next@^11.7.2:
60336033
"@babel/runtime" "^7.14.5"
60346034
html-parse-stringify "^3.0.1"
60356035

6036+
react-intersection-observer@^10.0.0:
6037+
version "10.0.0"
6038+
resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-10.0.0.tgz#77613d4e819dd170fbac4821c126c046aab69978"
6039+
integrity sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg==
6040+
60366041
react-is@^16.13.1, react-is@^16.7.0:
60376042
version "16.13.1"
60386043
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"

0 commit comments

Comments
 (0)