javascript - 自定义钩子错误:只能在函数组件的主体内部调用钩子
问题描述
我正在尝试开发一个似乎很容易的自定义钩子,但我遇到了一个错误
未捕获的不变违规:无效的挂钩调用。钩子只能在函数组件的主体内部调用。这可能由于以下原因之一而发生:
- 你可能有不匹配的 React 版本和渲染器(例如 React DOM)
- 您可能违反了 Hooks 规则
- 你可能在同一个应用程序中拥有多个 React 副本
这是我的钩子:
import React, { useState, useEffect } from 'react';
const useInfiniteScroll = (isLastPage: boolean, fetchFn: any) => {
const [pageCount, setPageCount] = useState(0);
const triggerFetchEvents = (): void => {
let response;
setPageCount(() => {
if (!isLastPage) {
response = fetchFn(pageCount + 1, 5, 'latest');
}
return pageCount + 1;
});
return response;
};
useEffect(() => {
triggerFetchEvents();
}, []);
return pageCount;
};
export default useInfiniteScroll;
我在这里调用它的组件:
import React, { FC } from 'react';
import { connect } from 'react-redux';
import { fetchEvents } from '../../shared/actions/eventActions';
import { AppState } from '../../shared/types/genericTypes';
import EventModel from '../../shared/models/Event.model';
import EventListPage from '../../components/events/EventListPage';
import useInfiniteScroll from '../../shared/services/triggerInfiniteScroll';
type Props = {
fetchEvents?: any;
isLastPage: boolean;
eventsList?: EventModel[];
};
const mapState: any = (state: AppState, props: Props): Props => ({
eventsList: state.eventReducers.eventsList,
isLastPage: state.eventReducers.isLastPage,
...props
});
const actionCreators = {
fetchEvents
};
export const EventsScene: FC<Props> = props => {
const { eventsList, fetchEvents, isLastPage } = props;
const useIn = () => useInfiniteScroll(isLastPage, fetchEvents);
useIn();
// const [pageCount, setPageCount] = useState(0);
// const triggerFetchEvents = (): void => {
// let response;
// setPageCount(() => {
// if (!isLastPage) {
// response = fetchEvents(pageCount + 1, 1, 'latest');
// }
// return pageCount + 1;
// });
// return response;
// };
// useEffect(() => {
// triggerFetchEvents();
// }, []);
if (!eventsList || !eventsList.length) return null;
return (
<EventListPage
eventsList={eventsList}
isLastPage={isLastPage}
triggerFetchEvents={useIn}
/>
);
};
export default connect(
mapState,
actionCreators
)(EventsScene);
我将注释代码留在那里向您展示,如果我取消注释并删除代码,useInfiniteScroll
那么它可以正常工作。
我会错过什么?
更新:这是EventListPage
组件
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import EventModel from '../../shared/models/Event.model';
import { formatDate } from '../../shared/services/date';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Card from 'react-bootstrap/Card';
type Props = {
eventsList?: EventModel[];
isLastPage: boolean;
triggerFetchEvents: any;
};
export const EventListPage: React.FC<Props> = props => {
const { eventsList, triggerFetchEvents, isLastPage } = props;
const [isFetching, setIsFetching] = useState(false);
const fetchMoreEvents = (): Promise<void> =>
triggerFetchEvents().then(() => {
setIsFetching(false);
});
const handleScroll = (): void => {
if (
document.documentElement.offsetHeight -
(window.innerHeight + document.documentElement.scrollTop) >
1 ||
isFetching
) {
return;
}
return setIsFetching(true);
};
useEffect(() => {
if (isFetching) return;
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
useEffect(() => {
if (!isFetching) return;
if (!isLastPage) fetchMoreEvents();
}, [isFetching]);
if (!eventsList) return null;
return (
<Container className='article-list mt-5'>
///...
</Container>
);
};
export default EventListPage;
解决方案
在EventsScene
中,更改useInfiniteScroll
为直接在函数体顶层调用(不知道为什么首先要创建此间接):
// before
const useIn = () => useInfiniteScroll(isLastPage, fetchEvents);
useIn();
// after
useInfiniteScroll(isLastPage, fetchEvents)
React 期望 Hook 调用只发生在顶层,因为它依赖于 Hook 的顺序始终相同。如果您将 Hook 包装在一个函数中,您可能会在许多代码位置调用此函数,从而扰乱 Hook 的顺序。
每个组件都有一个内部“存储单元”列表。它们只是 JavaScript 对象,我们可以在其中放置一些数据。当你调用 useState() 之类的 Hook 时,它会读取当前单元格(或在第一次渲染期间对其进行初始化),然后将指针移动到下一个单元格。这就是多个 useState() 调用各自获取独立本地状态的方式。关联
推荐阅读
- tensorflow - 导入 tensorflow 1.5.0 时 Jupyter Notebook 内核死机
- mysql - 存储过程函数中的过早循环退出正常工作,但并非针对所有变量运行
- python - 如何使用 Kivy 模块让动画一个接一个地播放
- jenkins - jenkins gradle 集成需要一些帮助
- mysql - 我的服务器不支持 mysql 中的 JSON 字段
- angular - 从 Angular 2.2 到 Angular 6.0 的角度更新
- python - 我正在使用 python3,我有字典列表
- javascript - 通过动态枚举其他对象的键来创建新对象
- c - 如何将字符串从函数返回到 main()?
- java - 在 Json 反序列化期间维护类字段的顺序