表示组件中数据数组的状态在函数内部使用时未更新

问题描述 投票:0回答:1

我正在尝试使用

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 所给出的相同问题。

javascript reactjs typescript react-hooks
1个回答
0
投票

问题在于,由于闭包,

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]);
© www.soinside.com 2019 - 2024. All rights reserved.