[英]React: I can't get updated state values in the onScroll function
任务:我正在尝试在滚动响应时实现内容的动态加载。 我有一个onScroll
函数来监听滚动事件,当页面一直向下滚动时,它将 isFetching 变量切换为 true。 当isFetching === true
时,执行来自useEffect
的代码并加载内容。 一切正常,但我需要向onScroll
添加另一个条件,该条件在加载的数据量 < 数据总量时触发。
问题:我有变量schoolResults
和schoolResultsCount
,其中包含加载数据的数量和数据总量,但是滚动时它们不会更新,滚动时它们始终为 0。 我知道我可以将功能转移到useEffect
或使用useEffect
中的条件,一切都会按我的需要工作。 但我不明白为什么函数中没有外部变量的更新值?
谢谢您的帮助!
export default function Results(props: Props) {
const location = useLocation();
const [isLoading, setLoading] = useState<boolean>(false);
const [isFetching, setFetching] = useState<boolean>(false);
const [schoolResults, setSchoolResults] = useState<SchoolEvent[]>([]);
const [schoolResultsCount, setSchoolResultsCount] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
const { school } = props;
const { id: schoolId } = school;
useEffect(() => {
setLoading(true);
const promises = [
getSchoolEvents(schoolId, getFilterForAllSchoolResults()),
getSchoolEventsCount(schoolId, getFilterForAllSchoolResults())
];
Promise.all(promises).then(([events, eventsCountObj]) => {
setSchoolResults(events);
setSchoolResultsCount(eventsCountObj.count);
setLoading(false);
});
}, [location.search]);
useEffect(() => {
if (isFetching) {
const queryFilter = {
...getFilterForAllSchoolResults(),
skip: currentPage * LIMIT
};
getSchoolEvents(schoolId, queryFilter)
.then((events) => {
setSchoolResults([...schoolResults, ...events]);
setCurrentPage(prevState => prevState + 1);
})
.finally(() => setFetching(false));
}
}, [isFetching]);
useEffect(() => {
document.addEventListener('scroll', onScroll);
return function () {
document.removeEventListener('scroll', onScroll);
}
}, []);
const onScroll = (event: Event) => {
const { target } = event;
const scrollHeight = propz.get(target, ['documentElement', 'scrollHeight']);
const scrollTop = propz.get(target, ['documentElement', 'scrollTop']);
const windowInnerHeight = window.innerHeight;
const isBottomOfPage = scrollHeight - (scrollTop + windowInnerHeight) < 100;
// console.log('schoolResults', schoolResults.length); //The value is 0. Not updated. Why?
// console.log('schoolResultsCount', schoolResultsCount); // The value is 0. Not updated. Why?
if (isBottomOfPage /*&& schoolResults.length > schoolResultsCount*/) {
setFetching(true);
};
};
if (isLoading) {
return <Loader />;
}
return (//Some JSX)
}
这是一个经典的陈旧关闭问题。
您有一个没有依赖关系的useEffect
,它在组件安装时将onScroll
处理程序附加到scroll
事件(并在组件卸载时将其删除)。 由于依赖数组为空,因此该函数在组件的生命周期内是相同的。
这就是问题所在,因为在第一次渲染的范围内的onScroll
是一个引用空数组作为它的schoolResults
的对象,因为这就是const schoolResults
在第一次渲染时所持有的内容。 是的,它在随后的渲染中会有不同的值,但是这些对于正在使用的onScroll
事件处理程序来说永远无法访问。
最佳解决方案可能取决于您尚未提供的组件的详细信息。 最简单的方法是用 ref 替换状态,因为 ref 与状态变量不同,在渲染之间保持相同的身份,只有它们current
的属性更新(变异)。 所以你可以更换
const [schoolResults, setSchoolResults] = useState<SchoolEvent[]>([]);
和
const schoolResults = useRef<SchoolEvent[]>([]);
并将所有对setSchoolResults(someValue)
的调用替换为schoolResults.current = someValue
。 (同样,在您引用schoolResults
以获取其值的地方,将其替换为schoolResults.current
。)这将解决陈旧的关闭问题。 (并且您必须对每个相关的状态变量执行此操作。)
然而,与状态相比,引用的缺点是更新引用不会导致重新渲染,这与状态更新不同。 因此,如果您的组件渲染的 JSX 输出中的任何内容使用isLoading
,这将无法按预期工作。
因此,在这种情况下,您将不得不使用另一种解决方案,其中涉及诚实地承认您的状态正在更新这一事实,因此您需要将事件处理函数更新为使用正确值的函数。 虽然只有几个步骤:
首先,将事件处理程序包装在useCallback
中,并将您引用的所有相关状态值放入其依赖数组中:
const onScroll = useCallback(() => { /* your function goes here */, [schoolResults, schoolResultsCount, /* you possibly need other dependencies here, that are used inside your function */]};
然后将onScroll
作为依赖项添加到添加滚动处理程序的useEffect
中:
useEffect(() => {
document.addEventListener('scroll', onScroll);
return function () {
document.removeEventListener('scroll', onScroll);
}
}, [onScroll]);
通过确保滚动处理程序始终保存状态变量的最新值,这应该可以按您的预期工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.