Javascript React Hooks useffect从useCallback调用prop回调
我正在尝试制作一个带有React钩子(和Research Gate React Intersection Observer)的通用无限滚动条。其思想是,父级将传递一个映射的JSX数据数组和一个回调,该回调将异步获取该数组的更多数据,当交叉点观察者触发时,因为您向下滚动到足以显示加载图标,将调用回调并加载更多数据 它工作得很好,除了一件事:esLint告诉我,因为我在useEffect中调用getMore函数(从道具),所以它必须是该效果的依赖项。但因为在父级回调中,我正在访问其数据数组的长度,所以该数组必须依赖于useCallback。然后该回调修改数组 TL;DR:我得到的竞争条件会导致异步回调在不应该触发的情况下多次触发,因为回调函数引用正在更改,然后被传递给调用它的对象 这里有一些代码需要澄清 父对象中的回调:Javascript React Hooks useffect从useCallback调用prop回调,javascript,reactjs,react-hooks,race-condition,use-effect,Javascript,Reactjs,React Hooks,Race Condition,Use Effect,我正在尝试制作一个带有React钩子(和Research Gate React Intersection Observer)的通用无限滚动条。其思想是,父级将传递一个映射的JSX数据数组和一个回调,该回调将异步获取该数组的更多数据,当交叉点观察者触发时,因为您向下滚动到足以显示加载图标,将调用回调并加载更多数据 它工作得很好,除了一件事:esLint告诉我,因为我在useEffect中调用getMore函数(从道具),所以它必须是该效果的依赖项。但因为在父级回调中,我正在访问其数据数组的长度,所
const loadData = useCallback(async () => {
if (hasMore) {
const startAmount = posts.length;
for (let i = 0; i < 20; ++i) {
posts.push(`I am post number ${i + startAmount}.`);
await delay(100);
}
setPosts([...posts]);
setHasMore(posts.length < 100);
}
}, [posts, hasMore]);
我将isLoading设置为true以触发效果;但它也会触发,因为当父级加载数据并且函数进行记忆时,getMore的引用会发生变化。这不应该发生。我可以为该行禁用esLint,但我认为有更好的解决方案,我想知道它是什么。解决方案:根本不使用useEffect。只需直接从观察者调用加载函数,设置isLoading并调用回调,而不是让isLoading触发回调
const loadData = async (observerEntry) => {
if (observerEntry.isIntersecting && !disabled && !isLoading) {
setIsLoading(true);
await getMore();
setIsLoading(false);
}
};
为什么
getMore
引用会更改?您可以在定义getMore
的地方共享代码吗?我认为真正的问题是,您正在使用isLoading bool来控制异步数据加载,而实际情况恰恰相反。您可以在useEffect之外将异步函数设置为自己的函数,并在需要时调用它,即intersectionObserver事件触发时。异步函数将在开始时setIsLoading(true)
,然后在结束时setIsLoading(false)
控制isLoading
变量。@Khauri问题是状态设置器是异步的,观察者可以在短时间内触发多次,具体取决于用户滚动的方式。因此,即使观察者调用的函数立即调用setIsLoading(true)
,它仍然可以在值更改为true
之前再次被调用,因此我仍然会得到重复调用:(…或者,至少我发誓我第一次尝试的时候就是这样。但是现在当我重新实现它时,它工作得很好O_O我以前肯定有一个未知的错误…你能把它作为一个官方答案发布出来让我接受吗?:DHmm我一生中只使用过IntersectionObserver一次,所以我不确定它是如何工作的。如果你有类似的错误我有点累了,你应该发布你自己的解决方案,并接受这个答案。
const loadData = async (observerEntry) => {
if (observerEntry.isIntersecting && !disabled && !isLoading) {
setIsLoading(true);
await getMore();
setIsLoading(false);
}
};