调用useEffect挂钩时useQuery数据未定义?

Posted

技术标签:

【中文标题】调用useEffect挂钩时useQuery数据未定义?【英文标题】:useQuery data undefined when calling useEffect hook? 【发布时间】:2020-05-17 09:36:42 【问题描述】:

我在这里有点困惑。根据this SO question 和this question,我应该能够在调用useQuery 钩子以填充状态后调用useEffect 钩子,我正在通过useReducer 执行此操作。例如,像这样(假设 foobarbaz 是对 GraphQL 服务器的单个请求中的命名查询):

const MyComponent = ( props ) => 

  const [ state, dispatch ] = useReducer( reducer, initialState )

  const query = someCondition ? query1 : query2
  const variables =  variables: someCondition ? query1Vars : query2Vars 

  const  loading, error, data  = useQuery( query, variables )

  useEffect(function() 
    dispatch(
      type: types.INIT,
      payload: 
        foo: data.foo,
        bar: data.bar,
        baz: data.baz
      
    )
  , [data])

  return (
    <div>
    
      (() => 
         if ( loading ) return <Loading />
         if ( error ) return <Error />
         return (
           // components that depend on data
         )
      )()
    
    </div>
  )

由于我无法确定的原因,当我在 useEffect 中引用它时,dataundefined。我检查了我的网络响应,并且对我的 GraphQL 端点的调用确实在返回数据。 (基本上,this other SO question about useQuery 中描述的内容。)

除了条件查询/变量赋值之外,此代码用于我们的其他一些组件并且工作正常,所以我不知道为什么它在我的情况下不起作用。

我不确定这里发生了什么,因为我已经确保没有违反钩子规则的有条件地调用任何钩子;我所做的只是有条件地分配变量值之前通过 Apollo 客户端发送查询。 (我还尝试了useQuery 选项的onCompleted 属性,没有useEffect 钩子,但无济于事;data 仍然是undefined。)

【问题讨论】:

这对我来说似乎是预期的行为? useEffect 将始终在 useQuery 完成之前触发,我期望useEffect 会触发两次,一次是在组件挂载时,第二次是在useQuery 返回结果时。在我看来,当 data 尚未设置时,您没有考虑初始 useEffect 调用。 嗯,添加对if ( data ) ... 的检查似乎并不能解释useEffect 钩子的初始触发。 github.com/***owski/react-apollo-hooks/issues/158 似乎建议您根本不需要使用 useEffect,但我之前已经看到该代码有效。初始渲染应该如何处理? 正确,你没有,因为useQuery 已经为你存储了状态 - 但是我不知道你的用例,只在你想要进入的地方关闭你提供的代码某种形式的减速器。 data 正如@James 指出的那样,最初将是未定义的。如果遇到网络错误,它也可以无限期地未定义。也就是说,几乎没有理由这样做——useQuery 已经为您管理状态。如果您需要进一步转换此状态,则只需将其作为组件内的常规变量即可。 感谢@DanielRearden,正如我在下面的评论中提到的,我用我的子组件更改了一些渲染,即使状态不支持它们,这些组件显然也在渲染。我确实为useQuery 使用了onCompleted 回调,但是我看到了很多useQuery + useEffect 的例子,我确信它应该像描述的那样工作,或者至少不会有问题。 :) 【参考方案1】:

除非我没有完全理解您的问题,否则这看起来像是预期的行为。

useEffect 将在挂载时触发,然后任何时候data 更改,data 只会在 API 调用完成后更改。初始的useEffect 调用将在useQuery 得到服务器响应之前发生,因此您不能在useEffect 内部假设data 将被设置。

如果您只想在data 存在时调度,那么您应该保护呼叫,例如

useEffect(() => 
  if (data) 
    dispatch(
      type: types.INIT,
      payload: 
        foo: data.foo,
        bar: data.bar,
        baz: data.baz
      
    );
  
, [data]);

【讨论】:

是的,我已经尝试过检查,但 data 似乎仍然未定义(实际错误只是显示在下游稍远一点)。 @diekunstderfuge useEffect 触发了多少次?是预期的两倍吗?这表明问题在于useQuery 而不是useEffect tbh 看起来useEffect 只触发一次,根据文档,只有在将空数组作为第二个参数传递时才会发生这种情况。 我回到useQueryonCompleted 选项并在那里进行了我的dispatch 调用,确保检查我需要的数据。根据 Apollo 的说法,onCompleted 回调应该只在查询成功 完成时执行(我定义了一个onError 回调以防万一)。现在我已经更改了渲染子组件的一些标准,它可以工作了。我会将此答案标记为已接受,如果有时间,请查看它是否适用于我对渲染所做的修改。谢谢! @diekunstderfuge “根据文档,只有在传递空数组时才会发生” - 这不是 only 的场景发生,useEffect 的第二个参数是触发器依赖项列表,换句话说,如果数组中的任何项目在任何时候发生更改,那么 useEffect 将重新触发 - 空数组只是确保useEffect 只会触发一次(空数组永远不会改变)。在这个例子中,我们看到useQuery 似乎没有强制改变状态,因此就useEffect 而言,data 没有改变。

以上是关于调用useEffect挂钩时useQuery数据未定义?的主要内容,如果未能解决你的问题,请参考以下文章

在使用 graphql 的 useQuery 获取的数据更新 useEffect 钩子内的状态变量时防止无限渲染

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

在 apollo graphql 的单个 useEffect 挂钩中使用多个查询

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

在 useEffect 挂钩中进行的异步调用的测试结果

移动设备上的 React useEffect 挂钩未清除 setTimeout