GraphQL 默认数据导致 useEffect 中的无限循环
Posted
技术标签:
【中文标题】GraphQL 默认数据导致 useEffect 中的无限循环【英文标题】:GraphQL defaulting data causes infinite loops in useEffect 【发布时间】:2021-07-17 15:38:11 【问题描述】:所以我有这个代码
const [localState, setLocalState] = useState<StateType[]>([]);
const data = attribute: [] , loading = useQuery<DataType>(QUERY,
variables:
id: client && client.id
,
skip: user.clients && user.clients.length === 0
);
useEffect(() =>
if (loading || !data)
return undefined;
if (data && data.attribute)
const sortedResult = data.attribute.sort((a, b) =>
a.updatedAt < b.updatedAt ? 1 : -1
);
setLocalState(sortedResult);
, [data]);
问题是当 useQuery 返回 empty(undefined) 结果并且数据默认为 attribute: []
useEffect 会一直被触发,但是当 useQuery 返回数据时(所以它不是默认的)useEffects 只被输入一次。这个问题的解决方案只是删除查询中的默认参数= attribute: []
,所以它看起来像这样:
const [localState, setLocalState] = useState<StateType[]>([]);
const data, loading = useQuery<DataType>(QUERY,
variables:
id: client && client.id
,
skip: user.clients && user.clients.length === 0
);
useEffect(() =>
if (loading || !data)
return undefined;
if (data && data.attribute)
const sortedResult = data.attribute.sort((a, b) =>
a.updatedAt < b.updatedAt ? 1 : -1
);
setLocalState(sortedResult);
, [data]);
为什么 useQuery 中的默认参数会使 useEffect 被无限触发?
(要补充的重要说明 - 我试图删除排序功能,认为它会改变数据对象并导致重新输入,但它没有改变任何东西)
【问题讨论】:
【参考方案1】:
const data = attribute: [] , loading = useQuery<DataType>(QUERY,
这个声明没有更大的意义:
不需要,因为所有渲染[和计算]都应该(并且已经)被data
首先检查阻止;
更深的嵌套值怎么办?下一个空声明?它会导致死胡同;
它可以在每次渲染时创建一个新的data
对象......但它不应该导致无限重新渲染......还有其他原因吗?
您可以通过以下方式打破效果循环:
const [localState, setLocalState] = useState(null);
useEffect(() =>
if (data && data.attribute && !localState)
你也可以在钩子中使用onCompleted
事件:
const loading = useQuery<DataType>(QUERY,
variables:
id: client && client.id
,
skip: user.clients && user.clients.length === 0
onCompleted: (data) =>
if(data && data.attribute)
const sortedResult = data.attribute.sort((a, b) =>
a.updatedAt < b.updatedAt ? 1 : -1
);
setLocalState(sortedResult);
);
...检查/使用loading
和localState && <SomeResultView data=localState />
【讨论】:
这比我的解决方案要好得多,谢谢。但是,我是否可以在 useQuery 的扩展运算符中使用像= attribute: []
这样的默认参数或者这是不良行为的问题仍然困扰着我。只是想知道以备将来使用
你应该避免这种声明... YAGNI【参考方案2】:
您的 data
在每次渲染时都是一个新对象,因此重新触发您的 useEffect
重新触发渲染等等。
我认为您在这里不需要useEffect
:
const data, loading = useQuery<
DataType
>(QUERY,
variables:
id: (client && client.id)
,
skip: user.clients && user.clients.length === 0
);
const localState = (!loading && data && data.attribute)
? undefined
: data.attribute.sort(
(a, b) => (a.updatedAt < b.updatedAt ? 1 : -1)
);
应该够了
【讨论】:
但是如果我不使用useEffect
localState 在 useQuery 的承诺返回成功后将不会更新。根据我的理解,也不会重新渲染没有状态 UI。
@valentinIvanov 您已经在重新渲染(data
更改后),不需要额外的重新渲染(状态更改后)
我不认为它在数据更改后重新渲染,来自graphql文档(apollographql.com/docs/react/data/queries)notifyOnNetworkStatusChange
默认设置为false,我没有在代码中的任何地方将其设置为true .
notifyOnNetworkStatusChange
不同,您尝试过代码吗? useQuery
将重新渲染,否则您将没有循环。以上是关于GraphQL 默认数据导致 useEffect 中的无限循环的主要内容,如果未能解决你的问题,请参考以下文章
在 apollo graphql 的单个 useEffect 挂钩中使用多个查询
如何在 useEffect 挂钩中形成 setFieldValue