调用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
执行此操作。例如,像这样(假设 foo
、bar
和 baz
是对 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
中引用它时,data
是 undefined
。我检查了我的网络响应,并且对我的 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
只触发一次,根据文档,只有在将空数组作为第二个参数传递时才会发生这种情况。
我回到useQuery
的onCompleted
选项并在那里进行了我的dispatch
调用,确保检查我需要的数据。根据 Apollo 的说法,onCompleted
回调应该只在查询成功 完成时执行(我定义了一个onError
回调以防万一)。现在我已经更改了渲染子组件的一些标准,它可以工作了。我会将此答案标记为已接受,如果有时间,请查看它是否适用于我对渲染所做的修改。谢谢!
@diekunstderfuge “根据文档,只有在传递空数组时才会发生” - 这不是 only 的场景发生,useEffect
的第二个参数是触发器依赖项列表,换句话说,如果数组中的任何项目在任何时候发生更改,那么 useEffect
将重新触发 - 空数组只是确保useEffect
只会触发一次(空数组永远不会改变)。在这个例子中,我们看到useQuery
似乎没有强制改变状态,因此就useEffect
而言,data
没有改变。以上是关于调用useEffect挂钩时useQuery数据未定义?的主要内容,如果未能解决你的问题,请参考以下文章
在使用 graphql 的 useQuery 获取的数据更新 useEffect 钩子内的状态变量时防止无限渲染
当来自 Apollo useQuery 挂钩的“数据”返回 useMemo 所依赖的“数据”的新值时,useMemo 未运行
在 apollo graphql 的单个 useEffect 挂钩中使用多个查询