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;
修改说明
- 拆分
useEffect逻辑:- 第一个
useEffect仅处理缓存加载,有缓存时直接展示,无缓存才调用API获取首批数据 - 第二个
useEffect仅处理分页加载,避免首次加载重复调用API
- 第一个
- 调整缓存逻辑:仅在
currentPage=0时缓存数据,确保只缓存首批25张图片 - 修复重复key问题:有缓存时不再触发首次API请求,避免重复添加相同图片
内容的提问来源于stack exchange,提问作者Tomas Gil Amoedo
相关产品推荐
相关产品推荐

