: 프로젝트에 사용하려고 했던 vue 인피니티 스크롤 컴포넌트가 vue3에서 지원을 하지 않아서 형태를 참고해서 직접 컴포넌트를 구현해 보았다.
: 정말 간단하게 만들어서 그런지 컴포넌트가 유연하지 않다..
부모 자식 컴포넌트간 통신하는 새로운 방법에 대해서 알게되었다
기존에는 부모 -> 자식은 props로 자식 -> 부모는 이벤트를 통해서 통신하는 방법만 알고 있었는데, 자식이 부모에 이벤트를 전달할 때 data를 변경할 수 있는 메소드를 가진 객체(참고한 라이브러리에서는 $state)를 전달하고, 이 $state의 메소드를 실행해서 자식의 data를 변경하는 방법을 알 수 있었다.
<!-- InfiniteScroll.vue -->
<template>
<div class="infinite-scroll">
<Spinner class="infinite-scroll__spinner" v-if="isLoading" />
</div>
</template>
<script>
import Spinner from '@/components/common/Spinner.vue';
class ObserverState {
constructor() {
this.status = '';
}
loading() {
this.status = 'loading';
}
complete() {
this.status = 'complete';
}
error() {
this.status = 'error';
}
loaded() {
this.status = 'loaded';
}
}
export default {
name: 'infinite-scroll',
components: {
Spinner,
},
data() {
return {
// 옵저버 상태
state: new ObserverState(),
// 옵저버 인스턴스
observer: null,
// 옵저버 옵션
options: {
threshold: 0.5,
},
};
},
computed: {
isLoading() {
return this.state.status === 'loading';
},
isComplete() {
return this.state.status === 'complete';
},
isError() {
return this.state.status === 'error';
},
isLoaded() {
return this.state.status === 'loaded';
},
},
watch: {
state: {
handler(newState) {
if (newState.status === 'loaded') {
this.refresh();
}
},
deep: true,
},
},
created() {
// 옵저버 인스턴스 생성
this.createInstance();
},
mounted() {
if (this.observer instanceof IntersectionObserver) {
// observe 시작
this.observer.observe(this.$el);
}
},
methods: {
// 옵저버 인스턴스 생성
createInstance() {
const observer = new IntersectionObserver(
this.observeHandler,
this.options,
);
this.observer = observer;
},
// intersect 콜백
observeHandler([entry]) {
const { intersectionRatio } = entry;
if (intersectionRatio >= this.options.threshold) {
const { status } = this.state;
// 로딩 중이거나 실행이 완료되었을 경우 함수 종료
if (status === 'loading' || status === 'complete') return;
this.state.loading();
this.$emit('infinite', this.state);
}
},
/**
* https://stackoverflow.com/questions/51402840/force-intersectionobserver-update
*/
refresh() {
if (this.observer instanceof IntersectionObserver) {
this.observer.unobserve(this.$el);
this.observer.observe(this.$el);
}
},
},
};
</script>
<style lang="scss" scoped></style>
<InfiniteScroll @infinite="infiniteHandler" />
methods: {
async infiniteHandler(state) {
try {
const results = await this.fetchMoreData();
if (results.length) {
state.loaded();
} else {
state.complete();
}
} catch (error) {
console.log(error.message);
}
}
}