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

本文是小编为大家收集整理的关于useQuery返回未定义的数据,但在gql操场上返回数据的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

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

Apollo客户端在React App上返回Undefined,但在GQL Playground上返回数据,我不明白它为什么在客户端上不起作用,而是在GraphQL Playground上工作.

模式

我已将Union定义为用于错误处理的用户查询.

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操场上查询时

在GraphQl操场上,查询作品.

在客户端

 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运行但返回Unefiend.

推荐答案

请,随心所欲,因为这个答案很长.

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

这是有关如何解决的详细逐步指南:

1-验证控制台警告.

验证您的控制台中是否有警告(这是我发生的示例).这些是与默认的启发式片段匹配器相撞的字段:

带警告的Firefox Dev控制台

阅读阿波罗文档,我发现以下内容:

默认情况下,阿波罗客户端的缓存将使用启发式片段 匹配器,假设片段匹配,则如果包括结果 选择集中的所有字段,并且在任何字段时都不匹配 失踪.在大多数情况下,这也有效,但这也意味着阿波罗 客户无法为您检查服务器响应,也无法分辨 当您手动将无效数据写入商店时 更新,更新,写作等.另外,启发式片段 使用工会片段或 接口.阿波罗客户端会让您通过控制台知道这一点 警告(在开发中),如果它试图使用默认的启发式 带有工会/接口的片段匹配器.这 IntrompectionFragmentMatcher是与之合作的解决方案 工会/接口,并在下面更详细地说明.

v2的更多信息此处: https:https://.apolographql.com/docs/react/v2.6/data/fragments/#fragments-on-inions-interfaces

v3的更多信息此处: .com/doc/react/data/fragments/#使用f-ragments-with-inions-int-interfaces

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

您有3个选项.可以手动或自动进行(远程或本地).

2-生成内省文件

选择以下选项中的一个(最终都是相同的).在选择一个之前,请先阅读所有内容.

2.1-选项A-手动生成文件.

使用以下模式将其适应您自己的. 注意,以下是打字稿代码.如果您使用的是普通JS.

,请删除type

请查看我在.gql文件中有以下方式的联合类型:

   # 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.

2.2-选项B-自动远程方法.

这是您在远程服务器中有架构,并且想获取它.这是直接从阿波罗文档提取的脚本.对于自动方法,您可以按照Apollo文档中的规定直接获取模式:

// 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.

2.3-选项C-自动本地方法

上面的选择对我来说很困难,因为我在身后墙后面有架构.幸运的是,我确实可以直接访问.GQL文件,并能够生成内省文件.阅读:

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

转到您的后端代码,或者您的GraphQl.gql文件所在的任何地方,然后做:

  1. 安装 GraphQl Code Generator :
yarn add graphql

yarn add -D @graphql-codegen/cli
  1. 运行初始化向导:
yarn graphql-codegen init
  1. 填写细节(适应您自己的) 就我而言,我选择了:
  • 后端 - API或服务器,用React构建的应用程序
  • 您的模式在哪里? ./appsync/appsync.gql
  • 选择插件:片段匹配器(也可以随意选择其他插件...这是重要的插件!)
  • 文档:./appsync/generated/introspection.ts(这是您希望输出文件的地方)

这将生成一个codegen.yml,其中包括插件和运行graphql-code-generator的配置.

这是我的:

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
  ./graphql.schema.json:
    plugins:
      - "introspection"

v3:

overwrite: true
schema: "./appsync/appSync.gql"
generates:
  ./appsync/generated/introspection.ts:
    plugins:
      # - "typescript"
      # - "typescript-operations"
      # - "typescript-resolvers"
      # - "typescript-react-apollo"
      - "introspection"

我已经评论了对我们任务并不重要的零件.

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

yarn install

因为向导向我们的package.json添加软件包.

然后,生成代码:

yarn generate

这将输出intropsection.ts文件,该文件需要在阿波罗(Apollo)继续进行.

3-将内省文件注入apolloclient

现在,在您的前端代码中,将introspection.ts文件复制到您的回购中(如果尚未在那里),并包括:

注意:我已将文件重命名为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,
});

之后,您的控制台警告应消失,疑问和突变应像正常状态.

其他推荐答案

可能会有所帮助的东西,您知道您在哪里调用{data} 您也可以查找错误和console.log('错误:',错误)

检查Apollo客户端查询文档

类似的东西,看看错误消息,它应该有所帮助!

import { useQuery } from '@apollo/react-hooks';
import gql from 'graphql-tag';

const GET_GREETING = gql`
  query getGreeting($language: String!) {
    greeting(language: $language) {
      message
    }
  }
`;

function Hello() {
    const { loading, error, data } = useQuery(GET_GREETING, {
    variables: { language: 'english' },
  });
  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;
  return <h1>Hello {data.greeting.message}!</h1>;
}

其他推荐答案

这可能与您的情况无关,但我的命名问题.

  const { isLoading, error, blogs } = useQuery(BLOGS);

钩子的恢复对象是data不是blogs.

本文地址:https://www.itbaoku.cn/post/1938146.html

问题描述

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

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.

Schema

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
}

Query resolver

 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)
      }
    }
  }

When querying on gql playground

on graphql playground, query works.

Screenshot from 2020-01-25 23-44-01_censored

On the client-side

 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 runs but returns undefiend.

推荐答案

Please, bear with me as this answer is long.

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:

1 - Verify Console Warnings.

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:

Firefox Dev Console with warnings

Reading the Apollo docs, I found out the following:

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.

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

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

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.

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

2 - Generate the introspection file

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

2.1 - Option A - Generating the file manually.

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.

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;
    

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

2.2 - Option B - Automatic Remote Approach.

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!');
      }
    });
  });

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

2.3 - Option C - Automatic Local Approach

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:

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

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

  1. Install GraphQL Code Generator:
yarn add graphql

yarn add -D @graphql-codegen/cli
  1. Run the initialization wizard:
yarn graphql-codegen init
  1. Fill in the details (adapt to your own) In my case, I selected:
  • Backend - API or server, Application built with React
  • Where is your schema? ./appsync/appSync.gql
  • Pick plugins: Fragment Matcher (Feel free to select other plugins as well... this is the important one!)
  • document: ./appsync/generated/introspection.ts (This is where you want the file to be outputted)

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

This is mine:

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
  ./graphql.schema.json:
    plugins:
      - "introspection"

For V3:

overwrite: true
schema: "./appsync/appSync.gql"
generates:
  ./appsync/generated/introspection.ts:
    plugins:
      # - "typescript"
      # - "typescript-operations"
      # - "typescript-resolvers"
      # - "typescript-react-apollo"
      - "introspection"

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

Then (very important!) Run:

yarn install

Because the wizard adds packages to our package.json.

Then, generate the code:

yarn generate

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

3 - Inject the introspection file to the ApolloClient

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

Note: I've renamed my file to fragmentTypes.ts and included it inside the apollo folder:

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.

其他推荐答案

something that might help, you know where you call {data} you can also look for error and console.log('Error:',error)

check the apollo client query docs

something like this , and look at the error message, it should help !

import { useQuery } from '@apollo/react-hooks';
import gql from 'graphql-tag';

const GET_GREETING = gql`
  query getGreeting($language: String!) {
    greeting(language: $language) {
      message
    }
  }
`;

function Hello() {
    const { loading, error, data } = useQuery(GET_GREETING, {
    variables: { language: 'english' },
  });
  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;
  return <h1>Hello {data.greeting.message}!</h1>;
}

其他推荐答案

This might be irrelevant to your case, but mine was a naming problem.

  const { isLoading, error, blogs } = useQuery(BLOGS);

the retured object from the hook was data not blogs.