使用 this.props.relay.setVariables 时,Relay/Graphql 查询字段“node”而不是“viewer”

Posted

技术标签:

【中文标题】使用 this.props.relay.setVariables 时,Relay/Graphql 查询字段“node”而不是“viewer”【英文标题】:Relay/Graphql querying field "node" instead of "viewer" when using this.props.relay.setVariables 【发布时间】:2016-10-02 07:48:11 【问题描述】:

我有一个在初始页面加载时获取 2 个项目的表。这正确地返回了 2 行。当我检查请求有效负载时,我看到以下信息:

"query":"query CampaignQuery 
  viewer 
    id,
    ...F0
  

fragment F0 on User 
  _campaigns3iwcB5:campaigns(first:2) 
    edges 
      node 
        id,
        account_id,
        start_time
      ,
      cursor
    ,
    pageInfo 
      hasNextPage,
      hasPreviousPage
    
  ,
  id
","variables":

然后我有一个按钮,它触发一个函数并设置一个变量以通过 this.props.relay.setVariables 返回其他项目。

然后我收到 3 次重试错误和以下错误:

[message: "Cannot query field "node" on type "Query".", locations: [line: 2, column: 3]]

当我检查请求有效负载时,我注意到它正在查询“节点”而不是像以前那样查询“查看器”。

"query":"query Listdata_ViewerRelayQL($id_0:ID!) 
  node(id:$id_0) 
    ...F0
  

fragment F0 on User 
  _campaignsgsWJ4:campaigns(after:\"CkkKFwoEbmFtZRIPGg1DYW1wYWlnbiBGb3VyEipqEXN+cmVhc29uaW5nLXVzLTAxchULEghDYW1wYWlnbhiAgICA3pCBCgwYACAA\",first:2) 
    edges 
      node 
        id,
        account_id,
        start_time
      ,
      cursor
    ,
    pageInfo 
      hasNextPage,
      hasPreviousPage
    
  ,
  id
","variables":"id_0":"VXNlcjo="

这是我的 schema.js 文件

/* @flow */
/* eslint-disable no-unused-consts, no-use-before-define */
import 
  GraphQLBoolean,
  GraphQLFloat,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  // GraphQLEnumType,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString
 from 'graphql';

// const types = require('graphql').type;
// const GraphQLEnumType = types.GraphQLEnumType;

import 
  connectionArgs,
  connectionDefinitions,
  connectionFromArray,
  fromGlobalId,
  globalIdField,
  mutationWithClientMutationId,
  nodeDefinitions
 from 'graphql-relay';

import 
  User,
  Feature,
  getUser,
  getFeature,
  getFeatures,
  getEventStream,

  Campaign,

  getCampaigns,
  resolveCampaigns,
  campaignById,
 from './database';

// Import loader DataLoader
import Loader from './loader';


/**
 * We get the node interface and field from the Relay library.
 *
 * The first method defines the way we resolve an ID to its object.
 * The second defines the way we resolve an object to its GraphQL type.
 */
const  nodeInterface, nodeField  = nodeDefinitions(
  (globalId) => 
    const  type, id  = fromGlobalId(globalId);
    if (type === 'User') 
      return getUser(id);
     else if (type === 'Feature') 
      return getFeature(id);
     else if (type === 'Campaign') 
      return campaignById(id);
     else 
      return null;
    
  ,
  (obj) => 
    if (obj instanceof User) 
      return userType;
     else if (obj instanceof Feature) 
      return featureType;
     else if (obj instanceof Campaign) 
      return campaignType;
     else 
      return null;
    
  
);

/**
 * Define your own types here
 */
const campaignType = new GraphQLObjectType(
  name: 'Campaign',
  description: 'A campaign',
  fields: () => (
    id: globalIdField('Campaign'),
    account_id: 
      type: GraphQLString,
      description: 'ID of the ad account that owns this campaign',
    ,
    adlabels: 
      type: GraphQLString,
      description: 'Ad Labels associated with this campaign',
    ,
    buying_type: 
      type: GraphQLString,
      description: 'Buying type, possible values are: AUCTION: default, RESERVED: for reach and frequency ads',
    ,
    can_use_spend_cap: 
      type: GraphQLBoolean,
      description: 'Whether the campaign can set the spend cap',
    ,
    configured_status: 
      type: GraphQLString, // GraphQLEnumType,
      description: 'ACTIVE, PAUSED, DELETED, ARCHIVED. If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. Prefer using \'status\' instead of this.',
    ,
    created_time: 
      type: GraphQLID,  // this should be a datetime
      description: 'Created Time',
    ,
    effective_status: 
      type: GraphQLString, // GraphQLEnumType,
      description: 'The effective status of this campaign. ACTIVE, PAUSED, DELETED, PENDING_REVIEW, DISAPPROVED, PREAPPROVED, PENDING_BILLING_INFO, CAMPAIGN_PAUSED, ARCHIVED, ADSET_PAUSED',
    ,
    name: 
      type: GraphQLString,
      description: 'Campaign\'s name',
    ,
    objective: 
      type: GraphQLString,
      description: 'Campaign\'s objective',
    ,
    recommendations: 
      type: GraphQLString, // GraphQLList,
      description: 'If there are recommendations for this campaign, this field includes them. Otherwise, this field will be null.',
    ,
    spend_cap: 
      type: GraphQLFloat,
      description: 'A spend cap for the campaign, such that it will not spend more than this cap. Expressed as integer value of the subunit in your currency.',
    ,
    start_time: 
      type: GraphQLID,  // this should be a datetime
      description: 'Start Time',
    ,
    status: 
      type: GraphQLString,
      description: 'ACTIVE, PAUSED, DELETED, ARCHIVED If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. The field returns the same value as \'configured_status\', and is the suggested one to use.',
    ,
    stop_time: 
      type: GraphQLID,  // this should be a datetime
      description: 'Stop Time',
    ,
    updated_time: 
      type: GraphQLID,  // this should be a datetime
      description: 'Updated Time',
    ,
  ),
  interfaces: [nodeInterface],
);

const userType = new GraphQLObjectType(
  name: 'User',
  description: 'A person who uses our app',
  fields: () => (
    id: globalIdField('User'),

    // advertising campaigns
    campaigns: 
      type: campaignConnection,
      description: 'list of campaigns',
      args: connectionArgs,
      resolve: (viewer, args, source, info) => 
        return resolveCampaigns(viewer, args, source, info);
      ,
    ,

    features: 
      type: featureConnection,
      description: 'Features available to the logged in user',
      args: connectionArgs,
      resolve: (_, args) => connectionFromArray(getFeatures(), args)
    ,
    username: 
      type: GraphQLString,
      description: 'Users\'s username'
    ,
    website: 
      type: GraphQLString,
      description: 'User\'s website'
    
  ),
  interfaces: [nodeInterface]
);


const featureType = new GraphQLObjectType(
  name: 'Feature',
  description: 'Feature integrated in our starter kit',
  fields: () => (
    id: globalIdField('Feature'),
    name: 
      type: GraphQLString,
      description: 'Name of the feature'
    ,
    description: 
      type: GraphQLString,
      description: 'Description of the feature'
    ,
    url: 
      type: GraphQLString,
      description: 'Url of the feature'
    
  ),
  interfaces: [nodeInterface]
);

/**
 * Define your own connection types here
 */
const 
  connectionType: featureConnection
 = connectionDefinitions(
  name: 'Feature',
  nodeType: featureType
);

// Campaign list ConnectionType
const 
  connectionType: campaignConnection,
 = connectionDefinitions(
  name: 'Campaign',
  nodeType: campaignType
);


/**
 * This is the type that will be the root of our query,
 * and the entry point into our schema.
 */

// Setup GraphQL RootQuery
const RootQuery = new GraphQLObjectType(
  name: 'Query',
  description: 'Realize Root Query',
  fields: () => (
    viewer: 
      type: userType,
      resolve: () => '1'
    ,
  )
);

/**
 * This is the type that will be the root of our mutations,
 * and the entry point into performing writes in our schema.
 */
const mutationType = new GraphQLObjectType(
  name: 'Mutation',
  fields: () => (
    // Add your own mutations here
  )
);

/**
 * Finally, we construct our schema (whose starting query type is the query
 * type we defined above) and export it.
 */
export default new GraphQLSchema(
  query: RootQuery
  // Uncomment the following after adding some mutation fields:
  // mutation: mutationType
);

我遇到过其他人有类似的问题,虽然他们没有提到他们是如何解决的,但他们确实这么说:

问题出在我的 nodeDefinitions 中。我没有正确加载用户 ID 或识别节点对象。一旦我让那些工作一切正常

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

TL;DR;

您的根查询没有node 字段。这就是获取更多项目失败的原因。像这样添加node 字段:

const RootQuery = new GraphQLObjectType(
  ...
  fields: () => (
    viewer: 
      ...
    ,
    node: nodeField,
  )
);

当我检查请求有效负载时,我注意到它正在查询“节点”而不是像以前那样查询“查看器”。

Relay 第一次获取对象时,它使用常规查询。

viewer 
    id,
    ...F0

现在 Relay 知道 viewer 的全局 ID。稍后当需要获取更多viewer 的数据时,Relay 使用node 根字段直接查询该对象。

node(id:$id_0) 
    ...F0

请参阅 steveluscher 的 excellent answer,了解节点定义在 Relay 中的工作原理。

【讨论】:

以上是关于使用 this.props.relay.setVariables 时,Relay/Graphql 查询字段“node”而不是“viewer”的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)