You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native FlatList无限滚动缓存:不必要API调用触发问题

React Native NASA火星车图片应用:缓存与重复API调用问题解决

问题详情

开发React Native应用调用NASA API获取火星车图片、实现FlatList无限滚动时,遇到以下问题:

  • 首次进入页面可正确获取并展示首批25张图片,已用AsyncStorage实现缓存,但离开返回页面时仍触发不必要的API调用,导致性能损耗和重复key警告
  • 当前缓存逻辑会缓存所有加载的图片,不符合仅缓存首批25张的需求

预期结果

  • 首次进入:获取并展示首批25张图片,缓存该批次内容
  • 返回页面:直接展示缓存数据,不触发新的API调用
  • 滚动到底部:加载更多图片但不重复缓存
  • 仅缓存首批25张图片

实际情况

  • 首次进入:正确获取并缓存图片
  • 返回页面:可展示缓存图片,但仍触发API调用,出现「Encountered two children with the same key...」警告

代码片段

// ...imports

const PictureCollection = ({ route }: PictureCollectionStackParams) => {
  const { title } = route.params;
  const [currentPage, setCurrentPage] = useState(0);
  const [isLoading, setIsLoading] = useState(false);
  const [loadMore, setLoadMore] = useState(true);
  const [error, setError] = useState(false);
  const [response, setResponse] = useState<Photo[]>([]);

  const handleFetchData = async () => {
    setIsLoading(true);

    axios
      .get(
        // API URL
      )
      .then(res => {
        if (res.data.photos.length === 0) {
          setLoadMore(false);
        }
        setResponse(prevResponse => [...prevResponse, ...newPhotos]);
        setIsLoading(false);

        // store new images in cache
        try {
          // Store new photos in cache
          AsyncStorage.setItem(
            `cachedPhotos_${title}`,
            JSON.stringify([...response, ...newPhotos]),
          );
        } catch (error) {
          console.error('Error saving cached photos:', error);
        }
      })
      .catch((error: any) => {
        // Handling errors
      });
  };

  useEffect(() => {
    try {
      AsyncStorage.getItem(`cachedPhotos_${title}`)
        .then(cachedPhotos => {
          if (cachedPhotos) {
            setResponse(JSON.parse(cachedPhotos));
            console.log('Cached photos displayed');
          }
        })
        .catch(error => {
          console.error('Error loading cached photos:', error);
        });
    } catch (error) {
      console.error('Error loading cached photos:', error);
    }
    handleFetchData();
  }, [currentPage, title]);

    const onEndReached = () => {
    if (loadMore && !isLoading) {
      setLoadMore(true);
      setCurrentPage(currentPage + 1);
      console.log('loading more items');
    }
  };

  // ...rest of the code
};

export default PictureCollection;

问题解答

1. 返回页面后触发API调用的原因

你的useEffect依赖数组包含currentPage和title,每次页面挂载时currentPage初始值为0、title不变,因此useEffect会执行。而useEffect内不管是否加载到缓存,都会直接调用handleFetchData,导致即使缓存存在,仍会触发API请求,进而重复添加相同图片,引发重复key警告。

2. 仅缓存首批25张图片的实现方法

当前代码每次请求数据后都会更新缓存(包括后续分页数据),需判断仅当currentPage为0(即首次加载)时,才将数据存入缓存。

修改后的解决方案代码

// ...imports

const PictureCollection = ({ route }: PictureCollectionStackParams) => {
  const { title } = route.params;
  const [currentPage, setCurrentPage] = useState(0);
  const [isLoading, setIsLoading] = useState(false);
  const [loadMore, setLoadMore] = useState(true);
  const [error, setError] = useState(false);
  const [response, setResponse] = useState<Photo[]>([]);

  const handleFetchData = async () => {
    setIsLoading(true);

    axios
      .get(
        // API URL
      )
      .then(res => {
        const newPhotos = res.data.photos;
        if (newPhotos.length === 0) {
          setLoadMore(false);
        }
        setResponse(prevResponse => [...prevResponse, ...newPhotos]);
        setIsLoading(false);

        // 仅首次加载(currentPage=0)时缓存数据
        if (currentPage === 0) {
          try {
            AsyncStorage.setItem(
              `cachedPhotos_${title}`,
              JSON.stringify(newPhotos),
            );
          } catch (error) {
            console.error('Error saving cached photos:', error);
          }
        }
      })
      .catch((error: any) => {
        setIsLoading(false);
        setError(true);
        console.error('Error fetching photos:', error);
      });
  };

  useEffect(() => {
    const loadCachedData = async () => {
      try {
        const cachedPhotos = await AsyncStorage.getItem(`cachedPhotos_${title}`);
        if (cachedPhotos) {
          setResponse(JSON.parse(cachedPhotos));
          console.log('Cached photos displayed');
        } else {
          // 无缓存时才调用API获取首批数据
          handleFetchData();
        }
      } catch (error) {
        console.error('Error loading cached photos:', error);
        // 缓存加载失败时调用API
        handleFetchData();
      }
    };

    loadCachedData();
  }, [title]); // 仅在title变化时执行,移除currentPage依赖

  // 处理分页加载的逻辑
  useEffect(() => {
    // 仅当分页加载(currentPage>0)且可加载更多时调用API
    if (currentPage > 0 && loadMore && !isLoading) {
      handleFetchData();
    }
  }, [currentPage, loadMore, isLoading]);

  const onEndReached = () => {
    if (loadMore && !isLoading) {
      setCurrentPage(prev => prev + 1);
      console.log('loading more items');
    }
  };

  // ...rest of the code
};

export default PictureCollection;

修改说明

  1. 拆分useEffect逻辑:
    • 第一个useEffect仅处理缓存加载,有缓存时直接展示,无缓存才调用API获取首批数据
    • 第二个useEffect仅处理分页加载,避免首次加载重复调用API
  2. 调整缓存逻辑:仅在currentPage=0时缓存数据,确保只缓存首批25张图片
  3. 修复重复key问题:有缓存时不再触发首次API请求,避免重复添加相同图片

内容的提问来源于stack exchange,提问作者Tomas Gil Amoedo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 11:25:16