【问题标题】:GraphQL defaulting data causes infinite loops in useEffectGraphQL 默认数据导致 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 被无限触发?

(要补充的重要说明 - 我试图删除排序功能,认为它会改变数据对象并导致重新输入,但它没有改变任何东西)

【问题讨论】:

    标签: javascript reactjs typescript graphql react-hooks


    【解决方案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);
        }
      }
    });
    

    ...检查/使用loading{localState &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/queriesnotifyOnNetworkStatusChange默认设置为false,我没有在代码中的任何地方将其设置为true .
    • notifyOnNetworkStatusChange 不同,您尝试过代码吗? useQuery 将重新渲染,否则您将没有循环。
    猜你喜欢
    • 2020-08-27
    • 2020-11-16
    • 2021-11-01
    • 1970-01-01
    • 2021-09-27
    相关资源
    最近更新 更多