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&lt;DataType&gt;(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);
    
  
);

...检查/使用loadinglocalState &amp;&amp; &lt;SomeResultView data=localState /&gt;

【讨论】:

这比我的解决方案要好得多,谢谢。但是,我是否可以在 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 挂钩中使用多个查询

数据更改后停止执行函数(React,useEffect)

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

如何在 useEffect 挂钩中形成 setFieldValue

React Router 的 useParams 导致 useEffect 重新运行

用于反应钩子的 useEffect 的替代品