在呈现组件之前仅调用一次 Hook (Meteor/React/Apollo/Graphql)
Calling a Hook only once before the component is rendered (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));
我正在尝试创建在您向下滑动时更新的内容提要。我的逻辑是我需要在呈现提要之前获取所需的数据,并且每次我向下滑动页面时点击第 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));