[英]React Hooks useEffect to call a prop callback from useCallback
I'm trying to make a general-purpose infinite scroller with React Hooks (and the ResearchGate React Intersection Observer).我正在尝试使用 React Hooks(和 ResearchGate React Intersection Observer)制作一个通用的无限滚动器。 The idea is that a parent will pass down a mapped JSX array of data and a callback that will asynchronously get more data for that array, and when the intersection observer fires because you've scrolled down enough to reveal the loading icon, the callback gets called and more data is loaded.
这个想法是,父级将传递一个映射的 JSX 数据数组和一个回调,该回调将异步获取该数组的更多数据,并且当交叉点观察者因为您向下滚动到足以显示加载图标而触发时,回调会获取调用并加载更多数据。
It works well enough, except one thing: esLint tells me that because I'm calling the getMore function (from the props) inside a useEffect, it must be a dependency of that effect.它工作得很好,除了一件事:esLint 告诉我,因为我在 useEffect 中调用 getMore function(来自道具),所以它必须是该效果的依赖项。 But because in the parent's callback I'm accessing its data array's length, that array must be a dependency of useCallback there.
但是因为在父回调中我正在访问其数据数组的长度,所以该数组必须是 useCallback 的依赖项。 And then that callback modifies the array.
然后该回调会修改数组。
TL;DR: I'm getting race conditions that cause the async callback to trigger multiple times when it shouldn't, because the callback function reference is changing and then being passed down to the thing that's calling it. TL; DR:我遇到了导致异步回调在不应该触发的情况下多次触发的竞争条件,因为回调 function 引用正在更改,然后被传递给调用它的东西。
Here's some code to clarify.这里有一些代码来澄清。
The callback in the parent:父级中的回调:
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]);
posts and hasMore are just state variables, with posts being passed down as the data array in props to the child. posts 和 hasMore 只是 state 变量,posts 作为 props 中的数据数组传递给孩子。 That function is being passed to the child in props, which has this (getMore is the destructured prop for the callback, isLoading is just a boolean state variable):
那个 function 被传递给道具中的孩子,它有这个(getMore 是回调的解构道具,isLoading 只是一个 boolean state 变量)
useEffect(() => {
if (isLoading) {
(async () => {
await getMore();
setIsLoading(false);
})();
}
}, [isLoading, getMore]);
I'm setting isLoading to true to trigger the effect;我将 isLoading 设置为 true 以触发效果; but it's also triggering because getMore's reference changes when the parent loads data and the function memoizes.
但它也会触发,因为当父级加载数据并且 function 记忆时,getMore 的引用发生了变化。 That shouldn't happen.
那不应该发生。 I could just disable esLint for that line, but I assume there's a better solution, and I'd like to know what it is.
我可以为该行禁用 esLint,但我认为有更好的解决方案,我想知道它是什么。
Solution: don't use useEffect at all.解决方案:根本不要使用 useEffect 。 Just call the loading function directly from the observer and have that set isLoading and call the callback rather than having isLoading trigger the callback.
只需直接从观察者调用加载 function 并设置 isLoading 并调用回调,而不是让 isLoading 触发回调。
const loadData = async (observerEntry) => {
if (observerEntry.isIntersecting && !disabled && !isLoading) {
setIsLoading(true);
await getMore();
setIsLoading(false);
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.