返回上一个屏幕时执行 useQuery() 挂钩反应原生堆栈导航器

Posted

技术标签:

【中文标题】返回上一个屏幕时执行 useQuery() 挂钩反应原生堆栈导航器【英文标题】:Execute useQuery() hook when returning to previous screen react native stack navigator 【发布时间】:2020-06-27 11:59:54 【问题描述】:

我知道 React 中的一个固定规则是,您不能在功能组件的方法内执行任何钩子,甚至不能在其他钩子(如 useEffect 钩子)内执行任何钩子。它必须在组件本身的主体中执行。

所以今天在开发一个测验应用程序时,我遇到了这样一种情况,在通过堆栈导航器上的一个屏幕后,我想通过单击返回按钮再次返回该屏幕。但是这一次,我想使用 Apollo 客户端提供的 useQuery 钩子,使用来自 graphql 查询的数据来修改上一个屏幕上的信息。

我可以使用道具上的isFocused 属性检测上一个屏幕是否是焦点。如果它发生变化,那么我将使用 useQuery 再次获取数据。

我可以这样做:

    React.useEffect(() => 
        //how do I fetch data from here using useQuery if React prohibits me to use useQuery here?
    , [props.isFocused]);

所以这是我的问题,有没有办法解决这个问题?非常感谢您的回复。

【问题讨论】:

【参考方案1】:

在 reactjs 上发现这个问题有类似的问题,我在其中加载了一个带有数据的页面,然后转到另一个页面将文档添加到数据库中,然后在完成后返回原始页面。

将默认 fetchPolicy 更改为 network-only 确保我始终获得最新的可用数据。

const  loading: loadingData, error, data  = useQuery(GetData, 
    fetchPolicy: "network-only"

参考:https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy

【讨论】:

感谢这很有魅力。我阅读了您建议的文档中的页面,并决定使用 fetchPolicy: "cache-and-network",因为它同样有效,而且有时它显然会从缓存中获取数据,这听起来是件好事。【参考方案2】:

正如你所说,你不能在其他钩子或方法中调用钩子。换句话说,你不能有条件地调用钩子。如果您使用 useQuery 钩子,您必须准备好它会在每次重新渲染组件时运行查询(尽管它会缓存结果,因此您不必担心性能)。

相反,您应该只调用组件内部的useQuery 挂钩,然后强制更新焦点组件。它应该已经在焦点上重新渲染,因为大概是 isFocused 属性发生了变化。如果没有,可以参考this。

【讨论】:

【参考方案3】:

useQuery 是一个钩子,默认情况下将执行与组件渲染联系起来。但是,通常希望此行为与其他事件相关联。在您的情况下,它将在屏幕焦点上。您可以使用useLazyQuery hook instead 来控制执行。将此钩子视为useMutation,但用于查询。

来自阿波罗文档:

useLazyQuery 钩子非常适合执行查询以响应组件渲染以外的事件。这个钩子的作用就像useQuery,除了一个关键的例外:当useLazyQuery 被调用时,它不会立即执行其关联的查询。相反,它会在结果元组中返回一个函数,您可以在准备好执行查询时调用该函数。

【讨论】:

【参考方案4】:

此答案假设您使用 react-navigation 在 React Native 应用程序中创建堆栈导航器

Apollo 客户端中的useQuery 钩子在组件挂载时运行查询。在“反应导航”中,当您导航到堆栈中的下一个屏幕时,前一个(当前)屏幕不会卸载。因此,当您返回时,组件已安装,因此 useQuery 不会运行。

为了解决这个问题,我们必须监听反应导航的生命周期事件。最重要的是,focus 事件。当调用 react 导航的焦点事件时,我们可以重新获取查询。

const  loading, data, refetch  = useQuery(QUERY);

React.useEffect(() => 
  const unsubscribe = navigation.addListener('focus', () => 
    // This check is to prevent error on component mount. The refetch function is defined only after the query is run once
    // It also ensures that refetch runs only when you go back and not on component mount
    if (refetch) 
      // This will re-run the query
      refetch();
    
  );

  return unsubscribe;
  , [navigation]);

这是我们确保在我们的 react 原生应用中每次打开屏幕时都会调用查询的方式。

【讨论】:

【参考方案5】:

我也在努力解决这个问题,并通过 useEffect 想出了这个解决方法。所以,

const  loading, data, refetch  = useQuery(Query_Data)


 useEffect(()=> 
      refetch()
  , [refetch]

)

这适用于 history.goBack(),但是,它还会在浏览器刷新时使服务器调用加倍(一个来自 useQuery,一个来自 useEffect)。为了避免这个问题,我添加了一个全局属性“global.refreshOnGoBack”,在调用history.goBack()之前设置为true,然后

  useEffect(()=> 
    if (global.refreshOnGoBack) 
      refetch()
      global.refreshOnGoBack = false
    
  , [refetch])

它似乎工作正常,但我不喜欢它。我在这里发布的更多是建议而不是答案。

【讨论】:

以上是关于返回上一个屏幕时执行 useQuery() 挂钩反应原生堆栈导航器的主要内容,如果未能解决你的问题,请参考以下文章

当来自 Apollo useQuery 挂钩的“数据”返回 useMemo 所依赖的“数据”的新值时,useMemo 未运行

如何在页面加载时设置 useState 挂钩的状态?

TypeError:无法读取未定义的属性“getPosts”-useQuery 挂钩,反应功能组件

无法使用 useQuery 挂钩为具有一些 @client 变量的远程查询获取数据

useContext 在 useQuery 时返回 null

Apollo 客户端没有读取使用 useQuery 钩子传入的变量