首页 > 解决方案 > 在渲染组件之前只调用一次 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在渲染组件之前,我怎样才能只运行一次钩子?

任何帮助表示赞赏,谢谢!

编辑:下面的截图来说明正在发生的事情,

图一 图二 图 3

第一个屏幕截图显示了它何时呈现,我将收到的数据打印到控制台。我硬编码了一个 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));

标签: javascriptreactjsgraphqlapollo

解决方案


你不需要使用状态。只需使用查询数据

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.getFeedArrayuseEffect依赖项,以便在更改时可以设置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));

推荐阅读