Svelte 3, XState 및 IntersectionObserver를 사용한 무한 스크롤
소개
1년 전에 저는 Svelte 및 IntersectionObserver를 사용하여 무한 스크롤과 관련된 것을 만들었습니다. 이번에는 XState를 사용하여 유사한 프로젝트(두 번째 버전이라고 할 수 있음)를 보여 드리겠습니다.
If you want to try it out go to this Live Demo
프로젝트 폴더 생성
Svelte 템플릿을 사용하려면 터미널에서 이러한 명령을 하나씩 실행하십시오.
# Install a Svelte project using sveltejs/template
$ npx degit sveltejs/template infinite-scroll
# Change working directory
$ cd infinite-scroll
# Install npm dependencies
$ npm install
# Install XState
$ npm install xstate
# Run the dev server
$ npm run dev
구성 요소 생성
src one 안에 components 라는 폴더를 만든 다음
Character.svelte
, Footer.svelte
, Header.svelte
, Loader.svelte
4개의 구성 요소를 만들고 Svelte의 파일 확장자.svelte
를 사용해야 합니다.구성 요소는 기본적으로 이전 게시물과 동일하며 코드here를 찾을 수 있습니다.
유틸리티 함수 생성
src 1 안에 lib라는 폴더를 만든 다음
index.js
파일을 만듭니다. 따라서 우리의 삶을 더 쉽게 만들기 위해 몇 가지 util 함수를 구현해 보겠습니다.export const transformCharacter = character => {
const {
id,
name,
status,
species,
created,
image,
gender,
origin,
location,
} = character
return {
id,
name,
image,
status,
species,
gender,
created,
origin: origin.name,
location: location.name,
}
}
// Format created date.
export const relativeTime = created => {
const createdYear = new Date(created).getFullYear()
const currentYear = new Date().getFullYear()
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
return rtf.format(createdYear - currentYear, 'year')
}
// Util function to fetch the list of Characters.
export const fetchCharacters = async (context) => {
try {
const endpoint = context.characters.length > 0 ? context.pageInfo.next : 'https://rickandmortyapi.com/api/character'
const blob = await fetch(endpoint)
const { results, info, error } = await blob.json()
if (!blob.ok) {
return Promise.reject(error)
}
const characters = results.map(result => transformCharacter(result))
return Promise.resolve({ characters, info })
} catch (error) {
return Promise.reject(error.message)
}
}
상태 머신 생성
lib 폴더에
scrollMachine.js
라는 파일을 만들고 XState를 사용하여 상태 머신을 구현해 보겠습니다.import { createMachine, assign } from 'xstate'
const scrollMachine = createMachine({
id: 'scrollMachine',
context: {
characters: [],
pageInfo: {},
error: '',
},
initial: 'idle',
states: {
idle: {
on: {
FETCH: {
target: 'loading',
},
},
},
loading: {
invoke: {
id: 'characters',
src: 'fetchCharacters',
onDone: {
target: 'success',
actions: 'setCharacters',
},
onError: {
target: 'failure',
actions: 'setError',
},
},
},
loadMore: {
invoke: {
src: 'fetchCharacters',
onDone: {
target: 'success',
actions: 'setMoreCharacters',
},
onError: {
target: 'failure',
actions: 'setError',
},
},
},
success: {
on: {
FETCH_MORE: {
target: 'loadMore',
cond: 'hasMoreCharacters',
},
},
},
failure: {
type: 'final',
},
},
}, {
guards: {
hasMoreCharacters: ({ pageInfo }) => pageInfo.next,
},
actions: {
setCharacters: assign({
characters: (_, event) => event.data.characters,
pageInfo: (_, event) => event.data.info,
}),
setMoreCharacters: assign({
characters: ({ characters }, { data }) => [...characters, ...data.characters],
pageInfo: (_, { data }) => data.info,
}),
setError: assign({
error: (_, event) => event.data,
}),
},
})
export default scrollMachine
앱 구성 요소 업데이트
<script>
import { onMount, onDestroy } from 'svelte'
import { interpret } from 'xstate'
import { fetchCharacters } from './lib'
import scrollMachine from './lib/scrollMachine'
import Header from './components/Header.svelte'
import Loader from './components/Loader.svelte'
import Character from './components/Character.svelte'
import Footer from './components/Footer.svelte'
// Let's pass the fetchCharacters function
// as a service to our state machine.
const machine = scrollMachine.withConfig({
services: { fetchCharacters },
})
// Interpret our machine and start it ("create an instance").
const service = interpret(machine).start()
// Create options to our IntersectionObserver instance.
let options = {
root: document.getElementById('scrollArea'),
rootMargin: '0px',
threshold: 0.5,
}
// Handle intersection and send `FETCH_MORE` event to
// our state machine if there are more characters to load.
let observer = new IntersectionObserver(event => {
const [entries] = event
if (!entries.isIntersecting || !$service.context.pageInfo.next) {
return
}
service.send({ type: 'FETCH_MORE' })
}, options)
// Fetch characters on component mounts.
onMount(() => {
service.send({ type: 'FETCH' })
observer.observe(document.querySelector('footer'))
})
// Remove observer from our target to avoid potential memory leaks.
onDestroy(() => {
observer.unobserve(document.querySelector('footer'))
})
</script>
<style>
.container {
min-height: 50vh;
background-color: var(--text-color);
}
.inner {
max-width: 80em;
margin: 0 auto;
padding: 3rem 0;
display: grid;
grid-gap: 20px;
justify-items: center;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.loader,
.error {
padding-top: var(--padding-lg);
display: flex;
align-items: center;
justify-content: center;
}
.error {
color: var(--orange-color);
font-size: 18px;
}
</style>
<Header />
<section class="container" id="scrollArea">
{#if $service.matches('success') || $service.matches('loadMore')}
<div class="inner">
{#each $service.context.characters as character (character.id)}
<Character {character} />
{/each}
</div>
{/if}
{#if $service.matches('failure')}
<div class="error"><span>{$service.context.error}</span></div>
{/if}
{#if $service.matches('loading') || $service.matches('loadMore')}
<div class="loader">
<Loader />
</div>
{/if}
<Footer />
</section>
메모:
Rick and Marty API 문서는 다음에서 찾을 수 있습니다. here
GitHub 저장소: here
유튜브 비디오(스페인어):
즐거운 코딩 👋🏽
Reference
이 문제에 관하여(Svelte 3, XState 및 IntersectionObserver를 사용한 무한 스크롤), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/gcdcoder/infinite-scrolling-with-svelte-3-xstate-and-intersectionobserver-127j텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)