我正在尝试使用
IntersectionObserverAPI
制作一个无限滚动的神奇宝贝列表,其中页面末尾有一个空的 div 元素,当需要查看更多神奇宝贝时,会从 API 中获取。
但是这使用
pokemons
数组(代表页面上所有神奇宝贝的状态)作为偏移量,以使用 length
属性加载数据。但 loadPokemons
函数内的 length 属性始终是 0
我不知道为什么。我尝试了很多事情,但还是发生了同样的事情。
这是 Pokemons 组件 -:
在这种情况下,每当观察者相交时,它都会调用
loadPokemons
函数,该函数使用 fetchPokemons
函数通过传入偏移量作为 pokemons.length
来获取数据,但这总是给出 0 我不知道为什么。请帮忙。
import { useEffect, useRef, useState } from "react";
import Pokemon from "./Pokemon";
import { type Pokemon as PokemonDataType } from "./types";
type ResponseType = {
count: number,
next: string,
previous: string | null,
results: PokemonDataType[]
}
const fetchPokemons = async (url: string, offset: number, limit: number): Promise<PokemonDataType[]> => {
const response = await fetch(`${url}?offset=${offset}&limit=${limit}`);
const data: ResponseType = await response.json();
const pokemons = data.results.map(result => { return { name: result.name, url: `https://img.pokemondb.net/artwork/${result.name}.jpg` } });
return pokemons;
}
const url = "https://pokeapi.co/api/v2/pokemon";
export default function Pokemons() {
const [pokemons, setPokemons] = useState<PokemonDataType[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const myRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const loadInitialPokemons = async () => {
const initData = await fetchPokemons(url, 0, 12);
setPokemons(initData);
const observer = new IntersectionObserver((entries) => {
for (let entry of entries) {
if (entry.isIntersecting && !isLoading) {
loadPokemons();
}
}
});
if (myRef.current) {
observer.observe(myRef.current);
}
}
loadInitialPokemons();
}, []);
const loadPokemons = async () => {
if (isLoading) return;
setIsLoading(true);
const morePokemons = await fetchPokemons(url, pokemons.length, 5);
setPokemons(prevPokemons => [...prevPokemons, ...morePokemons]);
setIsLoading(false);
}
return (
<div>
<ul>
{pokemons.map((pokemon, idx) => <Pokemon key={idx} name={pokemon.name} url={pokemon.url} />)}
</ul>
{isLoading && <h4>Loading....</h4>}
<div ref={myRef}></div>
</div>
);
}
这是神奇宝贝类型别名
export type Pokemon = {
url: string,
name: string
}
这是 Pokemon 组件 -:
import { type Pokemon } from "./types";
export default function Pokemon({ name, url }: Pokemon) {
return (
<li style={{ display: "block", width: "100%" }}>
<img src={url} />
<h3 style={{ textAlign: "center" }}>{name}</h3>
</li>
);
}
这是应用程序组件-:
import './App.css';
import Pokemons from './Pokemons';
function App() {
return (
<>
<Pokemons />
</>
);
}
export default App;
首先,我在
loadInitialPokemons
函数之外初始化了观察者 api,但它给出了与现在在函数内部初始化观察者 API 所给出的相同问题。
问题在于,由于闭包,
loadPokemons
函数始终使用pokemons
的初始空状态。当您创建 IntersectionObserver
并在 useEffect
中定义其回调函数时,它会捕获该时间点的神奇宝贝的状态(当神奇宝贝最初是一个空数组时)
这是解决此问题的方法
const loadPokemons = useCallback(async () => {
if (isLoading) return;
setIsLoading(true);
const morePokemons = await fetchPokemons(url, pokemons.length, 5);
setPokemons(prevPokemons => [...prevPokemons, ...morePokemons]);
setIsLoading(false);
}, [isLoading, pokemons]);
然后将其作为依赖项传递给 useEffect
useEffect(() => {
const loadInitialPokemons = async () => {
const initData = await fetchPokemons(url, 0, 12);
setPokemons(initData);
const observer = new IntersectionObserver((entries) => {
for (let entry of entries) {
if (entry.isIntersecting && !isLoading) {
loadPokemons();
}
}
});
if (myRef.current) {
observer.observe(myRef.current);
}
};
loadInitialPokemons();
return () => {
if (myRef.current) {
const observer = new IntersectionObserver(() => {});
observer.disconnect();
}
};
}, [loadPokemons, isLoading]);