javascript - 在渲染组件之前只调用一次 Hook (Meteor/React/Apollo/Graphql)
问题描述
我正在尝试创建一个在您向下滑动时更新的内容提要。我的逻辑是我需要在渲染提要之前获取所需的数据,并且每次在向下滑动页面时点击第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
钩子被触发了。这只会继续获取更多数据,因为组件将不断被重新渲染。
另外 - 我需要将我的数据存储在状态中,因为我将添加到它,并且我的组件没有其他方法可以记住上次获取的内容。
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));
解决方案
你不需要使用状态。只需使用查询数据
const Feed = props => {
const FEED_QUERY = gql`
query getFeedArray {
getFeedArray
}
`;
const { loading, error, data } = useQuery(FEED_QUERY);
if (loading) return <Loading />;
if (error) return `Error! ${error}`;
return <RenderredComponent data={data.getFeedArray} />;
};
export default withApollo(withRouter(Feed));
您可以data
在任何地方使用。
更新
如果您出于某种原因想使用状态,可以将其与useEffect
. 添加data.getFeedArray
到useEffect
依赖项,以便在更改时可以设置feedArray
状态。
const Feed = props => {
const [feedArray, setFeedArray] = useState([]);
const FEED_QUERY = gql`
query getFeedArray {
getFeedArray
}
`;
const { loading, error, data } = useQuery(FEED_QUERY);
useEffect(() => {
setFeedArray([...data.getFeedArray])
}, [data.getFeedArray])
if (loading) return <Loading />;
if (error) return `Error! ${error}`;
return <RenderredComponent data={feedArray} />;
};
export default withApollo(withRouter(Feed));