【发布时间】:2021-04-04 03:03:09
【问题描述】:
我正在尝试创建一个在您向下滑动时更新的内容提要。我的逻辑是我需要在呈现提要之前获取所需的数据,并且每次我在向下滑动页面时点击第 n 个元素时都会获取新数据。所以我想有一个布尔状态变量来检测第一次渲染,然后我将其设置为 false 以不再运行查询。
const Feed = (props) => {
const [feedArray, setFeedArray] = useState([]);
const [firstRender, setFirstRender] = useState(true);
const FEED_QUERY = gql`
query getFeedArray {
getFeedArray
}
`;
if (firstRender) {
const { loading, error, data } = useQuery(FEED_QUERY);
if (loading) return <Loading/>;
if (error) return `Error! ${error}`;
feedArray.push(...data.getFeedArray);
setFirstRender(false);
}
return (
<RenderredComponent />
);
}
export default withApollo(withRouter(Feed));
显然,这会导致错误 (Unexpected Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.),因为 useQuery 挂钩仅在 if 语句中调用一次。
我尝试了不同的方法来执行此操作,因为我在渲染组件之前需要这些数据。我尝试使用useEffect() 和props.client.query() 但这不起作用,因为它允许gql 查询同步运行,因此仅使用第二次渲染上的数据更新DOM,并且我无法阻止基于loading 状态。我不能将 useQuery() 放在 useEffect() 中,因为它也会引发错误(钩子中的钩子)。
如果我将 useQuery 钩子放在 if 语句之外,它可以工作,但会在每次渲染时运行,这是不必要的额外调用。对于上下文 - 由于React Swipeable Views 的工作原理,当我向下滚动时,渲染经常发生。
所以简单地说 - 我怎样才能在组件被渲染之前只运行一次 useQuery 钩子?
感谢您的帮助,谢谢!
编辑:下面的屏幕截图说明正在发生的事情,
第一个屏幕截图显示了它的渲染时间,我将收到的数据打印到控制台。我硬编码了一个 5 元素数组,以便在此示例中重复返回。然后当我向上滑动时,组件正在重新渲染,虽然我不需要获取更多数据,但这是因为 useQuery 钩子被触发了。这只会继续获取更多数据,因为组件会不断地重新渲染。
另外 - 我需要将我的数据存储在 state 中,因为我将添加到它,并且我的组件没有其他方法可以记住上次获取的内容。
EDIT2:接受的答案有效。但是我对useQuery 的工作原理有一个误解,我应该在调用查询时在服务器上进行一些日志记录。尽管所有 DOM 更新,这都会调用一次且仅一次的查询。此外,它只将它添加到我的状态一次,因为在第一次成功渲染后,firstRender 状态设置为 false。不确定这是否理想,但如果我想在我的服务器上记录调用,我会这样做。
const Feed = (props) => {
const [feedArray, setFeedArray] = useState([]);
const [firstRender, setFirstRender] = useState(true);
const FEED_QUERY = gql`
query getFeedArray {
getFeedArray
}
`;
const { loading, error, data } = useQuery(FEED_QUERY);
if (loading) return <Loading/>;
if (error) return `Error! ${error}`;
if (firstRender) {
feedArray.push(...data.getFeedArray);
setFirstRender(false);
}
return (
<RenderredComponent />
);
}
export default withApollo(withRouter(Feed));
【问题讨论】:
-
挂钩规则(无条件挂钩)已损坏,您可以使用
skip选项阻止进一步调用...但无需将查询中的数据复制到状态 -
可能是这个组件之外的其他问题,不仅重新渲染(什么原因?)而且还创建和删除了? (在滑动组件中没有
key渲染?)......即使fetchMore也不需要状态数据,请参阅阿波罗分页文档 -
我会说swipeable不是为了获取更多[用例],搜索其他虚拟列表渲染工具(内部渲染)[支持滑动]
-
@xadm 我想使用 framer-motion 进行页面滑动,但是由于滑动时的状态更改而出现冻结(不是 framer 运动的原生,而是因为我需要进行一些额外的状态更改)。见codesandbox.io/s/framer-motion-pages-shmd9。 Swipeable Views 对我来说效果更好,我将使用
onTransitionEnd函数触发更多查询。 -
另外为了更清晰 - 分页可能无法正常工作,因为我是随机获取数据,所以它不是按顺序排列的。
标签: javascript reactjs graphql apollo