useQuery 返回未定义,但返回 gql 操场上的数据 [英] useQuery returns undefined, But returns data on gql playground

查看:23
本文介绍了useQuery 返回未定义,但返回 gql 操场上的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

"@apollo/react-hooks": "^3.1.3",
"apollo-client": "^2.6.8",

Apollo 客户端在 react 应用程序上返回 undefined 但返回 gql playground 上的数据,我不明白为什么它不能在客户端运行,但可以在 graphql playground 上运行.

Apollo client return undefined on react app but return the data on gql playground, I don't understand why don't it works on client-side but works on graphql playground.

架构

我为用户查询定义了联合以进行错误处理.

I have defined union for user query for error handling.

type Query {
  user(id: ID!): UserReturn!
}

union UserReturn = User | Error

type User {
  id: ID!
  username: String!
  email: String
  profileUrl: String
  createdAt: Date
  ads: [Doc!]!
}


type Error {
  message: String
  code: ID
}

查询解析器

 async user(_, { id }, { User }) {
    console.log('query - User')
    try {
      await delay(1000 * 3)
      const user = await User.findById(id).populate('userData')
      console.log(user)
      if (!user) return {
        __typename: 'Error',
        message: 'User not found.',
        code: id
      }

      const { _id: id, username, email, createdAt, userData: { profileUrl } } = user

      console.log(username)
      return {
        __typename: 'User',
        id,
        username,
        email,
        createdAt,
        profileUrl
      }
    } catch (err) {
      console.log(err)
      return {
        __typename: 'Error',
        message: 'Something went wrong while getting user.',
        code: LogBack(err, `query/user?id=${id}`, __filename)
      }
    }
  }

在 gql playground 上查询时

在 graphql 操场上,查询有效.

on graphql playground, query works.

在客户端

 const { data } = useQuery(
    gql`query user($id: ID!) {
      user(id: $id) {
        __typename
        ... on User {
          id
          username
          email
          profileUrl
          createdAt
          # ads
        }
        ... on Error {
          message
          code
        }
      }
    }
    `,
    {
      variables: {
        id: userId
      }
    }
  );

  console.log(data) // undefined

useQuery 运行但返回 undefiend.

useQuery runs but returns undefiend.

推荐答案

请多多包涵,这个答案很长.

我也遇到了这个问题.似乎在使用片段(在这种情况下,内联)和接口时会出现问题.我设法通过将正确的内省数据传递给 Apollo 的启发式片段匹配器来解决它(参见步骤 3).

I ran into this issue as well. It seems the problem happens when using fragments (in this case, inline) and interfaces. I managed to solve it by passing the correct introspection data to Apollo's Heuristic Fragment Matcher (See Step 3).

以下是有关如何解决此问题的详细分步指南:

Here's a detailed step by step guide on how to solve it:

验证您的控制台中是否有警告(这是发生在我身上的一个例子).这些是与默认启发式片段匹配器冲突的字段:

Verify that there are warnings in your console (Here's an example that happened to me). These are the fields colliding with the default heuristic fragment matcher:

阅读 Apollo 文档,我发现了以下内容:

Reading the Apollo docs, I found out the following:

默认情况下,Apollo Client 的缓存会使用启发式片段匹配器,它假设一个片段匹配,如果结果包括其选择集中的所有字段,并且当任何字段不匹配时失踪.这在大多数情况下都有效,但这也意味着 Apollo客户端无法为您检查服务器响应,也无法告诉当您使用手动将无效数据写入商店时update、updateQuery、writeQuery 等.还有启发式片段使用带有联合或的片段时,匹配器将无法准确工作接口.Apollo Client 将通过控制台让您知道这一点警告(开发中),如果它尝试使用默认启发式带有联合/接口的片段匹配器.这IntrospectionFragmentMatcher 是用于处理的解决方案联合/接口,下面有更详细的解释.

By default, Apollo Client's cache will use a heuristic fragment matcher, which assumes that a fragment matched if the result included all the fields in its selection set, and didn't match when any field was missing. This works in most cases, but it also means that Apollo Client cannot check the server response for you, and it cannot tell you when you're manually writing invalid data into the store using update, updateQuery, writeQuery, etc. Also, the heuristic fragment matcher will not work accurately when using fragments with unions or interfaces. Apollo Client will let you know this with a console warning (in development), if it attempts to use the default heuristic fragment matcher with unions/interfaces. The IntrospectionFragmentMatcher is the solution for working with unions/interfaces, and is explained in more detail below.

这里有更多关于 v2 的信息:https://www.apollographql.com/docs/react/v2.6/data/fragments/#fragments-on-unions-and-interfaces

More info for v2 here: https://www.apollographql.com/docs/react/v2.6/data/fragments/#fragments-on-unions-and-interfaces

此处有更多关于 v3 的信息:https://www.apollographql.com/docs/react/data/fragments/#using-fragments-with-unions-and-interfaces

More info for v3 here: https://www.apollographql.com/docs/react/data/fragments/#using-fragments-with-unions-and-interfaces

要解决此问题,我们需要将 IntrospectionResultData 传递给 Apollo 客户端(请参阅步骤 3).但在此之前,我们需要生成文件或数据.

To fix this issue, we need to pass the IntrospectionResultData to the Apollo Client (See Step 3). But before that, we need to generate the file or data.

您有 3 个选项.手动或自动(远程或本地)执行此操作.

You have 3 options. Either do it manually or automatically (remote or local).

从以下选项中选择一个(所有选项最终都相同).在选择一个之前阅读所有这些.

Pick one of the options below (all of them end up being the same). Read all of them before choosing one.

使用以下架构使其适应您自己的架构.注意,以下是 TypeScript 代码.如果您使用纯 JS,请删除 type.

Use the following schema to adapt it to your own. Note, the following is TypeScript code. Remove the type if you're using plain JS.

请注意,在我的情况下,我的 .gql 文件中有以下方式的联合类型:

Please see that in my case I had in my .gql file a union type of the following fashion:

   # GraphQL code omitted. 
  union PlanningResult = Planning | PlanningTechnical

// For Apollo V 2.x
export interface IntrospectionResultData {
  __schema: {
    types: {
      kind: string;
      name: string;
      possibleTypes: {
        name: string;
      }[];
    }[];
  };
}

const result: IntrospectionResultData = {
  __schema: {
    types: [
      {
        kind: 'UNION',
        name: 'PlanningResult',
        possibleTypes: [
          {
            name: 'Planning',
          },
          {
            name: 'PlanningTechnical',
          },
        ],
      },
    ],
  },
};
export default result;

// For Apollo V3:

      export interface PossibleTypesResultData {
        possibleTypes: {
          [key: string]: string[]
        }
      }
      const result: PossibleTypesResultData = {
  "possibleTypes": {
    "PlanningResult": [
      "Planning",
      "PlanningTechnical"
    ]
  }
};
      export default result;
    

完成此操作后,继续执行第 3 步.

Once you've done this, proceed to step 3.

这是如果您在远程服务器中拥有架构并且想要获取它.这是直接从 Apollo Docs 中提取的脚本.对于自动方法,您可以按照 Apollo Docs 中的说明直接获取架构:

This is if you have your schema in a remote server and you'd like to fetch it. This is a script extracted directly from the Apollo Docs. For the automatic approach, you can fetch the schema directly as stated in the Apollo Docs:

// This is for V2 only, for V3 use the link down below (They're not the same!).

// For V2: https://www.apollographql.com/docs/react/v2.6/data/fragments/#fragments-on-unions-and-interfaces
// For V3 please, go to https://www.apollographql.com/docs/react/data/fragments/#generating-possibletypes-automatically

const fetch = require('node-fetch');
const fs = require('fs');

fetch(`${YOUR_API_HOST}/graphql`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    variables: {},
    query: `
      {
        __schema {
          types {
            kind
            name
            possibleTypes {
              name
            }
          }
        }
      }
    `,
  }),
})
  .then(result => result.json())
  .then(result => {
    // here we're filtering out any type information unrelated to unions or interfaces
    const filteredData = result.data.__schema.types.filter(
      type => type.possibleTypes !== null,
    );
    result.data.__schema.types = filteredData;
    fs.writeFile('./fragmentTypes.json', JSON.stringify(result.data), err => {
      if (err) {
        console.error('Error writing fragmentTypes file', err);
      } else {
        console.log('Fragment types successfully extracted!');
      }
    });
  });

这将生成一个带有 __schema 和适当类型的 json 文件.完成此操作后,请继续执行第 3 步.

This will generate a json file with the __schema and appropriate types. Once you've done this, proceed to step 3.

上述选项对我来说很困难,因为我的架构位于身份验证墙后面.幸运的是,我确实可以直接在本地访问 .gql 文件,并且能够生成自省文件.继续阅读:

The options above were difficult for me as I had my schema behind an auth wall. Fortunately, I did have direct local access to the .gql file and was able to generate the introspection file. Read on:

我们使用graphql-code-generator为我们生成内省文件.

We use graphql-code-generator to generate the introspection file for us.

转到您的后端代码,或您的 graphql.gql 文件所在的任何位置,然后执行:

Go to your back-end code, or wherever your graphql.gql file lies, and do:

  1. 安装 GraphQL 代码生成器:

yarn add graphql

yarn add -D @graphql-codegen/cli

  1. 运行初始化向导:

yarn graphql-codegen init

  1. 填写详细信息(根据自己的情况)就我而言,我选择了:

  • 后端 - API 或服务器,使用 React 构建的应用程序
  • 您的架构在哪里?./appsync/appSync.gql
  • 选择插件:Fragment Matcher(也可以随意选择其他插件……这是重要的一个!)
  • 文档:./appsync/generated/introspection.ts(这是您希望输出文件的位置)
  • 这将生成一个 codegen.yml,其中包含要运行的插件和 graphql-code-generator 的配置.

    This will generate a codegen.yml that will include the plugins and the configuration for graphql-code-generator to run.

    这是我的:

    overwrite: true
    schema: "./appsync/appSync.gql"
    # documents: "./appsync/**/*.gql"
    generates:
      ./appsync/generated/introspection.ts:
        plugins:
          # - "typescript"
          # - "typescript-operations"
          # - "typescript-resolvers"
          # - "typescript-react-apollo"
          - "fragment-matcher"
        config:
        # NOTE: Remember to specify the CORRECT Apollo Client Version
          apolloClientVersion: 2.6
      ./graphql.schema.json:
        plugins:
          - "introspection"
    
    

    我已经评论了对我们的任务不重要的部分.

    I've commented on the parts that are not critical for our mission.

    然后(非常重要!)运行:

    Then (very important!) Run:

    yarn install
    

    因为向导将包添加到我们的package.json.

    Because the wizard adds packages to our package.json.

    然后,生成代码:

    yarn generate
    

    这将输出需要包含在 Apollo 中才能继续的 introspection.ts 文件.

    This will output the introspection.ts file which needs to be included in Apollo to continue.

    现在,在您的前端代码中,将 introspection.ts 文件复制到您的存储库(如果它还没有在那里),并包含它:

    Now, in your front-end code, copy the introspection.ts file to your repo (if it's not already in there), and include it:

    注意:我已将我的文件重命名为 fragmentTypes.ts 并将其包含在 apollo 文件夹中:

    For V2:
    import ApolloClient from 'apollo-client/ApolloClient';
    import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
    import { InMemoryCache } from 'apollo-cache-inmemory/lib/inMemoryCache';
    // The file we just generated. If it's a .json file 
    // remember to include the .json extension
    import introspectionQueryResultData from './apollo/fragmentTypes';
    
    const fragmentMatcher = new IntrospectionFragmentMatcher({
      introspectionQueryResultData,
    });
    
    export const globalClient = new ApolloClient({
      link,
      cache: new InMemoryCache({ fragmentMatcher }),
    });
    
    

    For V3:
    import { InMemoryCache, ApolloClient } from '@apollo/client';
    // In case you used graphql-code-generator
    // import introspectionQueryResultData from './apollo/fragmentTypes';
    // The file we just generated. If it's a .json file 
    // remember to include the .json extension
    import possibleTypes from './path/to/possibleTypes.json';
    
    const cache = new InMemoryCache({
      possibleTypes,
    });
    const client = new ApolloClient({
      // ...other arguments...
      cache,
    });
    

    在此之后,您的控制台警告应该会消失,查询和更改应该会正常执行.

    After this, your console warnings should go away and the queries and mutations should perform as normal.

    这篇关于useQuery 返回未定义,但返回 gql 操场上的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆