我在网上看到了一些解决方案,但我无法让它们发挥作用。
我有一个 React Native 应用,它从 API 加载数据。数据是分页的;每次我检索一个页面时,我都会收到该页面的结果以及下一页的 URL。因此,API 的典型响应采用以下格式(显然它比这更复杂一些,但这是要点):
{
data: [
{ key: xx, title: 'Item 1' },
{ key: yy, title: 'Item 2' }
],
next: 'www/url/to/next/page/of/results'
}
我想在屏幕上显示每个项目,并且当用户滚动到屏幕底部时,应该加载下一组结果。我正在尝试FlatList
为此使用。
到目前为止我已经(我还没有进行任何类型的错误检查或任何操作;只是试图先让它工作):
const HomeScreen = () => {
const [next, setNext] = React.useState<string>(BASE_URL); // URL of first page of results
const [isLoading, setIsLoading] = React.useState<boolean>(true);
const [displayItems, setDisplayItems] = React.useState<Item[]|null>(null);
// Get next page of items
const fetchItems = async () => {
setIsLoading(true);
const response = await client(next, 'GET'); // Just calls axios
setDisplayItems((items) => items.concat(response.data));
setNext(response.next);
setIsLoading(false);
};
// Get items on first loading screen
React.useEffect(() => {
fetchItems();
}, [fetchItems]);
// Show items
if (isLoading) return <LoadingSpinner />
if (displayItems && displayItems.length === 0) return <Text>Nothing to show</Text>
return <FlatList
onEndReachedThreshold={0}
onEndReached={fetchItems}
data={displayItems}
renderItem={(i) => <ShowItem item={i}/>} />
};
export default HomeScreen;
问题在于它会标记一个错误,指出The 'fetchItems' function makes the dependencies of useEffect Hook change on every render.
。它建议To fix this, wrap the definition of 'fetchItems' in its own useCallback() Hook.
。
所以我把它包在一个useCallback()
钩子里:
const fetchItems = React.useCallback(async () => {
setIsLoading(true);
const response = await client(next, 'GET'); // Just calls axios
setDisplayItems((items) => items.concat(response.data));
setNext(response.next);
setIsLoading(false);
}, [next]);
除非我添加,否则它根本无法运行fetchItems()
,但此时它会无限地重新渲染。
我在网上找不到任何可行的方法。令人恼火的是,我记得几年前为另一个项目实施过这个,但我不记得它特别复杂!任何帮助都感激不尽。
您对 useEffect 的评论表明该效果的目的是在初始渲染时获取数据;因此您需要向该效果添加条件以确保它能够做到这一点:
这样做时,当
fetchItems
或displayItems
发生变化时会调用 useEffect,但只有fetchItems
当为displayItems
null(其初始状态)时才会调用。我认为没有必要用 useCallback 包装,但无论是否包装,fetchItems
此效果都应该有效。fetchItems
对于您在问题中提到的主要错误:
React.useCallback
,则每次渲染(或状态更新)时,例如,都会创建setNext(response.next)
一个新的 引用。这会强制再次触发 ,从而导致再次调用。这会产生无限的 API 调用和重新渲染循环。fetchItems
useEffect
fetchItems
React.useCallback
,由于您已next
在依赖项数组中包含(状态变量),并且在函数next
内进行更新fetchItems
,因此当next
更新时,React.useCallback
将返回对 的新引用fetchItems
,从而useEffect
将再次被触发。简单的解决方案:从依赖数组
fetchItems
中删除useEffect
,以避免不必要地触发重新渲染。最佳实践:
useRef
而不是useState
fornext
:这可以防止不必要的重新渲染,因为 useRef 更新时不会触发重新渲染,这与 useState 不同。onEndReached
仅当没有正在进行的 API 调用(数据提取)时才应调用:实现标志(isLoading
)或条件以确保在已经发生提取时不会触发其他提取。keyExtractor
:始终确保每个列表项都有一个稳定且唯一的键,以帮助 React 高效优化渲染。以下是示例解决方案: