背景
我正在开发一个基于React的简单歌词应用程序。
目标
当用户向下滚动艺术家列表时,使用Intersection Observer API加载更多艺术家。
信息
驱动前端的API具有分页功能。 offset
从0
开始,而limit
是10
。对API的调用如下:
https://api.bejebeje.com/artists?offset=0&limit=10
整个应用程序是deployed here。我在代码中包含有用的console.log()
语句,因此打开Chrome开发工具会有所帮助。
代码
所有代码均为开放源代码,并且为sits on GitHub。我正在处理的分支是 feature / intersection-api-enhancements 。
这里是App.jsx
组件:
import React, { useRef, useCallback, useState, useEffect } from "react";
import { render } from "react-dom";
import { Router } from "@reach/router";
import axios from "axios";
import Authorisation from "./components/Authorisation/Authorisation";
import Artists from "./components/Artists/Artists";
import ArtistLyrics from "./components/ArtistLyrics/ArtistLyrics";
import Lyric from "./components/Lyric/Lyric";
import { API_CONSTANTS } from "./helpers/apiEndpoints";
import "./App.css";
function App() {
const [artists, setArtists] = useState([]);
const offset = useRef(0);
const limit = 10;
const callback = entries => {
console.log(
`Intersection callback fired. isIntersecting is ${
entries[0].isIntersecting
}.`
);
if (entries[0].isIntersecting) {
console.log("about to fetch artists ...");
fetchArtists(offset, limit);
}
};
const fetchArtists = (offset, limit) => {
axios.get(API_CONSTANTS.artists(offset.current, limit)).then(result => {
const artistsArray = result.data.artists;
setArtists([...artists, ...artistsArray]);
offset.current += artistsArray.length;
console.log(`offset is now at ${offset.current}.`);
});
};
useEffect(() => {
fetchArtists(offset, limit);
}, []);
return (
<div>
<Router>
<Artists path="/" artists={artists} intersectionCallback={callback} />
<ArtistLyrics path="artists/:artistSlug/lyrics" />
<Lyric path="artists/:artistSlug/lyrics/:lyricSlug" />
<Authorisation path="/callback" />
</Router>
</div>
);
}
render(<App />, document.getElementById("root"));
这是Artists.jsx
组件:
import React, { useEffect, useRef } from "react";
import Header from "../Header/Header";
import "./Artists.css";
import ArtistCard from "../ArtistCard/ArtistCard";
function Artists(props) {
const getPrimaryArtistSlug = slugs => {
return slugs.filter(slug => slug.isPrimary)[0].name;
};
const singleRefs = props.artists.reduce((acc, value, index) => {
acc[index] = React.createRef();
return acc;
}, {});
const observer = new IntersectionObserver(props.intersectionCallback, {
root: null,
threshold: 0.8
});
useEffect(() => {
if (props.artists.length > 0) {
const indexOfLastArtist = props.artists.length - 2;
observer.observe(singleRefs[indexOfLastArtist].current);
const entries = observer.takeRecords();
}
}, [props.artists]);
return (
<>
<Header title="Browse" />
<div className="artists__list">
{props.artists.map((artist, index) => {
const primarySlug = getPrimaryArtistSlug(artist.slugs);
return (
<ArtistCard
key={primarySlug}
artist={artist}
primarySlug={primarySlug}
itemRef={singleRefs[index]}
/>
);
})}
</div>
</>
);
}
export default Artists;
问题
每次重新渲染Artists.jsx
时,都会得到一个新的IntersectionObserver
,然后将观察目标设置为artists数组中位于last之前的项目。因此,当props.artists
有10个项目时,目标是列表中的第9位艺术家。当props.artists
有20个项目时,目标是列表中的第19个艺术家,依此类推。
在初始页面加载时,所有功能似乎都能按预期工作,我向下滚动到第9个项目,并对该API进行了新调用,并加载并渲染了10个以上的艺术家。
新目标应该是第19位艺术家。但是,如果我向上滚动然后向下滚动经过第9个艺术家,则回调会再次触发!这是为什么?第9个演出者项目不再是观察者的目标,因此我不确定为什么它出现时会触发回调。我很困惑。