在@apollo/react-hooks 的 useQuery 钩子中忽略了跳过参数

Posted

技术标签:

【中文标题】在@apollo/react-hooks 的 useQuery 钩子中忽略了跳过参数【英文标题】:Skip argument is being ignored in useQuery hook from @apollo/react-hooks 【发布时间】:2020-09-06 19:08:00 【问题描述】:

问题:

您好,我在 ReactJS 应用程序上使用 apollo-client 已经有一段时间了。我刚刚注意到,有时当我使用useQuery 钩子时,执行完全忽略了skip 参数,并继续使用onCompleted(尽管没有任何数据)。有趣的是,它也不会向我的端点发出 API 请求。但是,如果我只是将 skip 设置为 false,那么一切正常(如预期的那样)。

此外,每次我将useQueryskip 一起使用时,这不会发生,它似乎适用于某些而不是其他。

为什么 apollo 会忽略 skip 参数而只使用空数据立即执行 onCompleted?是否有修复(useLazyQuery 除外)?

代码示例:

const userQuery = useQuery(GET_USER, 
    variables: 
      value: userId,
    ,
    skip: true,
    onCompleted: props => 
      console.log(`props => `, props)
      const 
        user:  type, role, firstName, lastName, permissions ,
       = props;

      setValue('firstName', firstName);
      setValue('lastName', lastName);
      setValue(
        'type',
        userTypes.find(( value ) => value === type),
      );
      setValue(
        'role',
        userRoles.find(( value ) => value === role),
      );
    ,
  );

补充说明:

• 从 onCompleted 函数记录道具的输出是props => undefined

• 我添加skip: true 只是为了证明它实际上不起作用。

• 如果我自己记录userQuery,那么在第一个日志中,userQuery.called 立即等于 true(但就像我之前所说,实际上没有执行任何 API 调用)

依赖关系

"@apollo/react-hooks": "^3.1.5",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.10",
"apollo-datasource-rest": "^0.6.6",
"apollo-link": "^1.2.13",
"apollo-link-context": "^1.0.19",
"apollo-link-error": "^1.1.12",
"apollo-link-rest": "^0.7.3",
"apollo-link-retry": "^2.2.15",
"apollo-link-token-refresh": "^0.2.7",
"apollo-upload-client": "^11.0.0",
"react": "16.10.2",
"react-apollo": "^3.1.3",

注意:

useLazyQuery 似乎工作正常,因此作为一种解决方法,您可以将其与useEffect 结合使用以获得类似的结果。

【问题讨论】:

我们也遇到过这个问题,其他人也遇到过:github.com/apollographql/apollo-client/issues/6190 【参考方案1】:

Apollo 客户端有问题。跳过中的错误是一个烦人的错误。你可以放弃 GraphQL,或者解决它们。我建议放弃它,但如果你不能在这里解决它。该模块的工作方式与 useQuery 类似,并且可以跳过工作。它不完整的完整规范,但应该可以帮助您入门。

import  useEffect, useState  from 'react';
import  QueryHookOptions, useApolloClient  from '@apollo/react-hooks';
import  ApolloQueryResult, NetworkStatus, QueryOptions, ApolloError  from 'apollo-client';

interface DocumentNode 
    loc: 
        source: 
            body: string;
        ;
    ;


interface QueryResult<T> extends ApolloQueryResult<T> 
    error?: Error | ApolloError;
    refetch?: () => void;


/**
 * Method: useQuery
 * Description: enable skip on useQuery. There is a bug that currently ignores it. The bug is confirmed in the 3.2 release as well.
 * Note: https://github.com/apollographql/react-apollo/issues/3492
 */
export function useQuery<T>(
    query: DocumentNode,
    options?: QueryHookOptions
): QueryResult<T | null> 
    // Note: using useApolloClient because useLazyQuery fires on initialization. If we are skipping, we don't even want the first fire.
    const apolloClient = useApolloClient();
    const [result, setQueryResult] = useState<QueryResult<T | null>>(
    // Note: initial state
        data: null,
        loading: false,
        stale: false,
        networkStatus: NetworkStatus.ready
    );

    const setResult = (res: QueryResult<T | null>) => 
    // Note: GraphQL and Apollo can't decide if they want to return errors, or error in the type contract. I want to be sure the contract is stable. If there are errors, there will be error.
    if (res.errors?.length && !res.error) 
        [res.error] = res.errors;
    
    // Note: Functions should always exist even if the query is not complete.
    if (!res.refetch) 
        res.refetch = () => 
            if (!result.loading) 
                execQuery();
            
        ;
      
      setQueryResult(res);
  ;

  const execQuery = async () => 
      // Note: loading state. Not spreading "...result" to allow errors to be reset.
      setResult(
          data: options?.fetchPolicy === 'no-cache' ? null : result.data,
          loading: true,
          networkStatus: NetworkStatus.loading,
          stale: true
      );
      try 
          const queryOptions = (
              ...options,
              query
           as unknown) as QueryOptions;
          
          await apolloClient
              .query(queryOptions)
              .then(result => 
                  setResult(result);
                  if(options?.onCompleted) options.onCompleted(result.data);
              )
              .catch(err => 
                  setResult(err);
                  if(options?.onError) options.onError(err);
              );
          
       catch (err) 
          // Note: fail state
          setResult(
              ...result,
              loading: false,
              errors: [err],
              error: err,
              stale: true,
              networkStatus: NetworkStatus.error
          );
      
  ;

  useEffect(() => 
      // Note: Skip Functionality
      if (!result.loading && options?.skip !== true) 
           execQuery();
      
  // Note only listen too explicit variables. If you listen to whole objects they are always different and will cause endless loops.
  , [options?.skip, query.loc.source.body]);

  return result;

【讨论】:

以上是关于在@apollo/react-hooks 的 useQuery 钩子中忽略了跳过参数的主要内容,如果未能解决你的问题,请参考以下文章

@apollo/react-hooks 中的 `useSubscription` 方法加载卡住

使用@apollo/react-hooks,react 应用程序在刷新过期时严重崩溃

使用 Jest 模拟 @apollo/react-hooks useQuery

有没有办法使用 @apollo/react-hooks 访问多个 graphql 突变的选项

如何在 Apollo Client React 中获取多个条件查询?

apollo usequery 导致未定义的错误